From f12de39c9128ab58df180adf93236bd3da4db5ce Mon Sep 17 00:00:00 2001 From: Vomitblood Date: Sat, 2 May 2026 01:00:05 +0800 Subject: [PATCH] namespaced formulas for easier importing Co-authored-by: Copilot --- README.md | 4 + src/ezgame.ts | 5 ++ src/ezgame/formulas.ts | 15 ++++ src/{utils => ezgame}/formulas/README | 2 +- src/{utils => ezgame}/formulas/constants.ts | 2 + src/{utils => ezgame}/formulas/exports.ts | 5 +- src/{utils => ezgame}/formulas/gang.ts | 31 +++++--- src/{utils => ezgame}/formulas/hacking.ts | 71 +++++++++++++---- .../formulas/hacknet-nodes.ts | 18 +++-- .../formulas/hacknet-servers.ts | 27 ++++--- src/{utils => ezgame}/formulas/player.ts | 0 src/{utils => ezgame}/formulas/reputation.ts | 26 +++++-- src/{utils => ezgame}/formulas/skills.ts | 17 ++-- src/test.ts | 12 +-- src/utils/utils.ts | 78 ++++++++++++++----- 15 files changed, 225 insertions(+), 88 deletions(-) create mode 100644 src/ezgame.ts create mode 100644 src/ezgame/formulas.ts rename src/{utils => ezgame}/formulas/README (97%) rename src/{utils => ezgame}/formulas/constants.ts (97%) rename src/{utils => ezgame}/formulas/exports.ts (98%) rename src/{utils => ezgame}/formulas/gang.ts (86%) rename src/{utils => ezgame}/formulas/hacking.ts (82%) rename src/{utils => ezgame}/formulas/hacknet-nodes.ts (78%) rename src/{utils => ezgame}/formulas/hacknet-servers.ts (79%) rename src/{utils => ezgame}/formulas/player.ts (100%) rename src/{utils => ezgame}/formulas/reputation.ts (62%) rename src/{utils => ezgame}/formulas/skills.ts (86%) diff --git a/README.md b/README.md index 95b75fa..424b7a3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ NOTE: All user scripts to be synced with the game is found under `src/`. +## TODO + +- Fix the fucking ezgame.formulas.hacking.growTime(). calculateIntelligenceBonus() is throwing errors when calculating intelligence. Clearly my intelligence value is 0. + This is a template for a viteburner project. It is a simple example of how to use Viteburner. ## How to use diff --git a/src/ezgame.ts b/src/ezgame.ts new file mode 100644 index 0000000..973b30d --- /dev/null +++ b/src/ezgame.ts @@ -0,0 +1,5 @@ +import { formulas } from "./ezgame/formulas"; + +export const ezgame = { + formulas, +}; diff --git a/src/ezgame/formulas.ts b/src/ezgame/formulas.ts new file mode 100644 index 0000000..a8020f2 --- /dev/null +++ b/src/ezgame/formulas.ts @@ -0,0 +1,15 @@ +import { gang } from "./formulas/gang"; +import { hacking } from "./formulas/hacking"; +import { hacknetNodes } from "./formulas/hacknet-nodes"; +import { hacknetServers } from "./formulas/hacknet-servers"; +import { reputation } from "./formulas/reputation"; +import { skills } from "./formulas/skills"; + +export const formulas = { + gang, + hacking, + hacknetNodes, + hacknetServers, + reputation, + skills, +}; diff --git a/src/utils/formulas/README b/src/ezgame/formulas/README similarity index 97% rename from src/utils/formulas/README rename to src/ezgame/formulas/README index b238573..4041e82 100644 --- a/src/utils/formulas/README +++ b/src/ezgame/formulas/README @@ -6,7 +6,7 @@ Reverse engineered from the [bitburner source](https://github.com/bitburner-offi ### Gang -- [ ] `ascencionMultipler(points)` +- [ ] `ascensionMultiplier(points)` - [ ] `ascensionPointsGain(exp)` - [ ] `moneyGain(gang, member, task)` - [ ] `respectGain(gang, member, task)` diff --git a/src/utils/formulas/constants.ts b/src/ezgame/formulas/constants.ts similarity index 97% rename from src/utils/formulas/constants.ts rename to src/ezgame/formulas/constants.ts index a5e21e5..3ad4d8a 100644 --- a/src/utils/formulas/constants.ts +++ b/src/ezgame/formulas/constants.ts @@ -1,4 +1,5 @@ import { Multipliers } from "@ns"; +import { PartialRecord } from "./exports"; /** * Generic Game Constants @@ -210,3 +211,4 @@ export const MaxFavor = 35331; // This is the nearest representable value of log(1.02), which is the base of our power. // It is *not* the same as Math.log(1.02), since "1.02" lacks sufficient precision. export const log1point02 = 0.019802627296179712; +export const getRecordEntries = Object.entries as (record: PartialRecord) => [K, V][]; diff --git a/src/utils/formulas/exports.ts b/src/ezgame/formulas/exports.ts similarity index 98% rename from src/utils/formulas/exports.ts rename to src/ezgame/formulas/exports.ts index 5354649..e9d3f45 100644 --- a/src/utils/formulas/exports.ts +++ b/src/ezgame/formulas/exports.ts @@ -1,8 +1,7 @@ -import { clampNumber } from "./utils"; +import { clampNumber } from "@/utils/utils"; +import { getRecordEntries } from "./constants"; export type PartialRecord = Partial>; -export const getRecordEntries = Object.entries as (record: PartialRecord) => [K, V][]; - /** * Bitnode multipliers influence the difficulty of different aspects of the game. * Each Bitnode has a different theme/strategy to achieving the end goal, so these multipliers will can help drive the diff --git a/src/utils/formulas/gang.ts b/src/ezgame/formulas/gang.ts similarity index 86% rename from src/utils/formulas/gang.ts rename to src/ezgame/formulas/gang.ts index d6a087e..dc09542 100644 --- a/src/utils/formulas/gang.ts +++ b/src/ezgame/formulas/gang.ts @@ -1,18 +1,27 @@ import { currentNodeMults } from "./exports"; -export interface FormulaGang { +export const gang = { + ascensionMultiplier: calculateAscensionMult, + ascensionPointsGain: calculateAscensionPointsGain, + moneyGain: calculateMoneyGain, + respectGain: calculateRespectGain, + wantedLevelGain: calculateWantedLevelGain, + wantedPenalty: calculateWantedPenalty, +}; + +interface FormulaGang { respect: number; territory: number; wantedLevel: number; } -export interface ITerritory { +interface ITerritory { money: number; respect: number; wanted: number; } -export interface ITaskParams { +interface ITaskParams { baseRespect?: number; baseWanted?: number; baseMoney?: number; @@ -26,7 +35,7 @@ export interface ITaskParams { territory?: ITerritory; } -export class GangMember { +class GangMember { name: string; task = "Unassigned"; @@ -68,7 +77,7 @@ export class GangMember { } } -export class GangMemberTask { +class GangMemberTask { name: string; desc: string; @@ -132,11 +141,11 @@ export class GangMemberTask { } } -export function calculateWantedPenalty(gang: FormulaGang): number { +function calculateWantedPenalty(gang: FormulaGang): number { return gang.respect / (gang.respect + gang.wantedLevel); } -export function calculateRespectGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number { +function calculateRespectGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number { if (task.baseRespect === 0) return 0; let statWeight = (task.hackWeight / 100) * member.hack + @@ -154,7 +163,7 @@ export function calculateRespectGain(gang: FormulaGang, member: GangMember, task return Math.pow(11 * task.baseRespect * statWeight * territoryMult * respectMult, territoryPenalty); } -export function calculateWantedLevelGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number { +function calculateWantedLevelGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number { if (task.baseWanted === 0) return 0; let statWeight = (task.hackWeight / 100) * member.hack + @@ -177,7 +186,7 @@ export function calculateWantedLevelGain(gang: FormulaGang, member: GangMember, return Math.min(100, calc); } -export function calculateMoneyGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number { +function calculateMoneyGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number { if (task.baseMoney === 0) return 0; let statWeight = (task.hackWeight / 100) * member.hack + @@ -196,10 +205,10 @@ export function calculateMoneyGain(gang: FormulaGang, member: GangMember, task: return Math.pow(5 * task.baseMoney * statWeight * territoryMult * respectMult, territoryPenalty); } -export function calculateAscensionPointsGain(exp: number): number { +function calculateAscensionPointsGain(exp: number): number { return Math.max(exp - 1000, 0); } -export function calculateAscensionMult(points: number): number { +function calculateAscensionMult(points: number): number { return Math.max(Math.pow(points / 2000, 0.5), 1); } diff --git a/src/utils/formulas/hacking.ts b/src/ezgame/formulas/hacking.ts similarity index 82% rename from src/utils/formulas/hacking.ts rename to src/ezgame/formulas/hacking.ts index d725705..e74cce2 100644 --- a/src/utils/formulas/hacking.ts +++ b/src/ezgame/formulas/hacking.ts @@ -1,10 +1,26 @@ +import { clampNumber, isValidNumber } from "@/utils/utils"; import { Player as IPerson, Server as IServer } from "@ns"; import { ServerConstants } from "./constants"; import { currentNodeMults } from "./exports"; import { Player } from "./player"; -import { clampNumber, isValidNumber } from "./utils"; -export function calculateIntelligenceBonus(intelligence: number, weight = 1): number { +export const hacking = { + growAmount: calculateGrowMoney, + growPercent: calculateServerGrowth, + growThreads, + growTime: calculateGrowTime, + hackChance: calculateHackingChance, + hackExp: calculateHackingExpGain, + hackPercent: calculatePercentMoneyHacked, + hackTime: calculateHackingTime, + weakenTime: calculateWeakenTime, + numCycleForGrowthCorrected, + calculateServerGrowthLog, + numCycleForGrowth, + getWeakenEffect, +}; + +function calculateIntelligenceBonus(intelligence: number, weight = 1): number { const effectiveIntelligence = Player.bitNodeOptions.intelligenceOverride !== undefined ? Math.min(Player.bitNodeOptions.intelligenceOverride, intelligence) @@ -13,7 +29,7 @@ export function calculateIntelligenceBonus(intelligence: number, weight = 1): nu } /** Returns the chance the person has to successfully hack a server */ -export function calculateHackingChance(server: IServer, person: IPerson): number { +function calculateHackingChance(server: IServer, person: IPerson): number { const hackDifficulty = server.hackDifficulty ?? 100; const requiredHackingSkill = server.requiredHackingSkill ?? 1e9; // Unrooted or unhackable server @@ -34,7 +50,7 @@ export function calculateHackingChance(server: IServer, person: IPerson): number * Returns the amount of hacking experience the person will gain upon * successfully hacking a server */ -export function calculateHackingExpGain(server: IServer, person: IPerson): number { +function calculateHackingExpGain(server: IServer, person: IPerson): number { const baseDifficulty = server.baseDifficulty; if (!baseDifficulty) return 0; const baseExpGain = 3; @@ -48,7 +64,7 @@ export function calculateHackingExpGain(server: IServer, person: IPerson): numbe * Returns the percentage of money that will be stolen from a server if * it is successfully hacked (returns the decimal form, not the actual percent value) */ -export function calculatePercentMoneyHacked(server: IServer, person: IPerson): number { +function calculatePercentMoneyHacked(server: IServer, person: IPerson): number { const hackDifficulty = server.hackDifficulty ?? 100; if (hackDifficulty >= 100) return 0; const requiredHackingSkill = server.requiredHackingSkill ?? 1e9; @@ -64,7 +80,7 @@ export function calculatePercentMoneyHacked(server: IServer, person: IPerson): n } /** Returns time it takes to complete a hack on a server, in seconds */ -export function calculateHackingTime(server: IServer, person: IPerson): number { +function calculateHackingTime(server: IServer, person: IPerson): number { const { hackDifficulty, requiredHackingSkill } = server; if (typeof hackDifficulty !== "number" || typeof requiredHackingSkill !== "number") return Infinity; const difficultyMult = requiredHackingSkill * hackDifficulty; @@ -86,21 +102,21 @@ export function calculateHackingTime(server: IServer, person: IPerson): number { } /** Returns time it takes to complete a grow operation on a server, in seconds */ -export function calculateGrowTime(server: IServer, person: IPerson): number { +function calculateGrowTime(server: IServer, person: IPerson): number { const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2 return growTimeMultiplier * calculateHackingTime(server, person); } /** Returns time it takes to complete a weaken operation on a server, in seconds */ -export function calculateWeakenTime(server: IServer, person: IPerson): number { +function calculateWeakenTime(server: IServer, person: IPerson): number { const weakenTimeMultiplier = 4; // Relative to hacking time return weakenTimeMultiplier * calculateHackingTime(server, person); } // Returns the log of the growth rate. When passing 1 for threads, this gives a useful constant. -export function calculateServerGrowthLog(server: IServer, threads: number, p: IPerson, cores = 1): number { +function calculateServerGrowthLog(server: IServer, threads: number, p: IPerson, cores = 1): number { if (!server.serverGrowth) return -Infinity; const hackDifficulty = server.hackDifficulty ?? 100; const numServerGrowthCycles = Math.max(threads, 0); @@ -123,14 +139,14 @@ export function calculateServerGrowthLog(server: IServer, threads: number, p: IP return adjGrowthLog * serverGrowthPercentageAdjusted * p.mults.hacking_grow * coreBonus * numServerGrowthCycles; } -export function calculateServerGrowth(server: IServer, threads: number, p: IPerson, cores = 1): number { +function calculateServerGrowth(server: IServer, threads: number, p: IPerson, cores = 1): number { if (!server.serverGrowth) return 0; return Math.exp(calculateServerGrowthLog(server, threads, p, cores)); } // This differs from calculateServerGrowth in that it includes the additive // factor and all the boundary checks. -export function calculateGrowMoney(server: IServer, threads: number, p: IPerson, cores = 1): number { +function calculateGrowMoney(server: IServer, p: IPerson, threads: number, cores = 1): number { let serverGrowth = calculateServerGrowth(server, threads, p, cores); if (serverGrowth < 1) { console.warn("serverGrowth calculated to be less than 1"); @@ -160,27 +176,48 @@ export function calculateGrowMoney(server: IServer, threads: number, p: IPerson, * @param p - Reference to Player object * @returns Number of "growth cycles" needed */ -export function numCycleForGrowth(server: IServer, growth: number, cores = 1): number { +function numCycleForGrowth(server: IServer, growth: number, cores = 1): number { if (!server.serverGrowth) return Infinity; return Math.log(growth) / calculateServerGrowthLog(server, 1, Player, cores); } +/** + * Wrapper of the function `numCycleForGrowthCorrected`. + * + * Calculate how many threads it will take to grow server to targetMoney. Starting money is server.moneyAvailable. + * Note that when simulating the effect of {@link NS.grow | grow}, what matters is the state of the server and player + * when the grow *finishes*, not when it is started. + * + * The growth amount depends both linearly *and* exponentially on threads; see {@link NS.grow | grow} for more details. + * + * The inverse of this function is {@link HackingFormulas.growAmount | formulas.hacking.growAmount}, + * although it can work with fractional threads. + * @param server - Server info, typically from {@link NS.getServer | getServer} + * @param player - Player info, typically from {@link NS.getPlayer | getPlayer} + * @param targetMoney - Desired final money, capped to server's moneyMax + * @param cores - Number of cores on the computer that will execute grow. + * @returns The calculated grow threads as an integer, rounded up. + */ +function growThreads(server: IServer, player: IPerson, targetMoney: number, cores = 1): number { + return numCycleForGrowthCorrected(server, player, targetMoney, server.moneyAvailable ?? 0, cores); +} + /** * This function calculates the number of threads needed to grow a server from one $amount to a higher $amount * (ie, how many threads to grow this server from $200 to $600 for example). * It protects the inputs (so putting in INFINITY for targetMoney will use moneyMax, putting in a negative for start will use 0, etc.) * @param server - Server being grown * @param targetMoney - How much you want the server grown TO (not by), for instance, to grow from 200 to 600, input 600 - * @param startMoney - How much you are growing the server from, for instance, to grow from 200 to 600, input 200 + * @param startMoney - How much you are growing the server from, for instance, to grow from 200 to 600, input 200. Usually this will just be the server's current money `server.available`, but it is a parameter to allow for more flexible use. * @param cores - Number of cores on the host performing grow * @returns Integer threads needed by a single ns.grow call to reach targetMoney from startMoney. */ -export function numCycleForGrowthCorrected( +function numCycleForGrowthCorrected( server: IServer, + person: IPerson = Player, targetMoney: number, startMoney: number, cores = 1, - person: IPerson = Player, ): number { if (!server.serverGrowth) return Infinity; const moneyMax = server.moneyMax ?? 1; @@ -286,12 +323,12 @@ export function numCycleForGrowthCorrected( return ccycle + 1; } -export function getCoreBonus(cores = 1): number { +function getCoreBonus(cores = 1): number { const coreBonus = 1 + (cores - 1) / 16; return coreBonus; } -export function getWeakenEffect(threads: number, cores: number): number { +function getWeakenEffect(threads: number, cores: number): number { const coreBonus = getCoreBonus(cores); return ServerConstants.ServerWeakenAmount * threads * coreBonus * currentNodeMults.ServerWeakenRate; } diff --git a/src/utils/formulas/hacknet-nodes.ts b/src/ezgame/formulas/hacknet-nodes.ts similarity index 78% rename from src/utils/formulas/hacknet-nodes.ts rename to src/ezgame/formulas/hacknet-nodes.ts index b50652c..ee8f276 100644 --- a/src/utils/formulas/hacknet-nodes.ts +++ b/src/ezgame/formulas/hacknet-nodes.ts @@ -1,7 +1,15 @@ import { currentNodeMults } from "./exports"; import { HacknetNodeConstants } from "./constants"; -export function calculateMoneyGainRate(level: number, ram: number, cores: number, mult: number): number { +export const hacknetNodes = { + calculateMoneyGainRate, + calculateLevelUpgradeCost, + calculateRamUpgradeCost, + calculateCoreUpgradeCost, + calculateNodeCost, +}; + +function calculateMoneyGainRate(level: number, ram: number, cores: number, mult: number): number { const gainPerLevel = HacknetNodeConstants.MoneyGainPerLevel; const levelMult = level * gainPerLevel; @@ -10,7 +18,7 @@ export function calculateMoneyGainRate(level: number, ram: number, cores: number return levelMult * ramMult * coresMult * mult * currentNodeMults.HacknetNodeMoney; } -export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number { +function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number { const sanitizedLevels = Math.round(extraLevels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -31,7 +39,7 @@ export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1 return HacknetNodeConstants.LevelBaseCost * totalMultiplier * costMult; } -export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, costMult = 1): number { +function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, costMult = 1): number { const sanitizedLevels = Math.round(extraLevels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -60,7 +68,7 @@ export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, co return totalCost; } -export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1, costMult = 1): number { +function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1, costMult = 1): number { const sanitizedCores = Math.round(extraLevels); if (isNaN(sanitizedCores) || sanitizedCores < 1) { return 0; @@ -84,7 +92,7 @@ export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1, return totalCost; } -export function calculateNodeCost(n: number, mult = 1): number { +function calculateNodeCost(n: number, mult = 1): number { if (n <= 0) { return 0; } diff --git a/src/utils/formulas/hacknet-servers.ts b/src/ezgame/formulas/hacknet-servers.ts similarity index 79% rename from src/utils/formulas/hacknet-servers.ts rename to src/ezgame/formulas/hacknet-servers.ts index 0e2e0da..c302f08 100644 --- a/src/utils/formulas/hacknet-servers.ts +++ b/src/ezgame/formulas/hacknet-servers.ts @@ -1,13 +1,16 @@ import { currentNodeMults } from "./exports"; import { HacknetServerConstants } from "./constants"; -export function calculateHashGainRate( - level: number, - ramUsed: number, - maxRam: number, - cores: number, - mult: number, -): number { +export const hacknetServers = { + calculateHashGainRate, + calculateLevelUpgradeCost, + calculateRamUpgradeCost, + calculateCoreUpgradeCost, + calculateCacheUpgradeCost, + calculateServerCost, +}; + +function calculateHashGainRate(level: number, ramUsed: number, maxRam: number, cores: number, mult: number): number { const baseGain = HacknetServerConstants.HashesPerLevel * level; const ramMultiplier = Math.pow(1.07, Math.log2(maxRam)); const coreMultiplier = 1 + (cores - 1) / 5; @@ -16,7 +19,7 @@ export function calculateHashGainRate( return baseGain * ramMultiplier * coreMultiplier * ramRatio * mult * currentNodeMults.HacknetNodeMoney; } -export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number { +function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1, costMult = 1): number { const sanitizedLevels = Math.round(extraLevels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -37,7 +40,7 @@ export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1 return 10 * HacknetServerConstants.BaseCost * totalMultiplier * costMult; } -export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, costMult = 1): number { +function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, costMult = 1): number { const sanitizedLevels = Math.round(extraLevels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -64,7 +67,7 @@ export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, co return totalCost; } -export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1, costMult = 1): number { +function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1, costMult = 1): number { const sanitizedLevels = Math.round(extraLevels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -87,7 +90,7 @@ export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1, return totalCost; } -export function calculateCacheUpgradeCost(startingCache: number, extraLevels = 1): number { +function calculateCacheUpgradeCost(startingCache: number, extraLevels = 1): number { const sanitizedLevels = Math.round(extraLevels); if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { return 0; @@ -111,7 +114,7 @@ export function calculateCacheUpgradeCost(startingCache: number, extraLevels = 1 // TODO: reverse engineer hashUpgradeCost -export function calculateServerCost(n: number, mult = 1): number { +function calculateServerCost(n: number, mult = 1): number { if (n - 1 >= HacknetServerConstants.MaxServers) { return Infinity; } diff --git a/src/utils/formulas/player.ts b/src/ezgame/formulas/player.ts similarity index 100% rename from src/utils/formulas/player.ts rename to src/ezgame/formulas/player.ts diff --git a/src/utils/formulas/reputation.ts b/src/ezgame/formulas/reputation.ts similarity index 62% rename from src/utils/formulas/reputation.ts rename to src/ezgame/formulas/reputation.ts index ebb9916..89055b8 100644 --- a/src/utils/formulas/reputation.ts +++ b/src/ezgame/formulas/reputation.ts @@ -1,35 +1,45 @@ import { Person as IPerson } from "@ns"; import { currentNodeMults } from "./exports"; -import { clampNumber } from "./utils"; import { CONSTANTS, log1point02, MaxFavor } from "./constants"; import { Player } from "./player"; +import { clampNumber } from "@/utils/utils"; -export function favorToRep(f: number): number { +export const reputation = { + favorToRep, + repToFavor, + calculateFavorAfterResetting, + repFromDonation, + donationForRep, + repNeededToDonate, + canDonate, +}; + +function favorToRep(f: number): number { // expm1 is e^x - 1, which is more accurate for small x than doing it the obvious way. return clampNumber(25000 * Math.expm1(log1point02 * f), 0); } -export function repToFavor(r: number): number { +function repToFavor(r: number): number { // log1p is log(x + 1), which is more accurate for small x than doing it the obvious way. return clampNumber(Math.log1p(r / 25000) / log1point02, 0, MaxFavor); } -export function calculateFavorAfterResetting(favor: number, playerReputation: number) { +function calculateFavorAfterResetting(favor: number, playerReputation: number) { return repToFavor(favorToRep(favor) + playerReputation); } -export function repFromDonation(amt: number, person: IPerson): number { +function repFromDonation(amt: number, person: IPerson): number { return (amt / CONSTANTS.DonateMoneyToRepDivisor) * person.mults.faction_rep * currentNodeMults.FactionWorkRepGain; } -export function donationForRep(rep: number, person: IPerson): number { +function donationForRep(rep: number, person: IPerson): number { return (rep * CONSTANTS.DonateMoneyToRepDivisor) / person.mults.faction_rep / currentNodeMults.FactionWorkRepGain; } -export function repNeededToDonate(): number { +function repNeededToDonate(): number { return Math.floor(CONSTANTS.BaseFavorToDonate * currentNodeMults.RepToDonateToFaction); } -export function canDonate(amt: number): boolean { +function canDonate(amt: number): boolean { return !isNaN(amt) && amt > 0 && Player.money >= amt; } diff --git a/src/utils/formulas/skills.ts b/src/ezgame/formulas/skills.ts similarity index 86% rename from src/utils/formulas/skills.ts rename to src/ezgame/formulas/skills.ts index b5ad82c..3b542dd 100644 --- a/src/utils/formulas/skills.ts +++ b/src/ezgame/formulas/skills.ts @@ -1,4 +1,11 @@ -import { clampNumber } from "./utils"; +import { clampNumber } from "@/utils/utils"; + +export const skills = { + calculateSkill, + calculateExp, + calculateSkillProgress, + getEmptySkillProgress, +}; /** * Given an experience amount and stat multiplier, calculates the @@ -14,7 +21,7 @@ export function calculateSkill(exp: number, mult = 1): number { return clampNumber(value, 1); } -export function calculateExp(skill: number, mult = 1): number { +function calculateExp(skill: number, mult = 1): number { const floorSkill = Math.floor(skill); let value = Math.exp((skill / mult + 200) / 32) - 534.6; if (skill === floorSkill && Number.isFinite(skill) && Number.isFinite(value)) { @@ -33,7 +40,7 @@ export function calculateExp(skill: number, mult = 1): number { return clampNumber(value, 0); } -export function calculateSkillProgress(exp: number, mult = 1): ISkillProgress { +function calculateSkillProgress(exp: number, mult = 1): ISkillProgress { const currentSkill = calculateSkill(exp, mult); const nextSkill = currentSkill + 1; @@ -60,7 +67,7 @@ export function calculateSkillProgress(exp: number, mult = 1): ISkillProgress { }; } -export interface ISkillProgress { +interface ISkillProgress { currentSkill: number; nextSkill: number; baseExperience: number; @@ -71,7 +78,7 @@ export interface ISkillProgress { progress: number; } -export function getEmptySkillProgress(): ISkillProgress { +function getEmptySkillProgress(): ISkillProgress { return { currentSkill: 0, nextSkill: 0, diff --git a/src/test.ts b/src/test.ts index b85bd6c..7dfb528 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,9 +1,11 @@ import { NS } from "@ns"; -import { scan } from "./utils/scan"; -import { startall } from "./utils/startall"; -import { kill } from "./utils/kill"; +import { ezgame } from "./ezgame"; export const main = (ns: NS) => { - ns.tprint(ns.getPurchasedServerCost(256)); - console.log(ns.getPlayer()); + const server = ns.getServer("n00dles"); + const player = ns.getPlayer(); + const testResult = ezgame.formulas.hacking.growTime(server, player); + const actualResult = ns.formulas.hacking.growTime(server, player); + console.log(testResult); + console.log(actualResult); }; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 16d233a..d038081 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,9 @@ -// format the date/time -// if the date is today, show the time +/** + * Format the date/time. + * If the date is today, show the time. Otherwise, show the date in "day month year" format. + * @param dateString The date string to format + * @returns Formatted date/time string + */ export const utilityFormatDate = (dateString: string): string => { const date = new Date(dateString); const today = new Date(); @@ -20,7 +24,11 @@ export const utilityFormatDate = (dateString: string): string => { } }; -// format the file sizes +/** + * Format bytes into human-readable size + * @param bytes Number of bytes to format + * @returns Formatted size string + */ export const utilityFormatSize = (bytes: number): string => { if (bytes === 0) return "0 Bytes"; @@ -32,7 +40,11 @@ export const utilityFormatSize = (bytes: number): string => { return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }; -// UNformat the file sizes +/** + * Convert a formatted size string back to bytes + * @param formattedSize Formatted size string (must be in the same format produced by `utilityFormatSize()`) + * @returns Number of bytes + */ export const utilityUnformatSize = (formattedSize: string): number => { const sizes = ["Bytes", "KB", "MB", "GB"]; const [value, unit] = formattedSize.split(" "); @@ -40,9 +52,12 @@ export const utilityUnformatSize = (formattedSize: string): number => { return parseFloat(value) * Math.pow(1024, index); }; -// extract filename and extension from a path -// heeheeheehaw safe coding my boys 🤠 -// only consider things after the last period +/** + * Extract filename stem and extension from a file path + * @param filePath The file path to parse + * @returns Object containing `fileStem` and `fileExtension`. `fileStem` is the filename without the extension, and `fileExtension` is the extension in lowercase (or an empty string if there is no extension). + * @throws Error if the file path is invalid + */ export const utilityExtractFilename = (filePath: string): { fileStem: string; fileExtension: string } | null => { const lastSlashIndex = filePath.lastIndexOf("/"); const lastDotIndex = filePath.lastIndexOf("."); @@ -74,11 +89,11 @@ export const utilityExtractFilename = (filePath: string): { fileStem: string; fi }; /** - * Clamps the value on a lower and an upper bound - * @param {number} value Value to clamp - * @param {number} min Lower bound, defaults to negative Number.MAX_VALUE - * @param {number} max Upper bound, defaults to Number.MAX_VALUE - * @returns {number} Clamped value + * Clamps a value to a lower and upper bound + * @param value Value to clamp + * @param min Lower bound, defaults to negative `Number.MAX_VALUE` + * @param max Upper bound, defaults to `Number.MAX_VALUE` + * @returns Clamped value */ export function clampNumber(value: number, min: number = -Number.MAX_VALUE, max: number = Number.MAX_VALUE): number { if (isNaN(value)) { @@ -89,11 +104,11 @@ export function clampNumber(value: number, min: number = -Number.MAX_VALUE, max: } /** - * Clamps the value to an integer within a lower and an upper bound - * @param {number} value Value to clamp - * @param {number} min Lower bound, defaults to negative Number.MAX_SAFE_INTEGER - * @param {number} max Upper bound, defaults to Number.MAX_SAFE_INTEGER - * @returns {number} Clamped integer value + * Clamps a value to an integer within a lower and upper bound + * @param value Value to clamp + * @param min Lower bound, defaults to negative `Number.MAX_SAFE_INTEGER` + * @param max Upper bound, defaults to `Number.MAX_SAFE_INTEGER` + * @returns Clamped integer value */ export function clampInteger( value: number, @@ -108,11 +123,32 @@ export function clampInteger( } /** - * Checks that a variable is a valid number. A valid number - * must be a "number" type and cannot be NaN - * @param {number} n The number to check - * @returns {boolean} True if n is a valid number, false otherwise + * Checks that a variable is a valid number + * A valid number must be of type "number" and cannot be NaN + * @param n The number to check + * @returns True if n is a valid number, false otherwise */ export function isValidNumber(n: number): boolean { return typeof n === "number" && !isNaN(n); } + +/** + * Generate a random number between min and max (inclusive) + * @param min Minimum value (inclusive) + * @param max Maximum value (inclusive) + * @returns Random number between min and max + */ +export function randomNumber(min: number, max: number): number { + if (min > max) throw new Error("min cannot be greater than max"); + return Math.random() * (max - min) + min; +} + +/** + * Generate a random integer between min and max (inclusive) + * @param min Minimum value (inclusive) + * @param max Maximum value (inclusive) + * @returns Random integer between min and max + */ +export function randomInteger(min: number, max: number): number { + return Math.floor(randomNumber(min, max + 1)); +}