From 29c54df5433ae1008b7ad5f9754e4b0400783074 Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Sat, 6 Jul 2024 04:27:28 +0700 Subject: [PATCH] BLADEBURNER: Change formula of skill cost (#1443) --- src/Bladeburner/Skill.ts | 69 +++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/Bladeburner/Skill.ts b/src/Bladeburner/Skill.ts index d76ef234e..2e36e931f 100644 --- a/src/Bladeburner/Skill.ts +++ b/src/Bladeburner/Skill.ts @@ -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, PositiveSafeInteger, isPositiveInteger } from "../types"; +import { PositiveInteger, isPositiveInteger } from "../types"; import { PartialRecord, getRecordEntries } from "../Types/Record"; interface SkillParams { @@ -35,30 +35,49 @@ export class Skill { } calculateCost(currentLevel: number, count = 1 as PositiveInteger): number { - if (currentLevel + count > this.maxLvl) return Infinity; - - const recursiveMode = (currentLevel: number, count: PositiveSafeInteger): number => { - if (count <= 1) { - return Math.floor((this.baseCost + currentLevel * this.costInc) * currentNodeMults.BladeburnerSkillCost); - } else { - const thisUpgrade = Math.floor( - (this.baseCost + currentLevel * this.costInc) * currentNodeMults.BladeburnerSkillCost, - ); - return this.calculateCost(currentLevel + 1, (count - 1) as PositiveSafeInteger) + thisUpgrade; - } - }; - - // Use recursive mode if count is small - if (count <= 100) return recursiveMode(currentLevel, count as PositiveSafeInteger); - // Use optimized mode if count is large - else { - //unFloored is roughly equivalent to - //(this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost - //being repeated for increasing currentLevel - const preMult = (count * (2 * this.baseCost + this.costInc * (2 * currentLevel + count + 1))) / 2; - const unFloored = preMult * currentNodeMults.BladeburnerSkillCost - count / 2; - return Math.floor(unFloored); - } + /** + * 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: + * + * [1]: + * $$Cost = \sum_{i = CurrentLevel}^{CurrentLevel+Count-1}\lfloor ((BaseCost + i \ast CostInc) \ast Mult) \rfloor$$ + * + * Or: + * + * [2]: + * $$Cost = \sum_{i = CurrentLevel}^{CurrentLevel+Count-1}\lceil ((BaseCost + i \ast CostInc) \ast Mult) \rceil$$ + * + * It's really hard to find the closed forms of those two equations, so we switch to these equations: + * + * [3]: + * $$Cost = \lfloor\sum_{i = CurrentLevel}^{CurrentLevel+Count-1} ((BaseCost + i \ast CostInc) \ast Mult) \rfloor$$ + * + * Or + * + * [4]: + * $$Cost = \lceil\sum_{i = CurrentLevel}^{CurrentLevel+Count-1} ((BaseCost + i \ast CostInc) \ast Mult) \rceil$$ + * + * This means that we do the flooring/rounding at the end instead of each iterative step. + * + * [3] and [4] are not equivalent to [1] and [2] respectively, but it's much easier to find the close forms of [3] + * and [4] than [1] and [2]. After testing, we conclude that the cost calculated by [4] is a good approximation of + * [2], so we choose [4] to calculate the cost. In order to calculate the cost with a big "count", we accept the + * slight inaccuracy. + * + * The closed form of [4]: + * + * $$Cost = \lceil Count \ast Mult \ast (BaseCost + (CostInc \ast (CurrentLevel + \frac{Count - 1}{2}))) \rceil$$ + * + */ + return Math.round( + count * currentNodeMults.BladeburnerSkillCost * (this.baseCost + this.costInc * (currentLevel + (count - 1) / 2)), + ); } canUpgrade(bladeburner: Bladeburner, count = 1): Availability<{ cost: number }> {