mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
Compare commits
3 Commits
7425d8a8fd
...
d1b6acc57a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1b6acc57a | ||
|
|
dc4ea8452c | ||
|
|
5fc54809de |
@@ -81,6 +81,9 @@ export class Skill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calculateMaxUpgradeCount(currentLevel: number, cost: PositiveNumber): number {
|
calculateMaxUpgradeCount(currentLevel: number, cost: PositiveNumber): number {
|
||||||
|
// At extreme levels, floating-point precision loss makes currentLevel + 1 === currentLevel,
|
||||||
|
// causing calculateCost to return 0. No upgrade is possible in this case.
|
||||||
|
if (this.calculateCost(currentLevel, 1 as PositiveInteger) <= 0) return 0;
|
||||||
/**
|
/**
|
||||||
* Define:
|
* Define:
|
||||||
* - x = count
|
* - x = count
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ import { getRandomAlphanumericString } from "../utils/StringHelperFunctions";
|
|||||||
|
|
||||||
export function tryGeneratingRandomContract(numberOfTries: number): void {
|
export function tryGeneratingRandomContract(numberOfTries: number): void {
|
||||||
/**
|
/**
|
||||||
* We try to generate a contract every 10 minutes. 525600 is the number of tries in 10 years. There is no reason to
|
* We try to generate contracts three times every 10 minutes. 1576800 is the number of tries in 10 years. There is no
|
||||||
* support anything above that. We tested this number (525600) on a very old machine. It took only 300-350ms to
|
* reason to support anything above that. We tested this number (1576800) on a very old machine. It took only ~300ms
|
||||||
* loop 525600 times and generate ~9137 contracts on that machine.
|
* to loop 1576800 times and generate ~10103 contracts on that machine.
|
||||||
*/
|
*/
|
||||||
numberOfTries = clampNumber(Math.floor(numberOfTries), 0, 525600);
|
numberOfTries = clampNumber(Math.floor(numberOfTries), 0, 1576800);
|
||||||
if (numberOfTries < 1) {
|
if (numberOfTries < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -47,19 +47,19 @@ export function tryGeneratingRandomContract(numberOfTries: number): void {
|
|||||||
* - If the offline time is unusually large (being offline for years, editing save file, tampering function prototype,
|
* - If the offline time is unusually large (being offline for years, editing save file, tampering function prototype,
|
||||||
* etc.), the game will not hang when it tries to generate contracts.
|
* etc.), the game will not hang when it tries to generate contracts.
|
||||||
*
|
*
|
||||||
* These are some data for reference:
|
* These are some data points for reference:
|
||||||
* - 1 month: ~1077 contracts.
|
* - 1 month: ~3157 contracts.
|
||||||
* - 3 months: ~3157 contracts.
|
* - 3 months: ~6198 contracts.
|
||||||
* - 6 months: ~5296 contracts.
|
* - 6 months: ~7231 contracts.
|
||||||
* - 12 months: ~6678 contracts.
|
* - 12 months: ~8003 contracts.
|
||||||
* - 2 years: ~7570 contracts.
|
* - 2 years: ~8687 contracts.
|
||||||
* - 5 years: ~8504 contracts.
|
* - 5 years: ~9506 contracts.
|
||||||
* - 10 years: ~9137 contracts.
|
* - 10 years: ~10103 contracts.
|
||||||
* - 25 years: ~9936 contracts.
|
* - 25 years: ~10879 contracts.
|
||||||
* - 50 years: ~10526 contracts.
|
* - 50 years: ~11461 contracts.
|
||||||
*
|
*
|
||||||
* Those numbers mean: If the player does not have any contracts and is online (or loads a save file with equivalent
|
* Those numbers mean that if the player does not have any contracts and is online (or loads a save file with
|
||||||
* offline time) for X months/years, they will have ~Y contracts.
|
* equivalent offline time) for X months/years, they will have ~Y contracts.
|
||||||
*/
|
*/
|
||||||
if (random > 100 / (399 + Math.exp(0.0012 * currentNumberOfContracts))) {
|
if (random > 100 / (399 + Math.exp(0.0012 * currentNumberOfContracts))) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -89,12 +89,7 @@ export const disconnectServers = (server1: BaseServer, server2: BaseServer) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function ipExists(ip: string): boolean {
|
export function ipExists(ip: string): boolean {
|
||||||
for (const server of AllServers.values()) {
|
return AllServers.has(ip);
|
||||||
if (server.ip === ip) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createUniqueRandomIp(): IPAddress {
|
export function createUniqueRandomIp(): IPAddress {
|
||||||
|
|||||||
36
test/jest/Bladeburner/Skill.test.ts
Normal file
36
test/jest/Bladeburner/Skill.test.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { BladeburnerMultName, BladeburnerSkillName } from "@enums";
|
||||||
|
import { Skill } from "../../../src/Bladeburner/Skill";
|
||||||
|
import { PositiveNumber } from "../../../src/types";
|
||||||
|
|
||||||
|
const hyperdrive = new Skill({
|
||||||
|
name: BladeburnerSkillName.Hyperdrive,
|
||||||
|
desc: "test",
|
||||||
|
baseCost: 1,
|
||||||
|
costInc: 2.5,
|
||||||
|
mults: { [BladeburnerMultName.ExpGain]: 10 },
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Bladeburner Skill", () => {
|
||||||
|
describe("calculateMaxUpgradeCount", () => {
|
||||||
|
it("should return 0 when currentLevel is too high for floating-point precision", () => {
|
||||||
|
// At level 1e50, adding 1 is below float64 precision: 1e50 + 1 === 1e50
|
||||||
|
// So calculateCost returns 0 for any count, and no upgrade is possible
|
||||||
|
const result = hyperdrive.calculateMaxUpgradeCount(1e50, 1 as PositiveNumber);
|
||||||
|
expect(result).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return correct count at normal levels", () => {
|
||||||
|
// At level 0 with cost 1, baseCost 1, costInc 2.5:
|
||||||
|
// cost of 1 level = round(1 * 1 * (1 + 2.5 * (0 + 0/2))) = round(1) = 1
|
||||||
|
const result = hyperdrive.calculateMaxUpgradeCount(0, 1 as PositiveNumber);
|
||||||
|
expect(result).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return 0 when cost is less than the price of one level", () => {
|
||||||
|
// At level 10: cost of 1 level = round(1 * 1 * (1 + 2.5 * 10)) = 26
|
||||||
|
const costOfOne = hyperdrive.calculateCost(10);
|
||||||
|
const result = hyperdrive.calculateMaxUpgradeCount(10, (costOfOne - 1) as PositiveNumber);
|
||||||
|
expect(result).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
saveAllServers,
|
saveAllServers,
|
||||||
renameServer,
|
renameServer,
|
||||||
GetServer,
|
GetServer,
|
||||||
|
ipExists,
|
||||||
} from "../../../src/Server/AllServers";
|
} from "../../../src/Server/AllServers";
|
||||||
import { Server } from "../../../src/Server/Server";
|
import { Server } from "../../../src/Server/Server";
|
||||||
import { IPAddress } from "../../../src/Types/strings";
|
import { IPAddress } from "../../../src/Types/strings";
|
||||||
@@ -38,6 +39,40 @@ describe("AllServers can be saved and loaded", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("ipExists", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
prestigeAllServers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns true for an IP that exists in AllServers", () => {
|
||||||
|
const server = new Server({ hostname: "test-server", ip: "10.20.30.40" as IPAddress });
|
||||||
|
AddToAllServers(server);
|
||||||
|
expect(ipExists("10.20.30.40")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false for an IP that does not exist", () => {
|
||||||
|
expect(ipExists("99.99.99.99")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not use linear scan (performance: uses Map.has, not iteration)", () => {
|
||||||
|
// Add a server so the map is non-empty
|
||||||
|
const server = new Server({ hostname: "perf-test", ip: "1.1.1.1" as IPAddress });
|
||||||
|
AddToAllServers(server);
|
||||||
|
|
||||||
|
// Spy on Map.prototype.values to detect if ipExists iterates
|
||||||
|
const valuesSpy = jest.spyOn(Map.prototype, "values");
|
||||||
|
|
||||||
|
ipExists("1.1.1.1");
|
||||||
|
ipExists("2.2.2.2");
|
||||||
|
|
||||||
|
// With the fix (Map.has), values() should NOT be called by ipExists
|
||||||
|
// With the bug (for...of iteration), values() WOULD be called
|
||||||
|
expect(valuesSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
valuesSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("renameServer tests", () => {
|
describe("renameServer tests", () => {
|
||||||
it("rename to self edge case", () => {
|
it("rename to self edge case", () => {
|
||||||
prestigeAllServers();
|
prestigeAllServers();
|
||||||
|
|||||||
Reference in New Issue
Block a user