fixed v3.0.0 api breaks

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Vomitblood 2026-05-03 18:27:06 +08:00
parent d5e9028750
commit f8c8a07f44
13 changed files with 2426 additions and 1501 deletions

File diff suppressed because it is too large Load diff

View file

@ -200,6 +200,7 @@ export const defaultMultipliers = (): Multipliers => {
work_money: 1, work_money: 1,
crime_success: 1, crime_success: 1,
crime_money: 1, crime_money: 1,
dnet_money: 1,
bladeburner_max_stamina: 1, bladeburner_max_stamina: 1,
bladeburner_stamina_gain: 1, bladeburner_stamina_gain: 1,
bladeburner_analysis: 1, bladeburner_analysis: 1,

View file

@ -1,4 +1,4 @@
import { clampNumber } from "@/utils/utils"; import { utils } from "@/utils/utils";
import { getRecordEntries } from "./constants"; 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>>;
@ -176,7 +176,7 @@ export class BitNodeMultipliers {
WorldDaemonDifficulty = 1; WorldDaemonDifficulty = 1;
constructor(a: PartialRecord<keyof BitNodeMultipliers, number> = {}) { constructor(a: PartialRecord<keyof BitNodeMultipliers, number> = {}) {
for (const [key, value] of getRecordEntries(a)) this[key] = clampNumber(value); for (const [key, value] of getRecordEntries(a)) this[key] = utils.clampNumber(value);
} }
} }

View file

