namespaced formulas for easier importing

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Vomitblood 2026-05-02 01:00:05 +08:00
parent a6a00066cc
commit f12de39c91
15 changed files with 225 additions and 88 deletions

View file

@ -2,6 +2,10 @@
NOTE: All user scripts to be synced with the game is found under `src/`. 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. This is a template for a viteburner project. It is a simple example of how to use Viteburner.
## How to use ## How to use

5
src/ezgame.ts Normal file
View file

@ -0,0 +1,5 @@
import { formulas } from "./ezgame/formulas";
export const ezgame = {
formulas,
};

15
src/ezgame/formulas.ts Normal file
View file

@ -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,
};

View file

@ -6,7 +6,7 @@ Reverse engineered from the [bitburner source](https://github.com/bitburner-offi
### Gang ### Gang
- [ ] `ascencionMultipler(points)` - [ ] `ascensionMultiplier(points)`
- [ ] `ascensionPointsGain(exp)` - [ ] `ascensionPointsGain(exp)`
- [ ] `moneyGain(gang, member, task)` - [ ] `moneyGain(gang, member, task)`
- [ ] `respectGain(gang, member, task)` - [ ] `respectGain(gang, member, task)`

View file

@ -1,4 +1,5 @@
import { Multipliers } from "@ns"; import { Multipliers } from "@ns";
import { PartialRecord } from "./exports";
/** /**
* Generic Game Constants * 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. // 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. // It is *not* the same as Math.log(1.02), since "1.02" lacks sufficient precision.
export const log1point02 = 0.019802627296179712; export const log1point02 = 0.019802627296179712;
export const getRecordEntries = Object.entries as <K extends string, V>(record: PartialRecord<K, V>) => [K, V][];

View file

@ -1,8 +1,7 @@
import { clampNumber } from "./utils"; import { clampNumber } from "@/utils/utils";
import { getRecordEntries } from "./constants";
export type PartialRecord<K extends string, V> = Partial<Record<K, V>>; export type PartialRecord<K extends string, V> = Partial<Record<K, V>>;
export const getRecordEntries = Object.entries as <K extends string, V>(record: PartialRecord<K, V>) => [K, V][];
/** /**
* Bitnode multipliers influence the difficulty of different aspects of the game. * 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 * Each Bitnode has a different theme/strategy to achieving the end goal, so these multipliers will can help drive the

View file

@ -1,18 +1,27 @@
import { currentNodeMults } from "./exports"; 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; respect: number;
territory: number; territory: number;
wantedLevel: number; wantedLevel: number;
} }
export interface ITerritory { interface ITerritory {
money: number; money: number;
respect: number; respect: number;
wanted: number; wanted: number;
} }
export interface ITaskParams { interface ITaskParams {
baseRespect?: number; baseRespect?: number;
baseWanted?: number; baseWanted?: number;
baseMoney?: number; baseMoney?: number;
@ -26,7 +35,7 @@ export interface ITaskParams {
territory?: ITerritory; territory?: ITerritory;
} }
export class GangMember { class GangMember {
name: string; name: string;
task = "Unassigned"; task = "Unassigned";
@ -68,7 +77,7 @@ export class GangMember {
} }
} }
export class GangMemberTask { class GangMemberTask {
name: string; name: string;
desc: 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); 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; if (task.baseRespect === 0) return 0;
let statWeight = let statWeight =
(task.hackWeight / 100) * member.hack + (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); 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; if (task.baseWanted === 0) return 0;
let statWeight = let statWeight =
(task.hackWeight / 100) * member.hack + (task.hackWeight / 100) * member.hack +
@ -177,7 +186,7 @@ export function calculateWantedLevelGain(gang: FormulaGang, member: GangMember,
return Math.min(100, calc); 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; if (task.baseMoney === 0) return 0;
let statWeight = let statWeight =
(task.hackWeight / 100) * member.hack + (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); 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); 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); return Math.max(Math.pow(points / 2000, 0.5), 1);
} }

View file

@ -1,10 +1,26 @@
import { clampNumber, isValidNumber } from "@/utils/utils";
import { Player as IPerson, Server as IServer } from "@ns"; import { Player as IPerson, Server as IServer } from "@ns";
import { ServerConstants } from "./constants"; import { ServerConstants } from "./constants";
import { currentNodeMults } from "./exports"; import { currentNodeMults } from "./exports";
import { Player } from "./player"; 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 = const effectiveIntelligence =
Player.bitNodeOptions.intelligenceOverride !== undefined Player.bitNodeOptions.intelligenceOverride !== undefined
? Math.min(Player.bitNodeOptions.intelligenceOverride, intelligence) ? 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 */ /** 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 hackDifficulty = server.hackDifficulty ?? 100;
const requiredHackingSkill = server.requiredHackingSkill ?? 1e9; const requiredHackingSkill = server.requiredHackingSkill ?? 1e9;
// Unrooted or unhackable server // 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 * Returns the amount of hacking experience the person will gain upon
* successfully hacking a server * successfully hacking a server
*/ */
export function calculateHackingExpGain(server: IServer, person: IPerson): number { function calculateHackingExpGain(server: IServer, person: IPerson): number {
const baseDifficulty = server.baseDifficulty; const baseDifficulty = server.baseDifficulty;
if (!baseDifficulty) return 0; if (!baseDifficulty) return 0;
const baseExpGain = 3; 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 * 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) * 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; const hackDifficulty = server.hackDifficulty ?? 100;
if (hackDifficulty >= 100) return 0; if (hackDifficulty >= 100) return 0;
const requiredHackingSkill = server.requiredHackingSkill ?? 1e9; 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 */ /** 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; const { hackDifficulty, requiredHackingSkill } = server;
if (typeof hackDifficulty !== "number" || typeof requiredHackingSkill !== "number") return Infinity; if (typeof hackDifficulty !== "number" || typeof requiredHackingSkill !== "number") return Infinity;
const difficultyMult = requiredHackingSkill * hackDifficulty; 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 */ /** 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 const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2
return growTimeMultiplier * calculateHackingTime(server, person); return growTimeMultiplier * calculateHackingTime(server, person);
} }
/** Returns time it takes to complete a weaken operation on a server, in seconds */ /** 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 const weakenTimeMultiplier = 4; // Relative to hacking time
return weakenTimeMultiplier * calculateHackingTime(server, person); return weakenTimeMultiplier * calculateHackingTime(server, person);
} }
// Returns the log of the growth rate. When passing 1 for threads, this gives a useful constant. // 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; if (!server.serverGrowth) return -Infinity;
const hackDifficulty = server.hackDifficulty ?? 100; const hackDifficulty = server.hackDifficulty ?? 100;
const numServerGrowthCycles = Math.max(threads, 0); 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; 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; if (!server.serverGrowth) return 0;
return Math.exp(calculateServerGrowthLog(server, threads, p, cores)); return Math.exp(calculateServerGrowthLog(server, threads, p, cores));
} }
// This differs from calculateServerGrowth in that it includes the additive // This differs from calculateServerGrowth in that it includes the additive
// factor and all the boundary checks. // 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); let serverGrowth = calculateServerGrowth(server, threads, p, cores);
if (serverGrowth < 1) { if (serverGrowth < 1) {
console.warn("serverGrowth calculated to be less than 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 * @param p - Reference to Player object
* @returns Number of "growth cycles" needed * @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; if (!server.serverGrowth) return Infinity;
return Math.log(growth) / calculateServerGrowthLog(server, 1, Player, cores); 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 * 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). * (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.) * 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 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 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 * @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. * @returns Integer threads needed by a single ns.grow call to reach targetMoney from startMoney.
*/ */
export function numCycleForGrowthCorrected( function numCycleForGrowthCorrected(
server: IServer, server: IServer,
person: IPerson = Player,
targetMoney: number, targetMoney: number,
startMoney: number, startMoney: number,
cores = 1, cores = 1,
person: IPerson = Player,
): number { ): number {
if (!server.serverGrowth) return Infinity; if (!server.serverGrowth) return Infinity;
const moneyMax = server.moneyMax ?? 1; const moneyMax = server.moneyMax ?? 1;
@ -286,12 +323,12 @@ export function numCycleForGrowthCorrected(
return ccycle + 1; return ccycle + 1;
} }
export function getCoreBonus(cores = 1): number { function getCoreBonus(cores = 1): number {
const coreBonus = 1 + (cores - 1) / 16; const coreBonus = 1 + (cores - 1) / 16;
return coreBonus; return coreBonus;
} }
export function getWeakenEffect(threads: number, cores: number): number { function getWeakenEffect(threads: number, cores: number): number {
const coreBonus = getCoreBonus(cores); const coreBonus = getCoreBonus(cores);
return ServerConstants.ServerWeakenAmount * threads * coreBonus * currentNodeMults.ServerWeakenRate; return ServerConstants.ServerWeakenAmount * threads * coreBonus * currentNodeMults.ServerWeakenRate;
} }

View file

@ -1,7 +1,15 @@
import { currentNodeMults } from "./exports"; import { currentNodeMults } from "./exports";
import { HacknetNodeConstants } from "./constants"; 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 gainPerLevel = HacknetNodeConstants.MoneyGainPerLevel;
const levelMult = level * gainPerLevel; const levelMult = level * gainPerLevel;
@ -10,7 +18,7 @@ export function calculateMoneyGainRate(level: number, ram: number, cores: number
return levelMult * ramMult * coresMult * mult * currentNodeMults.HacknetNodeMoney; 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); const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@ -31,7 +39,7 @@ export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1
return HacknetNodeConstants.LevelBaseCost * totalMultiplier * costMult; 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); const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@ -60,7 +68,7 @@ export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, co
return totalCost; 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); const sanitizedCores = Math.round(extraLevels);
if (isNaN(sanitizedCores) || sanitizedCores < 1) { if (isNaN(sanitizedCores) || sanitizedCores < 1) {
return 0; return 0;
@ -84,7 +92,7 @@ export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1,
return totalCost; return totalCost;
} }
export function calculateNodeCost(n: number, mult = 1): number { function calculateNodeCost(n: number, mult = 1): number {
if (n <= 0) { if (n <= 0) {
return 0; return 0;
} }

View file

@ -1,13 +1,16 @@
import { currentNodeMults } from "./exports"; import { currentNodeMults } from "./exports";
import { HacknetServerConstants } from "./constants"; import { HacknetServerConstants } from "./constants";
export function calculateHashGainRate( export const hacknetServers = {
level: number, calculateHashGainRate,
ramUsed: number, calculateLevelUpgradeCost,
maxRam: number, calculateRamUpgradeCost,
cores: number, calculateCoreUpgradeCost,
mult: number, calculateCacheUpgradeCost,
): number { calculateServerCost,
};
function calculateHashGainRate(level: number, ramUsed: number, maxRam: number, cores: number, mult: number): number {
const baseGain = HacknetServerConstants.HashesPerLevel * level; const baseGain = HacknetServerConstants.HashesPerLevel * level;
const ramMultiplier = Math.pow(1.07, Math.log2(maxRam)); const ramMultiplier = Math.pow(1.07, Math.log2(maxRam));
const coreMultiplier = 1 + (cores - 1) / 5; const coreMultiplier = 1 + (cores - 1) / 5;
@ -16,7 +19,7 @@ export function calculateHashGainRate(
return baseGain * ramMultiplier * coreMultiplier * ramRatio * mult * currentNodeMults.HacknetNodeMoney; 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); const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@ -37,7 +40,7 @@ export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1
return 10 * HacknetServerConstants.BaseCost * totalMultiplier * costMult; 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); const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@ -64,7 +67,7 @@ export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, co
return totalCost; 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); const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@ -87,7 +90,7 @@ export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1,
return totalCost; return totalCost;
} }
export function calculateCacheUpgradeCost(startingCache: number, extraLevels = 1): number { function calculateCacheUpgradeCost(startingCache: number, extraLevels = 1): number {
const sanitizedLevels = Math.round(extraLevels); const sanitizedLevels = Math.round(extraLevels);
if (isNaN(sanitizedLevels) || sanitizedLevels < 1) { if (isNaN(sanitizedLevels) || sanitizedLevels < 1) {
return 0; return 0;
@ -111,7 +114,7 @@ export function calculateCacheUpgradeCost(startingCache: number, extraLevels = 1
// TODO: reverse engineer hashUpgradeCost // TODO: reverse engineer hashUpgradeCost
export function calculateServerCost(n: number, mult = 1): number { function calculateServerCost(n: number, mult = 1): number {
if (n - 1 >= HacknetServerConstants.MaxServers) { if (n - 1 >= HacknetServerConstants.MaxServers) {
return Infinity; return Infinity;
} }

View file

@ -1,35 +1,45 @@
import { Person as IPerson } from "@ns"; import { Person as IPerson } from "@ns";
import { currentNodeMults } from "./exports"; import { currentNodeMults } from "./exports";
import { clampNumber } from "./utils";
import { CONSTANTS, log1point02, MaxFavor } from "./constants"; import { CONSTANTS, log1point02, MaxFavor } from "./constants";
import { Player } from "./player"; 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. // 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); 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. // 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); 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); 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; 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; return (rep * CONSTANTS.DonateMoneyToRepDivisor) / person.mults.faction_rep / currentNodeMults.FactionWorkRepGain;
} }
export function repNeededToDonate(): number { function repNeededToDonate(): number {
return Math.floor(CONSTANTS.BaseFavorToDonate * currentNodeMults.RepToDonateToFaction); 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; return !isNaN(amt) && amt > 0 && Player.money >= amt;
} }

View file

@ -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 * 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); return clampNumber(value, 1);
} }
export function calculateExp(skill: number, mult = 1): number { function calculateExp(skill: number, mult = 1): number {
const floorSkill = Math.floor(skill); const floorSkill = Math.floor(skill);
let value = Math.exp((skill / mult + 200) / 32) - 534.6; let value = Math.exp((skill / mult + 200) / 32) - 534.6;
if (skill === floorSkill && Number.isFinite(skill) && Number.isFinite(value)) { 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); 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 currentSkill = calculateSkill(exp, mult);
const nextSkill = currentSkill + 1; const nextSkill = currentSkill + 1;
@ -60,7 +67,7 @@ export function calculateSkillProgress(exp: number, mult = 1): ISkillProgress {
}; };
} }
export interface ISkillProgress { interface ISkillProgress {
currentSkill: number; currentSkill: number;
nextSkill: number; nextSkill: number;
baseExperience: number; baseExperience: number;
@ -71,7 +78,7 @@ export interface ISkillProgress {
progress: number; progress: number;
} }
export function getEmptySkillProgress(): ISkillProgress { function getEmptySkillProgress(): ISkillProgress {
return { return {
currentSkill: 0, currentSkill: 0,
nextSkill: 0, nextSkill: 0,

View file

@ -1,9 +1,11 @@
import { NS } from "@ns"; import { NS } from "@ns";
import { scan } from "./utils/scan"; import { ezgame } from "./ezgame";
import { startall } from "./utils/startall";
import { kill } from "./utils/kill";
export const main = (ns: NS) => { export const main = (ns: NS) => {
ns.tprint(ns.getPurchasedServerCost(256)); const server = ns.getServer("n00dles");
console.log(ns.getPlayer()); 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);
}; };

View file

@ -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 => { export const utilityFormatDate = (dateString: string): string => {
const date = new Date(dateString); const date = new Date(dateString);
const today = new Date(); 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 => { export const utilityFormatSize = (bytes: number): string => {
if (bytes === 0) return "0 Bytes"; 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]; 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 => { export const utilityUnformatSize = (formattedSize: string): number => {
const sizes = ["Bytes", "KB", "MB", "GB"]; const sizes = ["Bytes", "KB", "MB", "GB"];
const [value, unit] = formattedSize.split(" "); const [value, unit] = formattedSize.split(" ");
@ -40,9 +52,12 @@ export const utilityUnformatSize = (formattedSize: string): number => {
return parseFloat(value) * Math.pow(1024, index); return parseFloat(value) * Math.pow(1024, index);
}; };
// extract filename and extension from a path /**
// heeheeheehaw safe coding my boys 🤠 * Extract filename stem and extension from a file path
// only consider things after the last period * @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 => { export const utilityExtractFilename = (filePath: string): { fileStem: string; fileExtension: string } | null => {
const lastSlashIndex = filePath.lastIndexOf("/"); const lastSlashIndex = filePath.lastIndexOf("/");
const lastDotIndex = 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 * Clamps a value to a lower and upper bound
* @param {number} value Value to clamp * @param value Value to clamp
* @param {number} min Lower bound, defaults to negative Number.MAX_VALUE * @param min Lower bound, defaults to negative `Number.MAX_VALUE`
* @param {number} max Upper bound, defaults to Number.MAX_VALUE * @param max Upper bound, defaults to `Number.MAX_VALUE`
* @returns {number} Clamped value * @returns Clamped value
*/ */
export function clampNumber(value: number, min: number = -Number.MAX_VALUE, max: number = Number.MAX_VALUE): number { export function clampNumber(value: number, min: number = -Number.MAX_VALUE, max: number = Number.MAX_VALUE): number {
if (isNaN(value)) { 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 * Clamps a value to an integer within a lower and upper bound
* @param {number} value Value to clamp * @param value Value to clamp
* @param {number} min Lower bound, defaults to negative Number.MAX_SAFE_INTEGER * @param min Lower bound, defaults to negative `Number.MAX_SAFE_INTEGER`
* @param {number} max Upper bound, defaults to Number.MAX_SAFE_INTEGER * @param max Upper bound, defaults to `Number.MAX_SAFE_INTEGER`
* @returns {number} Clamped integer value * @returns Clamped integer value
*/ */
export function clampInteger( export function clampInteger(
value: number, value: number,
@ -108,11 +123,32 @@ export function clampInteger(
} }
/** /**
* Checks that a variable is a valid number. A valid number * Checks that a variable is a valid number
* must be a "number" type and cannot be NaN * A valid number must be of type "number" and cannot be NaN
* @param {number} n The number to check * @param n The number to check
* @returns {boolean} True if n is a valid number, false otherwise * @returns True if n is a valid number, false otherwise
*/ */
export function isValidNumber(n: number): boolean { export function isValidNumber(n: number): boolean {
return typeof n === "number" && !isNaN(n); 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));
}