mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-18 12:15:44 +01:00
BLADEBURNER: Add API to calculate max upgrade count of skill (#1475)
This commit is contained in:
parent
3d15413619
commit
289f60d8c8
20
markdown/bitburner.bladeburnerformulas.md
Normal file
20
markdown/bitburner.bladeburnerformulas.md
Normal file
@ -0,0 +1,20 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [BladeburnerFormulas](./bitburner.bladeburnerformulas.md)
|
||||
|
||||
## BladeburnerFormulas interface
|
||||
|
||||
Bladeburner formulas
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
interface BladeburnerFormulas
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [skillMaxUpgradeCount(name, level, skillPoints)](./bitburner.bladeburnerformulas.skillmaxupgradecount.md) | Calculate the number of times that you can upgrade a skill. |
|
||||
|
@ -0,0 +1,28 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [BladeburnerFormulas](./bitburner.bladeburnerformulas.md) > [skillMaxUpgradeCount](./bitburner.bladeburnerformulas.skillmaxupgradecount.md)
|
||||
|
||||
## BladeburnerFormulas.skillMaxUpgradeCount() method
|
||||
|
||||
Calculate the number of times that you can upgrade a skill.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
skillMaxUpgradeCount(name: string, level: number, skillPoints: number): number;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| name | string | Skill name. It's case-sensitive and must be an exact match. |
|
||||
| level | number | Skill level. It must be a non-negative number. |
|
||||
| skillPoints | number | Number of skill points to upgrade the skill. It must be a positive number. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
number
|
||||
|
||||
Number of times that you can upgrade the skill.
|
||||
|
13
markdown/bitburner.formulas.bladeburner.md
Normal file
13
markdown/bitburner.formulas.bladeburner.md
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Formulas](./bitburner.formulas.md) > [bladeburner](./bitburner.formulas.bladeburner.md)
|
||||
|
||||
## Formulas.bladeburner property
|
||||
|
||||
Bladeburner formulas
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
bladeburner: BladeburnerFormulas;
|
||||
```
|
@ -20,6 +20,7 @@ You need Formulas.exe on your home computer to use this API.
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [bladeburner](./bitburner.formulas.bladeburner.md) | | [BladeburnerFormulas](./bitburner.bladeburnerformulas.md) | Bladeburner formulas |
|
||||
| [gang](./bitburner.formulas.gang.md) | | [GangFormulas](./bitburner.gangformulas.md) | Gang formulas |
|
||||
| [hacking](./bitburner.formulas.hacking.md) | | [HackingFormulas](./bitburner.hackingformulas.md) | Hacking formulas |
|
||||
| [hacknetNodes](./bitburner.formulas.hacknetnodes.md) | | [HacknetNodesFormulas](./bitburner.hacknetnodesformulas.md) | Hacknet Nodes formulas |
|
||||
|
@ -36,6 +36,7 @@
|
||||
| [BitNodeRequirement](./bitburner.bitnoderequirement.md) | Player must be located in this BitNode. |
|
||||
| [Bladeburner](./bitburner.bladeburner.md) | Bladeburner API |
|
||||
| [BladeburnerCurAction](./bitburner.bladeburnercuraction.md) | Bladeburner current action. |
|
||||
| [BladeburnerFormulas](./bitburner.bladeburnerformulas.md) | Bladeburner formulas |
|
||||
| [BladeburnerRankRequirement](./bitburner.bladeburnerrankrequirement.md) | Player must have at least this rank in the Bladeburner Division. |
|
||||
| [CityRequirement](./bitburner.cityrequirement.md) | Player must be located in this city. |
|
||||
| [CodingContract](./bitburner.codingcontract.md) | Coding Contract API |
|
||||
|
@ -3,7 +3,7 @@ import type { BladeMultName, BladeSkillName } from "@enums";
|
||||
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
||||
import { Bladeburner } from "./Bladeburner";
|
||||
import { Availability } from "./Types";
|
||||
import { PositiveInteger, isPositiveInteger } from "../types";
|
||||
import { PositiveInteger, PositiveNumber, isPositiveInteger } from "../types";
|
||||
import { PartialRecord, getRecordEntries } from "../Types/Record";
|
||||
|
||||
interface SkillParams {
|
||||
@ -40,9 +40,6 @@ export class Skill {
|
||||
* The cost of the next level: (baseCost + currentLevel * costInc) * mult. The cost needs to be an integer, so we
|
||||
* need to use Math.floor or Math.round.
|
||||
*
|
||||
* Note: there is no notation for Math.round, so I use \lceil and \rceil as alternatives for non-existent \lround
|
||||
* and \rround. When you see \lceil and \rceil, it means Math.round, not Math.ceil.
|
||||
*
|
||||
* In order to calculate the cost of "count" levels, we need to run a loop. "count" can be a big number, so it's
|
||||
* infeasible to calculate the cost in that way. We need to find the closed forms of:
|
||||
*
|
||||
@ -52,7 +49,7 @@ export class Skill {
|
||||
* Or:
|
||||
*
|
||||
* [2]:
|
||||
* $$Cost = \sum_{i = CurrentLevel}^{CurrentLevel+Count-1}\lceil ((BaseCost + i \ast CostInc) \ast Mult) \rceil$$
|
||||
* $$Cost = \sum_{i = CurrentLevel}^{CurrentLevel+Count-1} \mathrm{Round}((BaseCost + i \ast CostInc) \ast Mult)$$
|
||||
*
|
||||
* It's really hard to find the closed forms of those two equations, so we switch to these equations:
|
||||
*
|
||||
@ -62,7 +59,7 @@ export class Skill {
|
||||
* Or
|
||||
*
|
||||
* [4]:
|
||||
* $$Cost = \lceil\sum_{i = CurrentLevel}^{CurrentLevel+Count-1} ((BaseCost + i \ast CostInc) \ast Mult) \rceil$$
|
||||
* $$Cost = \mathrm{Round}(\sum_{i = CurrentLevel}^{CurrentLevel+Count-1} ((BaseCost + i \ast CostInc) \ast Mult))$$
|
||||
*
|
||||
* This means that we do the flooring/rounding at the end instead of each iterative step.
|
||||
*
|
||||
@ -73,7 +70,7 @@ export class Skill {
|
||||
*
|
||||
* The closed form of [4]:
|
||||
*
|
||||
* $$Cost = \lceil Count \ast Mult \ast (BaseCost + (CostInc \ast (CurrentLevel + \frac{Count - 1}{2}))) \rceil$$
|
||||
* $$Cost = \mathrm{Round}(Count \ast Mult \ast (BaseCost + (CostInc \ast (CurrentLevel + \frac{Count - 1}{2}))))$$
|
||||
*
|
||||
*/
|
||||
return Math.round(
|
||||
@ -83,6 +80,61 @@ export class Skill {
|
||||
);
|
||||
}
|
||||
|
||||
calculateMaxUpgradeCount(currentLevel: number, cost: PositiveNumber): number {
|
||||
/**
|
||||
* Define:
|
||||
* - x = count
|
||||
* - a = currentNodeMults.BladeburnerSkillCost
|
||||
* - b = this.baseCost
|
||||
* - c = this.costInc
|
||||
* - d = currentLevel
|
||||
* - y = cost
|
||||
*
|
||||
* We have:
|
||||
*
|
||||
* $$ y = \mathrm{Round}(x \ast a \ast (b + c \ast (d + \frac{x - 1}{2})))$$
|
||||
*
|
||||
* To simplify the calculation, let's ignore the Math.round part:
|
||||
*
|
||||
* $$ y = x \ast a \ast (b + c \ast (d + \frac{x - 1}{2}))$$
|
||||
*
|
||||
* Solve for x in terms of y:
|
||||
*
|
||||
* Define:
|
||||
*
|
||||
* $$ m = -b - c \ast d + \frac{c}{2} $$
|
||||
*
|
||||
* $$ Delta = \sqrt{{m ^ 2} + \frac{2 \ast c \ast y}{a}} $$
|
||||
*
|
||||
* Solutions:
|
||||
*
|
||||
* $$ x_1 = \frac{m + Delta}{c} $$
|
||||
*
|
||||
* $$ x_2 = \frac{m - Delta}{c} $$
|
||||
*
|
||||
* $a$, $c$ and $y$ are always greater than 0, so $x_2$ is always less than 0. Therefore, $x_1$ is the only
|
||||
* solution.
|
||||
*/
|
||||
const m = -this.baseCost - this.costInc * currentLevel + this.costInc / 2;
|
||||
const delta = Math.sqrt(m * m + (2 * this.costInc * cost) / currentNodeMults.BladeburnerSkillCost);
|
||||
const result = Math.round((m + delta) / this.costInc);
|
||||
/**
|
||||
* Due to floating-point rounding and edge-cases, we cannot ensure that rounding x_1 will give us the correct
|
||||
* integer. In other words, we cannot be sure that x_1 is within 0.5 of the integer value we want. However, we can
|
||||
* be sure that it is within 1 of the value we want, which means that checking the numbers above and below the
|
||||
* rounded value are sufficient to find our correct integer.
|
||||
*/
|
||||
const costOfResultPlus1 = this.calculateCost(currentLevel, (result + 1) as PositiveInteger);
|
||||
if (costOfResultPlus1 <= cost) {
|
||||
return result + 1;
|
||||
}
|
||||
const costOfResult = this.calculateCost(currentLevel, result as PositiveInteger);
|
||||
if (costOfResult <= cost) {
|
||||
return result;
|
||||
}
|
||||
return result - 1;
|
||||
}
|
||||
|
||||
canUpgrade(bladeburner: Bladeburner, count = 1): Availability<{ cost: number }> {
|
||||
const currentLevel = bladeburner.skills[this.name] ?? 0;
|
||||
if (!isPositiveInteger(count)) {
|
||||
|
@ -3,12 +3,12 @@ import React, { useState } from "react";
|
||||
import { AugmentationName } from "@enums";
|
||||
import { Player } from "@player";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { random } from "../utils";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { getRandomArbitrary } from "../../utils/helpers/getRandomArbitrary";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -67,7 +67,7 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function makeAnswer(difficulty: Difficulty): string {
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
const length = getRandomArbitrary(difficulty.min, difficulty.max);
|
||||
let answer = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (i > 0) answer += " ";
|
||||
|
@ -3,12 +3,12 @@ import React, { useState } from "react";
|
||||
import { AugmentationName } from "@enums";
|
||||
import { Player } from "@player";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { random } from "../utils";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { getRandomArbitrary } from "../../utils/helpers/getRandomArbitrary";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -35,7 +35,7 @@ function generateLeftSide(difficulty: Difficulty): string {
|
||||
if (Player.hasAugmentation(AugmentationName.WisdomOfAthena, true)) {
|
||||
options.splice(0, 1);
|
||||
}
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
const length = getRandomArbitrary(difficulty.min, difficulty.max);
|
||||
for (let i = 0; i < length; i++) {
|
||||
str += options[Math.floor(Math.random() * options.length)];
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ import { Paper, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { AugmentationName } from "@enums";
|
||||
import { Player } from "@player";
|
||||
import { Arrow, downArrowSymbol, getArrow, leftArrowSymbol, random, rightArrowSymbol, upArrowSymbol } from "../utils";
|
||||
import { Arrow, downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { getRandomArbitrary } from "../../utils/helpers/getRandomArbitrary";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -77,7 +78,7 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
|
||||
function generateCode(difficulty: Difficulty): Arrow[] {
|
||||
const arrows: Arrow[] = [leftArrowSymbol, rightArrowSymbol, upArrowSymbol, downArrowSymbol];
|
||||
const code: Arrow[] = [];
|
||||
for (let i = 0; i < random(difficulty.min, difficulty.max); i++) {
|
||||
for (let i = 0; i < getRandomArbitrary(difficulty.min, difficulty.max); i++) {
|
||||
let arrow = arrows[Math.floor(4 * Math.random())];
|
||||
while (arrow === code[code.length - 1]) arrow = arrows[Math.floor(4 * Math.random())];
|
||||
code.push(arrow);
|
||||
|
@ -4,12 +4,12 @@ import { Box, Paper, Typography } from "@mui/material";
|
||||
import { AugmentationName } from "@enums";
|
||||
import { Player } from "@player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { isPositiveInteger } from "../../types";
|
||||
import { getRandomArbitrary } from "../../utils/helpers/getRandomArbitrary";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -200,7 +200,7 @@ function generateQuestion(wires: Wire[], difficulty: Difficulty): Question[] {
|
||||
|
||||
function generateWires(difficulty: Difficulty): Wire[] {
|
||||
const wires = [];
|
||||
const numWires = random(difficulty.wiresmin, difficulty.wiresmax);
|
||||
const numWires = getRandomArbitrary(difficulty.wiresmin, difficulty.wiresmax);
|
||||
for (let i = 0; i < numWires; i++) {
|
||||
const wireColors = [colors[Math.floor(Math.random() * colors.length)]];
|
||||
if (Math.random() < 0.15) {
|
||||
|
@ -2,10 +2,6 @@ import { KEY } from "../utils/helpers/keyCodes";
|
||||
import { Player } from "@player";
|
||||
import { AugmentationName } from "@enums";
|
||||
|
||||
export function random(min: number, max: number): number {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
export const upArrowSymbol = "↑";
|
||||
export const downArrowSymbol = "↓";
|
||||
export const leftArrowSymbol = "←";
|
||||
|
@ -67,6 +67,7 @@ export const helpers = {
|
||||
number,
|
||||
positiveInteger,
|
||||
positiveSafeInteger,
|
||||
positiveNumber,
|
||||
scriptArgs,
|
||||
runOptions,
|
||||
spawnOptions,
|
||||
|
@ -669,6 +669,9 @@ export const RamCosts: RamCostTree<NSFull> = {
|
||||
factionGains: 0,
|
||||
companyGains: 0,
|
||||
},
|
||||
bladeburner: {
|
||||
skillMaxUpgradeCount: 0,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
@ -51,6 +51,7 @@ import { findEnumMember } from "../utils/helpers/enum";
|
||||
import { getEnumHelper } from "../utils/EnumHelper";
|
||||
import { CompanyPositions } from "../Company/CompanyPositions";
|
||||
import { findCrime } from "../Crime/CrimeHelpers";
|
||||
import { Skills } from "../Bladeburner/data/Skills";
|
||||
|
||||
export function NetscriptFormulas(): InternalAPI<IFormulas> {
|
||||
const checkFormulasAccess = function (ctx: NetscriptContext): void {
|
||||
@ -427,6 +428,22 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
|
||||
return calculateCompanyWorkStats(person, company, position, favor);
|
||||
},
|
||||
},
|
||||
bladeburner: {
|
||||
skillMaxUpgradeCount: (ctx) => (_name, _level, _skillPoints) => {
|
||||
checkFormulasAccess(ctx);
|
||||
const name = getEnumHelper("BladeSkillName").nsGetMember(ctx, _name, "name");
|
||||
const level = helpers.number(ctx, "level", _level);
|
||||
if (level < 0) {
|
||||
throw new Error(`Level must be a non-negative number.`);
|
||||
}
|
||||
const skillPoints = helpers.positiveNumber(ctx, "skillPoints", _skillPoints);
|
||||
const skill = Skills[name];
|
||||
if (level >= skill.maxLvl) {
|
||||
return 0;
|
||||
}
|
||||
return skill.calculateMaxUpgradeCount(level, skillPoints);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Removed functions
|
||||
|
18
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
18
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -5110,6 +5110,22 @@ interface GangFormulas {
|
||||
ascensionMultiplier(points: number): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bladeburner formulas
|
||||
* @public
|
||||
*/
|
||||
interface BladeburnerFormulas {
|
||||
/**
|
||||
* Calculate the number of times that you can upgrade a skill.
|
||||
*
|
||||
* @param name - Skill name. It's case-sensitive and must be an exact match.
|
||||
* @param level - Skill level. It must be a non-negative number.
|
||||
* @param skillPoints - Number of skill points to upgrade the skill. It must be a positive number.
|
||||
* @returns Number of times that you can upgrade the skill.
|
||||
*/
|
||||
skillMaxUpgradeCount(name: string, level: number, skillPoints: number): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formulas API
|
||||
* @remarks
|
||||
@ -5134,6 +5150,8 @@ export interface Formulas {
|
||||
gang: GangFormulas;
|
||||
/** Work formulas */
|
||||
work: WorkFormulas;
|
||||
/** Bladeburner formulas */
|
||||
bladeburner: BladeburnerFormulas;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
7
src/utils/helpers/getRandomArbitrary.ts
Normal file
7
src/utils/helpers/getRandomArbitrary.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#getting_a_random_number_between_two_values
|
||||
export function getRandomArbitrary(min: number, max: number): number {
|
||||
if (min > max) {
|
||||
throw new Error(`Min is greater than max. Min: ${min}. Max: ${max}.`);
|
||||
}
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
97
test/jest/Netscript/Bladeburner.test.ts
Normal file
97
test/jest/Netscript/Bladeburner.test.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { currentNodeMults } from "../../../src/BitNode/BitNodeMultipliers";
|
||||
import { Skill } from "../../../src/Bladeburner/Skill";
|
||||
import { BladeSkillName } from "../../../src/Enums";
|
||||
import { PositiveInteger, isPositiveInteger, isPositiveNumber } from "../../../src/types";
|
||||
import { getRandomArbitrary } from "../../../src/utils/helpers/getRandomArbitrary";
|
||||
import { getRandomIntInclusive } from "../../../src/utils/helpers/getRandomIntInclusive";
|
||||
|
||||
const skill = new Skill({
|
||||
name: BladeSkillName.hyperdrive,
|
||||
desc: "",
|
||||
baseCost: 1,
|
||||
costInc: 1,
|
||||
mults: {},
|
||||
});
|
||||
|
||||
describe("Test calculateMaxUpgradeCount", function () {
|
||||
test("errorCount", () => {
|
||||
let testCaseCount = 0;
|
||||
let errorCount = 0;
|
||||
const test1Errors = [];
|
||||
const test2Errors = [];
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
skill.baseCost = getRandomIntInclusive(1, 1000);
|
||||
for (let j = 0; j < 10; ++j) {
|
||||
skill.costInc = getRandomArbitrary(1, 1000);
|
||||
for (let k = 0; k < 10; ++k) {
|
||||
currentNodeMults.BladeburnerSkillCost = getRandomArbitrary(1, 1000);
|
||||
for (let m = 0; m < 1e4; ++m) {
|
||||
const currentLevel = getRandomIntInclusive(0, 1e9);
|
||||
let count = 0;
|
||||
let cost = 0;
|
||||
|
||||
// Test 1
|
||||
++testCaseCount;
|
||||
const expectedCount = getRandomIntInclusive(1, 1e9);
|
||||
if (!isPositiveInteger(expectedCount)) {
|
||||
throw new Error(`Invalid expectedCount: ${expectedCount}`);
|
||||
}
|
||||
cost = skill.calculateCost(currentLevel, expectedCount);
|
||||
if (!isPositiveNumber(cost)) {
|
||||
throw new Error(`Invalid cost: ${cost}`);
|
||||
}
|
||||
count = skill.calculateMaxUpgradeCount(currentLevel, cost);
|
||||
if (expectedCount !== count) {
|
||||
++errorCount;
|
||||
test1Errors.push({
|
||||
baseCost: skill.baseCost,
|
||||
costInc: skill.costInc,
|
||||
mult: currentNodeMults.BladeburnerSkillCost,
|
||||
currentLevel,
|
||||
cost,
|
||||
count,
|
||||
expectedCount,
|
||||
});
|
||||
}
|
||||
|
||||
// Test 2
|
||||
++testCaseCount;
|
||||
const budget = getRandomArbitrary(1e9, 1e50);
|
||||
if (!isPositiveNumber(budget)) {
|
||||
throw new Error(`Invalid budget: ${budget}`);
|
||||
}
|
||||
count = skill.calculateMaxUpgradeCount(currentLevel, budget);
|
||||
if (!isPositiveInteger(count)) {
|
||||
throw new Error(`Invalid count: ${count}`);
|
||||
}
|
||||
cost = skill.calculateCost(currentLevel, count);
|
||||
const costOfCountPlus1 = skill.calculateCost(currentLevel, (count + 1) as PositiveInteger);
|
||||
if (count !== count + 1 && (budget < cost || budget >= costOfCountPlus1)) {
|
||||
++errorCount;
|
||||
test2Errors.push({
|
||||
baseCost: skill.baseCost,
|
||||
costInc: skill.costInc,
|
||||
mult: currentNodeMults.BladeburnerSkillCost,
|
||||
currentLevel,
|
||||
count,
|
||||
budget,
|
||||
cost,
|
||||
costOfCountPlus1,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errorCount !== 0) {
|
||||
// There may be hundreds of thousands or even millions of failed test cases, so we only show a limited number of them.
|
||||
console.error(
|
||||
`testCaseCount: ${testCaseCount}. errorCount: ${errorCount}. test1Errors: ${JSON.stringify(
|
||||
test1Errors.slice(0, 1000),
|
||||
)}. test2Errors: ${JSON.stringify(test2Errors.slice(0, 1000))}`,
|
||||
);
|
||||
}
|
||||
|
||||
expect(errorCount).toBe(0);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user