@ -1,8 +1,8 @@
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 { utils } from "@/utils/utils";
export const hacking = { export const hacking = {
growAmount: calculateGrowMoney, growAmount: calculateGrowMoney,
@ -21,11 +21,7 @@ export const hacking = {
}; };
function calculateIntelligenceBonus(intelligence: number, weight = 1): number { function calculateIntelligenceBonus(intelligence: number, weight = 1): number {
const effectiveIntelligence = return 1 + (weight * Math.pow(intelligence, 0.8)) / 600;
Player.bitNodeOptions.intelligenceOverride !== undefined
? Math.min(Player.bitNodeOptions.intelligenceOverride, intelligence)
: intelligence;
return 1 + (weight * Math.pow(effectiveIntelligence, 0.8)) / 600;
} }
/** Returns the chance the person has to successfully hack a server */ /** Returns the chance the person has to successfully hack a server */
@ -36,14 +32,14 @@ function calculateHackingChance(server: IServer, person: IPerson): number {
if (!server.hasAdminRights || hackDifficulty >= 100) return 0; if (!server.hasAdminRights || hackDifficulty >= 100) return 0;
const hackFactor = 1.75; const hackFactor = 1.75;
const difficultyMult = (100 - hackDifficulty) / 100; const difficultyMult = (100 - hackDifficulty) / 100;
const skillMult = clampNumber(hackFactor * person.skills.hacking, 1); const skillMult = utils.clampNumber(hackFactor * person.skills.hacking, 1);
const skillChance = (skillMult - requiredHackingSkill) / skillMult; const skillChance = (skillMult - requiredHackingSkill) / skillMult;
const chance = const chance =
skillChance * skillChance *
difficultyMult * difficultyMult *
person.mults.hacking_chance * person.mults.hacking_chance *
calculateIntelligenceBonus(person.skills.intelligence, 1); calculateIntelligenceBonus(person.skills.intelligence, 1);
return clampNumber(chance, 0, 1); return utils.clampNumber(chance, 0, 1);
} }
/** /**
@ -79,8 +75,9 @@ function calculatePercentMoneyHacked(server: IServer, person: IPerson): number {
return Math.min(1, Math.max(percentMoneyHacked, 0)); return Math.min(1, Math.max(percentMoneyHacked, 0));
} }
/** 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 milliseconds */
function calculateHackingTime(server: IServer, person: IPerson): number { function calculateHackingTime(server: IServer, person: IPerson): number {
if (utils.isDarknetServer(server)) return 16;
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;
@ -98,17 +95,17 @@ function calculateHackingTime(server: IServer, person: IPerson): number {
currentNodeMults.HackingSpeedMultiplier * currentNodeMults.HackingSpeedMultiplier *
calculateIntelligenceBonus(person.skills.intelligence, 1)); calculateIntelligenceBonus(person.skills.intelligence, 1));
return hackingTime; return hackingTime * 1000;
} }
/** 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 milliseconds */
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 milliseconds */
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
@ -160,7 +157,7 @@ function calculateGrowMoney(server: IServer, p: IPerson, threads: number, cores
// cap at max (or data corruption) // cap at max (or data corruption)
if ( if (
server.moneyMax !== undefined && server.moneyMax !== undefined &&
isValidNumber(server.moneyMax) && utils.isValidNumber(server.moneyMax) &&
(moneyAvailable > server.moneyMax || isNaN(moneyAvailable)) (moneyAvailable > server.moneyMax || isNaN(moneyAvailable))
) { ) {
moneyAvailable = server.moneyMax; moneyAvailable = server.moneyMax;

View file

@ -1,8 +1,8 @@
import { utils } from "@/utils/utils";
import { Person as IPerson } from "@ns"; import { Person as IPerson } from "@ns";
import { currentNodeMults } from "./exports";
import { CONSTANTS, log1point02, MaxFavor } from "./constants"; import { CONSTANTS, log1point02, MaxFavor } from "./constants";
import { currentNodeMults } from "./exports";
import { Player } from "./player"; import { Player } from "./player";
import { clampNumber } from "@/utils/utils";
export const reputation = { export const reputation = {
favorToRep, favorToRep,
@ -16,12 +16,12 @@ export const reputation = {
function favorToRep(f: number): number { 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 utils.clampNumber(25000 * Math.expm1(log1point02 * f), 0);
} }
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 utils.clampNumber(Math.log1p(r / 25000) / log1point02, 0, MaxFavor);
} }
function calculateFavorAfterResetting(favor: number, playerReputation: number) { function calculateFavorAfterResetting(favor: number, playerReputation: number) {

View file

@ -1,4 +1,4 @@
import { clampNumber } from "@/utils/utils"; import { utils } from "@/utils/utils";
export const skills = { export const skills = {
calculateSkill, calculateSkill,
@ -18,7 +18,7 @@ export function calculateSkill(exp: number, mult = 1): number {
return 1; return 1;
} }
const value = Math.floor(mult * (32 * Math.log(exp + 534.6) - 200)); const value = Math.floor(mult * (32 * Math.log(exp + 534.6) - 200));
return clampNumber(value, 1); return utils.clampNumber(value, 1);
} }
function calculateExp(skill: number, mult = 1): number { function calculateExp(skill: number, mult = 1): number {
@ -37,7 +37,7 @@ function calculateExp(skill: number, mult = 1): number {
} }
value = newValue; value = newValue;
} }
return clampNumber(value, 0); return utils.clampNumber(value, 0);
} }
function calculateSkillProgress(exp: number, mult = 1): ISkillProgress { function calculateSkillProgress(exp: number, mult = 1): ISkillProgress {
@ -50,10 +50,10 @@ function calculateSkillProgress(exp: number, mult = 1): ISkillProgress {
const normalize = (value: number): number => ((value - baseExperience) * 100) / (nextExperience - baseExperience); const normalize = (value: number): number => ((value - baseExperience) * 100) / (nextExperience - baseExperience);
const rawProgress = nextExperience - baseExperience !== 0 ? normalize(exp) : 99.99; const rawProgress = nextExperience - baseExperience !== 0 ? normalize(exp) : 99.99;
const progress = clampNumber(rawProgress, 0, 100); const progress = utils.clampNumber(rawProgress, 0, 100);
const currentExperience = clampNumber(exp - baseExperience, 0); const currentExperience = utils.clampNumber(exp - baseExperience, 0);
const remainingExperience = clampNumber(nextExperience - exp, 0); const remainingExperience = utils.clampNumber(nextExperience - exp, 0);
return { return {
currentSkill, currentSkill,

View file

@ -1,5 +1,6 @@
import { ezgame } from "@/ezgame"; import { ezgame } from "@/ezgame";
import { randomNumber } from "@/utils/utils"; import { getNormalServer } from "@/utils/get-normal-server";
import { utils } from "@/utils/utils";
import { NS } from "@ns"; import { NS } from "@ns";
interface TestCase { interface TestCase {
@ -7,6 +8,12 @@ interface TestCase {
fn: (ns: NS, serverName: string) => void; fn: (ns: NS, serverName: string) => void;
} }
// the actual result sometimes has 1 - 2 extra decimal places compared to my implementation,
// which causes tests to fail, so we round both results to 5 decimal places before comparing
function roundTo5Decimals(num: number): number {
return Math.round(num * 100000) / 100000;
}
// add servers to be tested against here // add servers to be tested against here
const serverNames: string[] = ["n00dles", "foodnstuff", "joesguns", "sigma-cosmetics", "max-hardware"]; const serverNames: string[] = ["n00dles", "foodnstuff", "joesguns", "sigma-cosmetics", "max-hardware"];
@ -15,9 +22,9 @@ const gangTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "gang.ascensionMultiplier", name: "gang.ascensionMultiplier",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const points = randomNumber(0, 1e9); const points = utils.randomNumber(0, 1e9);
const testResult = ezgame.formulas.gang.ascensionMultiplier(points); const testResult = roundTo5Decimals(ezgame.formulas.gang.ascensionMultiplier(points));
const actualResult = ns.formulas.gang.ascensionMultiplier(points); const actualResult = roundTo5Decimals(ns.formulas.gang.ascensionMultiplier(points));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -26,9 +33,9 @@ const gangTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "gang.ascensionPointsGain", name: "gang.ascensionPointsGain",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const exp = randomNumber(0, 1e9); const exp = utils.randomNumber(0, 1e9);
const testResult = ezgame.formulas.gang.ascensionPointsGain(exp); const testResult = roundTo5Decimals(ezgame.formulas.gang.ascensionPointsGain(exp));
const actualResult = ns.formulas.gang.ascensionPointsGain(exp); const actualResult = roundTo5Decimals(ns.formulas.gang.ascensionPointsGain(exp));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -45,12 +52,12 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.growAmount", name: "hacking.growAmount",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
const threads = randomNumber(1, 100); const threads = utils.randomNumber(1, 100);
const cores = randomNumber(1, 16); const cores = utils.randomNumber(1, 16);
const testResult = ezgame.formulas.hacking.growAmount(server, player, threads, cores); const testResult = roundTo5Decimals(ezgame.formulas.hacking.growAmount(server, player, threads, cores));
const actualResult = ns.formulas.hacking.growAmount(server, player, threads, cores); const actualResult = roundTo5Decimals(ns.formulas.hacking.growAmount(server, player, threads, cores));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -59,12 +66,12 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.growPercent", name: "hacking.growPercent",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const threads = randomNumber(1, 100); const threads = utils.randomNumber(1, 100);
const player = ns.getPlayer(); const player = ns.getPlayer();
const cores = randomNumber(1, 16); const cores = utils.randomNumber(1, 16);
const testResult = ezgame.formulas.hacking.growPercent(server, threads, player, cores); const testResult = roundTo5Decimals(ezgame.formulas.hacking.growPercent(server, threads, player, cores));
const actualResult = ns.formulas.hacking.growPercent(server, threads, player, cores); const actualResult = roundTo5Decimals(ns.formulas.hacking.growPercent(server, threads, player, cores));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -73,14 +80,14 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.growThreads", name: "hacking.growThreads",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
// if moneyMax is not set, use a large number to ensure growThreads returns a valid number of threads // if moneyMax is not set, use a large number to ensure growThreads returns a valid number of threads
// anyways if it is above moneyMax, the function should clamp it to the max (hopefully) // anyways if it is above moneyMax, the function should clamp it to the max (hopefully)
const targetMoney = server.moneyMax ?? 1e120; const targetMoney = server.moneyMax ?? 1e120;
const cores = randomNumber(1, 16); const cores = utils.randomNumber(1, 16);
const testResult = ezgame.formulas.hacking.growThreads(server, player, targetMoney, cores); const testResult = roundTo5Decimals(ezgame.formulas.hacking.growThreads(server, player, targetMoney, cores));
const actualResult = ns.formulas.hacking.growThreads(server, player, targetMoney, cores); const actualResult = roundTo5Decimals(ns.formulas.hacking.growThreads(server, player, targetMoney, cores));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -89,10 +96,10 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.growTime", name: "hacking.growTime",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
const testResult = ezgame.formulas.hacking.growTime(server, player); const testResult = roundTo5Decimals(ezgame.formulas.hacking.growTime(server, player));
const actualResult = ns.formulas.hacking.growTime(server, player); const actualResult = roundTo5Decimals(ns.formulas.hacking.growTime(server, player));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -101,10 +108,10 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.hackChance", name: "hacking.hackChance",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
const testResult = ezgame.formulas.hacking.hackChance(server, player); const testResult = roundTo5Decimals(ezgame.formulas.hacking.hackChance(server, player));
const actualResult = ns.formulas.hacking.hackChance(server, player); const actualResult = roundTo5Decimals(ns.formulas.hacking.hackChance(server, player));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -113,10 +120,10 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.hackExp", name: "hacking.hackExp",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
const testResult = ezgame.formulas.hacking.hackExp(server, player); const testResult = roundTo5Decimals(ezgame.formulas.hacking.hackExp(server, player));
const actualResult = ns.formulas.hacking.hackExp(server, player); const actualResult = roundTo5Decimals(ns.formulas.hacking.hackExp(server, player));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -125,10 +132,10 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.hackPercent", name: "hacking.hackPercent",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
const testResult = ezgame.formulas.hacking.hackPercent(server, player); const testResult = roundTo5Decimals(ezgame.formulas.hacking.hackPercent(server, player));
const actualResult = ns.formulas.hacking.hackPercent(server, player); const actualResult = roundTo5Decimals(ns.formulas.hacking.hackPercent(server, player));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -137,10 +144,10 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.hackTime", name: "hacking.hackTime",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
const testResult = ezgame.formulas.hacking.hackTime(server, player); const testResult = roundTo5Decimals(ezgame.formulas.hacking.hackTime(server, player));
const actualResult = ns.formulas.hacking.hackTime(server, player); const actualResult = roundTo5Decimals(ns.formulas.hacking.hackTime(server, player));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }
@ -149,10 +156,10 @@ const hackingTests = (ns: NS, serverName: string): TestCase[] => [
{ {
name: "hacking.weakenTime", name: "hacking.weakenTime",
fn: (ns, serverName) => { fn: (ns, serverName) => {
const server = ns.getServer(serverName); const server = getNormalServer(ns, serverName);
const player = ns.getPlayer(); const player = ns.getPlayer();
const testResult = ezgame.formulas.hacking.weakenTime(server, player); const testResult = roundTo5Decimals(ezgame.formulas.hacking.weakenTime(server, player));
const actualResult = ns.formulas.hacking.weakenTime(server, player); const actualResult = roundTo5Decimals(ns.formulas.hacking.weakenTime(server, player));
if (testResult !== actualResult) { if (testResult !== actualResult) {
throw new Error(`expected ${actualResult}, got ${testResult}`); throw new Error(`expected ${actualResult}, got ${testResult}`);
} }

View file

@ -1,11 +1,9 @@
import { NS } from "@ns"; import { NS, Server } from "@ns";
import { ezgame } from "./ezgame"; import { ezgame } from "./ezgame";
import { scan } from "./utils/scan";
import { getNormalServer } from "./utils/get-normal-server";
export const main = (ns: NS) => { export const main = (ns: NS) => {
const server = ns.getServer("n00dles"); // console.log(ns.formulas.hacking.growTime(getNormalServer(ns, "n00dles"), ns.getPlayer()));
const player = ns.getPlayer(); console.log(ns.getServer("n00dles"));
const testResult = ezgame.formulas.hacking.growTime(server, player);
const actualResult = ns.formulas.hacking.growTime(server, player);
console.log(testResult);
console.log(actualResult);
}; };

View file

@ -11,8 +11,15 @@ interface ServerAnalysis {
export const analyze = (ns: NS, hostnames: string[]) => { export const analyze = (ns: NS, hostnames: string[]) => {
hostnames.forEach((hostname) => { hostnames.forEach((hostname) => {
// skip darknet servers
if (ns.dnet.isDarknetServer(hostname)) {
ns.tprint(`Skipping darknet server: ${hostname}`);
return;
}
// get the server object for the hostname // get the server object for the hostname
const server: Server = ns.getServer(hostname); const server = ns.getServer(hostname) as Server;
// create a new object that matches the ServerAnalysis interface // create a new object that matches the ServerAnalysis interface
const analysis: ServerAnalysis = { const analysis: ServerAnalysis = {
hostname: server.hostname, hostname: server.hostname,
@ -21,6 +28,8 @@ export const analyze = (ns: NS, hostnames: string[]) => {
portsRequiredForNuke: server.numOpenPortsRequired ?? 0, portsRequiredForNuke: server.numOpenPortsRequired ?? 0,
maxRam: server.maxRam, maxRam: server.maxRam,
}; };
ns.tprint(analysis);
}); });
}; };

View file

@ -0,0 +1,9 @@
import { NS, Server } from "@ns";
export function getNormalServer(ns: NS, hostname: string): Server {
if (ns.dnet.isDarknetServer(hostname)) {
throw new Error(`Server ${hostname} is a darknet server`);
}
return ns.getServer(hostname) as Server;
}

View file

@ -1,29 +1,23 @@
import { NS } from '@ns'; import { NS } from "@ns";
export const serverBuyMax = (ns: NS, serverNamePrefix: string) => { export const serverBuyMax = (ns: NS, serverNamePrefix: string) => {
// get the maximum number of servers that can be purchased // get the maximum number of servers that can be purchased
const maxServers = ns.getPurchasedServerLimit(); const maxServers = ns.cloud.getServerLimit();
// get the number of servers currently owned // get the number of servers currently owned
const currentServers = ns.getPurchasedServers().length; const currentServers = ns.cloud.getServerNames().length;
// calculate the number of servers that can still be purchased // calculate the number of servers that can still be purchased
const serversToPurchase = maxServers - currentServers; const serversToPurchase = maxServers - currentServers;
// if we can't buy any more servers, return // if we can't buy any more servers, return
if (serversToPurchase <= 0) { if (serversToPurchase <= 0) {
ns.tprint('Cannot purchase any more servers. Maximum limit reached.'); ns.tprint("Cannot purchase any more servers. Maximum limit reached.");
return; return;
} }
// get current money available // get current money available
const moneyAvailable = ns.getServerMoneyAvailable('home'); const moneyAvailable = ns.getServerMoneyAvailable("home");
// smallest ram we can buy is 2gb, check if can even afford that
if (moneyAvailable < ns.getPurchasedServerCost(2)) {
ns.tprint('Nigga you broke, not enough money to buy the smallest server');
return;
}
// find the maximum ram we can afford spread out over the number of servers we can buy // find the maximum ram we can afford spread out over the number of servers we can buy
// the ram amount that can be bought is in power of 2 // the ram amount that can be bought is in power of 2
@ -35,7 +29,7 @@ export const serverBuyMax = (ns: NS, serverNamePrefix: string) => {
// calculate the ram based on power // calculate the ram based on power
const ram = Math.pow(2, pow); const ram = Math.pow(2, pow);
// get the cost of the server with the ram // get the cost of the server with the ram
const totalCost = ns.getPurchasedServerCost(ram) * serversToPurchase; const totalCost = ns.cloud.getServerCost(ram) * serversToPurchase;
// check if can buy // check if can buy
if (totalCost <= moneyAvailable) { if (totalCost <= moneyAvailable) {
@ -47,13 +41,19 @@ export const serverBuyMax = (ns: NS, serverNamePrefix: string) => {
} }
} }
// check if we found a valid target ram amount
if (targetRam < 2) {
ns.tprint("Unable to find affordable server configuration. Try buying fewer servers.");
return;
}
// now buy the servers with the target ram // now buy the servers with the target ram
let serversPurchased = 0; let serversPurchased = 0;
while (serversPurchased < serversToPurchase) { while (serversPurchased < serversToPurchase) {
const serverName = `${serverNamePrefix}-${currentServers + serversPurchased + 1}`; const serverName = `${serverNamePrefix}-${currentServers + serversPurchased + 1}`;
if (!ns.serverExists(serverName)) { if (!ns.serverExists(serverName)) {
ns.purchaseServer(serverName, targetRam); ns.cloud.purchaseServer(serverName, targetRam);
serversPurchased++; serversPurchased++;
} }
} }
@ -63,6 +63,6 @@ export const serverBuyMax = (ns: NS, serverNamePrefix: string) => {
}; };
export const main = (ns: NS) => { export const main = (ns: NS) => {
const serverNamePrefix = 'worker'; const serverNamePrefix = "worker";
serverBuyMax(ns, serverNamePrefix); serverBuyMax(ns, serverNamePrefix);
}; };

View file

@ -1,9 +1,9 @@
import { NS } from '@ns'; import { NS } from "@ns";
import { kill } from '../kill'; import { kill } from "../kill";
export const serverDeleteAll = (ns: NS) => { export const serverDeleteAll = (ns: NS) => {
// get a list of all the purchased servers // get a list of all the purchased servers
const serverNames: string[] = ns.getPurchasedServers(); const serverNames: string[] = ns.cloud.getServerNames();
// initialize a counter for total cost of all the servers // initialize a counter for total cost of all the servers
let totalCost = 0; let totalCost = 0;
@ -14,14 +14,14 @@ export const serverDeleteAll = (ns: NS) => {
const ram = ns.getServerMaxRam(serverName); const ram = ns.getServerMaxRam(serverName);
// calculate the cost of the server and add it to the total cost // calculate the cost of the server and add it to the total cost
const cost = ns.getPurchasedServerCost(ram); const cost = ns.cloud.getServerCost(ram);
totalCost += cost; totalCost += cost;
// kill all the processes running on the server before removing it // kill all the processes running on the server before removing it
kill(ns, serverName); kill(ns, serverName);
// remove the server // remove the server
ns.deleteServer(serverName); ns.cloud.deleteServer(serverName);
}); });
ns.tprint(`Purchased servers deleted: ${serverNames.length}`); ns.tprint(`Purchased servers deleted: ${serverNames.length}`);

View file

@ -1,10 +1,25 @@
import { Server } from "@ns";
export const utils = {
utilityFormatDate,
utilityFormatSize,
utilityUnformatSize,
utilityExtractFilename,
clampNumber,
clampInteger,
isValidNumber,
randomNumber,
randomInteger,
isDarknetServer,
};
/** /**
* Format the date/time. * Format the date/time.
* If the date is today, show the time. Otherwise, show the date in "day month year" format. * If the date is today, show the time. Otherwise, show the date in "day month year" format.
* @param dateString The date string to format * @param dateString The date string to format
* @returns Formatted date/time string * @returns Formatted date/time string
*/ */
export const utilityFormatDate = (dateString: string): string => { function utilityFormatDate(dateString: string): string {
const date = new Date(dateString); const date = new Date(dateString);
const today = new Date(); const today = new Date();
@ -22,14 +37,14 @@ export const utilityFormatDate = (dateString: string): string => {
// "day month year" format // "day month year" format
return date.toLocaleDateString(undefined, { day: "numeric", month: "long", year: "numeric" }); return date.toLocaleDateString(undefined, { day: "numeric", month: "long", year: "numeric" });
} }
}; }
/** /**
* Format bytes into human-readable size * Format bytes into human-readable size
* @param bytes Number of bytes to format * @param bytes Number of bytes to format
* @returns Formatted size string * @returns Formatted size string
*/ */
export const utilityFormatSize = (bytes: number): string => { function utilityFormatSize(bytes: number): string {
if (bytes === 0) return "0 Bytes"; if (bytes === 0) return "0 Bytes";
const k = 1024; const k = 1024;
@ -38,19 +53,19 @@ export const utilityFormatSize = (bytes: number): string => {
// tf is pow // tf is pow
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}; }
/** /**
* Convert a formatted size string back to bytes * Convert a formatted size string back to bytes
* @param formattedSize Formatted size string (must be in the same format produced by `utilityFormatSize()`) * @param formattedSize Formatted size string (must be in the same format produced by `utilityFormatSize()`)
* @returns Number of bytes * @returns Number of bytes
*/ */
export const utilityUnformatSize = (formattedSize: string): number => { function 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(" ");
const index = sizes.indexOf(unit); const index = sizes.indexOf(unit);
return parseFloat(value) * Math.pow(1024, index); return parseFloat(value) * Math.pow(1024, index);
}; }
/** /**
* Extract filename stem and extension from a file path * Extract filename stem and extension from a file path
@ -58,7 +73,7 @@ export const utilityUnformatSize = (formattedSize: string): number => {
* @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). * @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 * @throws Error if the file path is invalid
*/ */
export const utilityExtractFilename = (filePath: string): { fileStem: string; fileExtension: string } | null => { function utilityExtractFilename(filePath: string): { fileStem: string; fileExtension: string } | null {
const lastSlashIndex = filePath.lastIndexOf("/"); const lastSlashIndex = filePath.lastIndexOf("/");
const lastDotIndex = filePath.lastIndexOf("."); const lastDotIndex = filePath.lastIndexOf(".");
@ -86,7 +101,7 @@ export const utilityExtractFilename = (filePath: string): { fileStem: string; fi
} }
return { fileStem: fileStem, fileExtension: fileExtension }; return { fileStem: fileStem, fileExtension: fileExtension };
}; }
/** /**
* Clamps a value to a lower and upper bound * Clamps a value to a lower and upper bound
@ -95,7 +110,7 @@ export const utilityExtractFilename = (filePath: string): { fileStem: string; fi
* @param max Upper bound, defaults to `Number.MAX_VALUE` * @param max Upper bound, defaults to `Number.MAX_VALUE`
* @returns Clamped value * @returns Clamped value
*/ */
export function clampNumber(value: number, min: number = -Number.MAX_VALUE, max: number = Number.MAX_VALUE): number { function clampNumber(value: number, min: number = -Number.MAX_VALUE, max: number = Number.MAX_VALUE): number {
if (isNaN(value)) { if (isNaN(value)) {
// if (CONSTANTS.isDevBranch) throw new Error("NaN passed into clampNumber()"); // if (CONSTANTS.isDevBranch) throw new Error("NaN passed into clampNumber()");
return min; return min;
@ -110,7 +125,7 @@ export function clampNumber(value: number, min: number = -Number.MAX_VALUE, max:
* @param max Upper bound, defaults to `Number.MAX_SAFE_INTEGER` * @param max Upper bound, defaults to `Number.MAX_SAFE_INTEGER`
* @returns Clamped integer value * @returns Clamped integer value
*/ */
export function clampInteger( function clampInteger(
value: number, value: number,
min: number = -Number.MAX_SAFE_INTEGER, min: number = -Number.MAX_SAFE_INTEGER,
max: number = Number.MAX_SAFE_INTEGER, max: number = Number.MAX_SAFE_INTEGER,
@ -128,7 +143,7 @@ export function clampInteger(
* @param n The number to check * @param n The number to check
* @returns 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 { function isValidNumber(n: number): boolean {
return typeof n === "number" && !isNaN(n); return typeof n === "number" && !isNaN(n);
} }
@ -138,7 +153,7 @@ export function isValidNumber(n: number): boolean {
* @param max Maximum value (inclusive) * @param max Maximum value (inclusive)
* @returns Random number between min and max * @returns Random number between min and max
*/ */
export function randomNumber(min: number, max: number): number { function randomNumber(min: number, max: number): number {
if (min > max) throw new Error("min cannot be greater than max"); if (min > max) throw new Error("min cannot be greater than max");
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
@ -149,6 +164,10 @@ export function randomNumber(min: number, max: number): number {
* @param max Maximum value (inclusive) * @param max Maximum value (inclusive)
* @returns Random integer between min and max * @returns Random integer between min and max
*/ */
export function randomInteger(min: number, max: number): number { function randomInteger(min: number, max: number): number {
return Math.floor(randomNumber(min, max + 1)); return Math.floor(randomNumber(min, max + 1));
} }
function isDarknetServer(server: Server): server is Server & { isStationary: boolean } {
return "isStationary" in server;
}