diff --git a/package.json b/package.json index dc8b71b..9b8488a 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "build": "node build.js", + "check": "tsc --noEmit", "defs": "node ./updateDefs.js" }, "repository": { diff --git a/src/batch-cal.ts b/src/batch-cal.ts new file mode 100644 index 0000000..9258010 --- /dev/null +++ b/src/batch-cal.ts @@ -0,0 +1,49 @@ +import { NS, AutocompleteData } from '@ns' +import { parse } from './lib/flag'; +import { sprintf } from './lib/printf'; +import {calculateBatchResource} from './lib/batchbase'; + +// eslint-disable-next-line require-await +export async function main(ns: NS) : Promise { + const flag = parse(ns.args.map(x=>x.toString())); + if(flag.h || flag.help){ + const msg = ['run cmd [startThread] [--hostname || -h] [--graph]'] + msg.forEach(x=>ns.tprint(x)); + return; + } + if(flag._.length === 0){ + ns.tprint("Error. Require Argument"); + return; + } + if(!(flag.host)){ + ns.tprint("host required"); + return; + } + const v = parseInt(flag._[0].toString()); + const hostname = flag.host ?? flag.t; + const server = ns.getServer(hostname) + if(flag.graph){ + const col = []; + for (let i = 1; i < v; i++) { + const info = calculateBatchResource(ns,hostname,i,server.moneyMax,1); + col.push({ + costEffect :(info.earnMoneyPerSec / info.totalThreadCount), + index: i, + total: info.totalThreadCount + }) + } + col.forEach(({costEffect,index,total})=>{ + ns.tprint(sprintf("%3d %10.2f %4d",index,costEffect,total)); + }) + ns.tprint(col.map(x=>x.costEffect)) + return; + } + const info = calculateBatchResource(ns,hostname,v,server.moneyMax,1); + ns.tprint(JSON.stringify(info,undefined,2)); + return; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function autocomplete(data : AutocompleteData, args : string[]) : string[] { + return [...data.servers] +} \ No newline at end of file diff --git a/src/deploy-share.ts b/src/deploy-share.ts new file mode 100644 index 0000000..588101d --- /dev/null +++ b/src/deploy-share.ts @@ -0,0 +1,45 @@ +import { AutocompleteData, NS } from "@ns"; +import {parse} from "./lib/flag"; +import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, execWeakenLoop, getRamOfScript } from './lib/batchbase'; + +/** @param {NS} ns */ +export async function main(ns: NS): Promise { + const flag = parse(ns.args.map(String)); + if(flag._.length < 1){ + ns.tprint("argument required"); + ns.tprint("run cmd [src(hostname)]"); + ns.exit(); + } + const hostname = String(flag._[0]); + + const th = flag.thread || false; + + ns.scp("share-server.js",hostname); + + let nThread; + if(th){ + nThread = th; + } + else { + nThread = calculateMaximumThread(ns, hostname, getRamOfScript(ns,"weakenLoop")); + } + const pid = ns.exec("share-server.js",hostname, nThread); + ns.tprint(pid," Process started."); +} + +/** + * @param {NS} ns + * @param {string} dst + * @param {number} amount the program ram + */ +export function calculateMaximumThread(ns:NS,host:string,amount:number):number{ + const maxRam = ns.getServerMaxRam(host); + const usedRam = ns.getServerUsedRam(host); + const restRam = maxRam - usedRam; + return Math.floor(restRam / amount); +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function autocomplete(data: AutocompleteData,args: string[]): string[]{ + return [...data.servers]; +} \ No newline at end of file diff --git a/src/deploy-weaken.ts b/src/deploy-weaken.ts new file mode 100644 index 0000000..3098128 --- /dev/null +++ b/src/deploy-weaken.ts @@ -0,0 +1,57 @@ +import { AutocompleteData, NS } from "@ns"; +import {parse} from "./lib/flag"; +import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, execWeakenLoop, getRamOfScript } from './lib/batchbase'; + +/** @param {NS} ns */ +export async function main(ns: NS): Promise { + const flag = parse(ns.args.map(String)); + if(flag._.length < 2){ + ns.tprint("argument required"); + ns.tprint("run cmd [src(hostname)] [dest(hostname)]"); + ns.exit(); + } + const src = String(flag._[0]); + const dest = String(flag._[1]); + + const th = flag.thread || false; + + if(!isBatchFilePackInstalled(ns)){ + await installBatchFilePack(ns); + } + + await scpBatchFilePack(ns,src); + + const stock = flag.stock ?? false; + + let nThread; + if(th){ + nThread = th; + } + else { + nThread = calculateMaximumThread(ns, src, getRamOfScript(ns,"weakenLoop")); + } + const pid = execWeakenLoop(ns,{ + hostname: src, + target: dest, + numThread: nThread, + stock: stock, + }); + ns.tprint(pid," Process started."); +} + +/** + * @param {NS} ns + * @param {string} dst + * @param {number} amount the program ram + */ +export function calculateMaximumThread(ns:NS,host:string,amount:number):number{ + const maxRam = ns.getServerMaxRam(host); + const usedRam = ns.getServerUsedRam(host); + const restRam = maxRam - usedRam; + return Math.floor(restRam / amount); +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function autocomplete(data: AutocompleteData,args: string[]): string[]{ + return [...data.servers]; +} \ No newline at end of file diff --git a/src/get-cylce-size.ts b/src/get-cylce-size.ts index c2cf974..7ec1162 100644 --- a/src/get-cylce-size.ts +++ b/src/get-cylce-size.ts @@ -2,16 +2,17 @@ import { NS } from '@ns' export async function main(ns : NS) : Promise { const stat: number[] = []; + const M = 10; for (let i = 0; i < 100; i++) { const start = performance.now(); - await ns.sleep(0); + await ns.sleep(M); const end = performance.now(); stat.push(end- start); } const avg = stat.reduce((x,y)=>x+y)/stat.length; const max = Math.max(...stat); const min = Math.min(...stat); - ns.tprint(`avg : ${avg}`); - ns.tprint(`max : ${max}`); - ns.tprint(`min : ${min}`); + ns.tprint(`avg : ${avg - M}`); + ns.tprint(`max : ${max - M}`); + ns.tprint(`min : ${min - M}`); } \ No newline at end of file diff --git a/src/lib/batchbase.ts b/src/lib/batchbase.ts index 7af3bf2..a317de6 100644 --- a/src/lib/batchbase.ts +++ b/src/lib/batchbase.ts @@ -1,11 +1,17 @@ -import { NS } from "@ns" +import { NS, Server } from "@ns" +import { calculateServerGrowth, FormulaHelper } from "./formula"; -type FilePackEntry = { + +export type BatchType = "hack" | "grow" | "weaken" | "weakenLoop"; + +type BatchFilePackEntry = { + type: BatchType; path: string; content: string; + ram: number; }; -type FilePack = FilePackEntry[]; +type FilePack = BatchFilePackEntry[]; const createScriptContent: (f: string) => string = (funcName: string) => `export async function main(ns) { if(ns.args.length < 3){ @@ -17,13 +23,15 @@ const createScriptContent: (f: string) => string = (funcName: string) => `export const sleepDuration = parseInt(ns.args[0]); const hostname = ns.args[1].toString(); const stock = ns.args[2] == "true"; - await ns.sleep(sleepDuration); + if(sleepDuration > 0){ + await ns.sleep(sleepDuration); + } await ns.${funcName}(hostname,{ stock, }); }` -async function unpackFileEntry(ns: NS, filePack: FilePackEntry): Promise { +async function unpackFileEntry(ns: NS, filePack: BatchFilePackEntry): Promise { await ns.write(filePack.path, filePack.content, "w"); } @@ -35,23 +43,50 @@ export async function unpackFilePack(ns: NS, pack: FilePack): Promise { export const batchFilePack: FilePack = [ { + type: "hack", path: "/tmp/hack.js", - content: createScriptContent("hack") + content: createScriptContent("hack"), + ram:1.7, }, { + type: "weaken", path: "/tmp/weaken.js", - content: createScriptContent("weaken") + content: createScriptContent("weaken"), + ram: 1.75, }, { + type: "grow", path: "/tmp/grow.js", - content: createScriptContent("grow") + content: createScriptContent("grow"), + ram: 1.75, + }, + { + type:"weakenLoop", + path: "/tmp/weakenLoop.js", + ram: 1.75, + content: `export async function main(ns) { + if(ns.args.length < 3){ + ns.print("[ERROR] args required"); + ns.print("run cmd [sleepDuration] [hostname] [effectStock]") + ns.exit(); + return; + } + const sleepDuration = parseInt(ns.args[0]); + const hostname = ns.args[1].toString(); + const stock = ns.args[2] == "true"; + for(;;){ + await ns.weaken(hostname,{ + stock, + }); + } +}`, } ] -export async function scpBatchFilePack(ns: NS,hostname :string): Promise { - const list = batchFilePack.map(entry=>entry.path); +export async function scpBatchFilePack(ns: NS, hostname: string): Promise { + const list = batchFilePack.map(entry => entry.path); await installBatchFilePack(ns); - await ns.scp(list,hostname); + await ns.scp(list, hostname); } export async function installBatchFilePack(ns: NS): Promise { @@ -62,8 +97,8 @@ export function isBatchFilePackInstalled(ns: NS, hostname?: string): boolean { return batchFilePack.every(entry => ns.fileExists(entry.path, hostname)) } -export interface ExecOption{ - hostname: string; +export interface ExecOption { + hostname: string; /** * @default 1 */ @@ -75,21 +110,191 @@ export interface ExecOption{ /** * target hostname to operate "hack", "weaken" or "grow" */ - target: number; + target: string; stock?: boolean; } -function execBatchfile(ns: NS, path: string, option:ExecOption): number { +function execBatchfile(ns: NS, path: string, option: ExecOption): number { return ns.exec(path, option.hostname, option.numThread, option.sleepDuration ?? 0, option.target, option.stock ?? false); } -export function execHack(ns: NS,option: ExecOption): number{ - return execBatchfile(ns,"/tmp/hack.js",option); +export function execHack(ns: NS, option: ExecOption): number { + return execBatchfile(ns, "/tmp/hack.js", option); } -export function execGrow(ns: NS,option: ExecOption): number{ - return execBatchfile(ns,"/tmp/grow.js",option); +export function execGrow(ns: NS, option: ExecOption): number { + return execBatchfile(ns, "/tmp/grow.js", option); } -export function execWeaken(ns: NS,option: ExecOption): number{ - return execBatchfile(ns,"/tmp/weaken.js",option); -} \ No newline at end of file +export function execWeaken(ns: NS, option: ExecOption): number { + return execBatchfile(ns, "/tmp/weaken.js", option); +} +export function execWeakenLoop(ns: NS, option: ExecOption): number { + return execBatchfile(ns, "/tmp/weakenLoop.js", option); +} +export function getRamOfScript(ns: NS, type: BatchType): number{ + return batchFilePack.filter(x=>x.type == type)[0].ram; +} + +//type BatchType = "hack" | "grow" | "weaken"; +// +//type Batch = { +// type: BatchType; +// options: ExecOption; +//} +type ReserveRequest = { + hostname: string; + usableRam: number; +} + +function getKeyOfHWGWBatchManager(hostname: string): string{ + return `HWGW-${hostname}`; +} +interface HWGW { + HThreadCount: number; + HWThreadCount: number; + GThreadCount: number; + GWThreadCount: number; +} + +type WorkReserved = { + allocated:false; +} | { + allocated:true; + hwgw: HWGW; +}; + +type Resource = { + hostname: string; + usableRam: string; +}; + +class HWGWBatchManager { + willExecuted: WorkReserved[]; + server: Server; + ns: NS; + createdAt: number; + + + constructor(ns: NS, hostname: string) { + this.ns = ns; + this.willExecuted = []; + this.server = ns.getServer(hostname); + this.createdAt = ns.getTimeSinceLastAug(); + + this.growArray(); + } + private popRequest(): ReserveRequest[]{ + const key = getKeyOfHWGWBatchManager(this.server.hostname); + const text = localStorage.getItem(key) + if(text === null){ + return []; + } + localStorage.setItem(key,""); + return JSON.parse(text) as ReserveRequest[]; + } + private growArray(n: number): void { + while (this.willExecuted.length <= n) { + this.willExecuted.push({ + allocated:false + }); + } + } + + reserveBatch(time: number): void { + const milli = time - this.ns.getTimeSinceLastAug(); + const sec = Math.floor(milli / 1000); + this.growArray(sec); + this.willExecuted[sec] = true; + } + async tick(): Promise { + const data = this.willExecuted.shift(); + await this.sleepCorrect(); + } + /** + * exact time Sleep + */ + async sleepCorrect(): Promise { + const cur = this.ns.getTimeSinceLastAug(); + const sliceTime = 250; + const delta = (cur - this.createdAt + sliceTime/2) % sliceTime - sliceTime/2; + await this.ns.sleep(sliceTime - delta); + } +} + + +interface HackCalculation extends HWGW { + earnMoneyPerSec: number; + earnExpPerSec: number; + /** + * cycle time in seconds. + */ + cycleTime: number; + totalThreadCount: number; +} + +export function calculateBatchResource(ns: NS, hostname: string, hackThread: number, startMoney?: number, cores = 1): HackCalculation { + const helper = new FormulaHelper(ns); + const server = ns.getServer(hostname); + startMoney ??= server.moneyMax; + server.hackDifficulty = server.minDifficulty; + + const percentMoneyHacked = helper.calculatePercentMoneyHacked(server); + const hackingChance = helper.calculateHackingChance(server); + const earnMoneyPerCycle = hackThread * percentMoneyHacked * startMoney * hackingChance; + const earnExpPerCycle = helper.calculateHackingExpGain(server) * hackThread * hackingChance; + const cycleTime = 1 + helper.calculateWeakenTime(server); + + const increasedSecurityAfterHack = helper.calculateHackSecurity(hackThread) + server.minDifficulty; + const nWeakenHackThreads = Math.ceil(helper.calculateWeakenThreadCountToTargetSecurity( + server.minDifficulty, + increasedSecurityAfterHack, cores)); + + const nGrowThreads = Math.ceil(helper.calculateGrowThreadCount(hostname, startMoney - earnMoneyPerCycle, startMoney, cores)); + const increasedSecurityAfterGrow = helper.calculuateGrowthSecurity(nGrowThreads, cores) + server.minDifficulty; + + const nWeakenGrowThreads = Math.ceil(helper.calculateWeakenThreadCountToTargetSecurity( + server.minDifficulty, + increasedSecurityAfterGrow, cores)); + + return { + earnMoneyPerSec: earnMoneyPerCycle / cycleTime, + earnExpPerSec: earnExpPerCycle / cycleTime, + cycleTime, + totalThreadCount: hackThread + nWeakenHackThreads + nGrowThreads + nWeakenGrowThreads, + HThreadCount: hackThread, + HWThreadCount: nWeakenHackThreads, + GThreadCount: nGrowThreads, + GWThreadCount: nWeakenGrowThreads + } +} + +function calculateOptimal(ns: NS, target: string, req: ReserveRequest): HWGW { + + return { + GThreadCount: 0, + HThreadCount: 0, + GWThreadCount: 0, + HWThreadCount: 0, + } +} + +export function requestHWGW(req: ReserveRequest, target: string): void { + const key = getKeyOfHWGWBatchManager(target); + const data = localStorage.getItem(key); + let requests: ReserveRequest[]; + if (data) { + requests = JSON.parse(data); + } + else { + requests = []; + } + requests.push(req); + localStorage.setItem(key, JSON.stringify(requests)); +} + +//export async function batchDaemon(ns: NS): Promise { +// const mgr = new HWGWBatchManager(ns, BATCH_KEY); +// for (; ;) { +// await mgr.tick(); +// } +//} \ No newline at end of file diff --git a/src/lib/formula.ts b/src/lib/formula.ts index c0808b4..22d2511 100644 --- a/src/lib/formula.ts +++ b/src/lib/formula.ts @@ -1,4 +1,4 @@ -import { Server, Player } from "@ns"; +import { NS, Server, Player, BitNodeMultipliers } from "@ns"; const CONSTANTS = { ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate @@ -7,8 +7,13 @@ const CONSTANTS = { ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened } -const BitNodeMultipliers = { +const DefaultBitNodeMultipliers: BitNodeMultipliers = { HackingLevelMultiplier: 1, + StrengthLevelMultiplier: 1, + DefenseLevelMultiplier: 1, + DexterityLevelMultiplier: 1, + AgilityLevelMultiplier: 1, + CharismaLevelMultiplier: 1, ServerGrowthRate: 1, ServerMaxMoney: 1, @@ -16,16 +21,62 @@ const BitNodeMultipliers = { ServerStartingSecurity: 1, ServerWeakenRate: 1, + HomeComputerRamCost: 1, + + PurchasedServerCost: 1, + PurchasedServerSoftcap: 1, + PurchasedServerLimit: 1, + PurchasedServerMaxRam: 1, + + CompanyWorkMoney: 1, + CrimeMoney: 1, + HacknetNodeMoney: 1, + ManualHackMoney: 1, + ScriptHackMoney: 1, + ScriptHackMoneyGain: 1, + CodingContractMoney: 1, + + ClassGymExpGain: 1, + CompanyWorkExpGain: 1, + CrimeExpGain: 1, + FactionWorkExpGain: 1, HackExpGain: 1, - ScriptHackMoney: 1, -} + FactionPassiveRepGain: 1, + FactionWorkRepGain: 1, + RepToDonateToFaction: 1, + + AugmentationMoneyCost: 1, + AugmentationRepCost: 1, + + InfiltrationMoney: 1, + InfiltrationRep: 1, + + FourSigmaMarketDataCost: 1, + FourSigmaMarketDataApiCost: 1, + + CorporationValuation: 1, + CorporationSoftcap: 1, + + BladeburnerRank: 1, + BladeburnerSkillCost: 1, + + GangSoftcap: 1, + //GangUniqueAugs: 1, + + DaedalusAugsRequirement: 30, + + StaneksGiftPowerMultiplier: 1, + StaneksGiftExtraSize: 0, + + WorldDaemonDifficulty: 1, +}; export function calculateIntelligenceBonus(intelligence: number, weight = 1): number { return 1 + (weight * Math.pow(intelligence, 0.8)) / 600; - } +} -export function calculateServerGrowth(server: Server, threads: number, p: Player, cores = 1): number { +export function calculateServerGrowth(server: Server, threads: number, p: Player, cores = 1, bit: BitNodeMultipliers = DefaultBitNodeMultipliers): number { const numServerGrowthCycles = Math.max(Math.floor(threads), 0); //Get adjusted growth rate, which accounts for server security @@ -38,7 +89,7 @@ export function calculateServerGrowth(server: Server, threads: number, p: Player //Calculate adjusted server growth rate based on parameters const serverGrowthPercentage = server.serverGrowth / 100; const numServerGrowthCyclesAdjusted = - numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate; + numServerGrowthCycles * serverGrowthPercentage * bit.ServerGrowthRate; //Apply serverGrowth for the calculated number of growth cycles const coreBonus = 1 + (cores - 1) / 16; @@ -72,7 +123,7 @@ export function calculateHackingChance(server: Server, player: Player): number { * Returns the amount of hacking experience the player will gain upon * successfully hacking a server */ -export function calculateHackingExpGain(server: Server, player: Player): number { +export function calculateHackingExpGain(server: Server, player: Player, bit: BitNodeMultipliers = DefaultBitNodeMultipliers): number { const baseExpGain = 3; const diffFactor = 0.3; if (server.baseDifficulty == null) { @@ -81,21 +132,21 @@ export function calculateHackingExpGain(server: Server, player: Player): number let expGain = baseExpGain; expGain += server.baseDifficulty * diffFactor; - return expGain * player.mults.hacking_exp * BitNodeMultipliers.HackExpGain; + return expGain * player.mults.hacking_exp * bit.HackExpGain; } /** * 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: Server, player: Player): number { +export function calculatePercentMoneyHacked(server: Server, player: Player, bit: BitNodeMultipliers = DefaultBitNodeMultipliers): number { // Adjust if needed for balancing. This is the divisor for the final calculation const balanceFactor = 240; const difficultyMult = (100 - server.hackDifficulty) / 100; const skillMult = (player.skills.hacking - (server.requiredHackingSkill - 1)) / player.skills.hacking; const percentMoneyHacked = - (difficultyMult * skillMult * player.mults.hacking_money * BitNodeMultipliers.ScriptHackMoney) / balanceFactor; + (difficultyMult * skillMult * player.mults.hacking_money * bit.ScriptHackMoney) / balanceFactor; if (percentMoneyHacked < 0) { return 0; } @@ -143,4 +194,95 @@ export function calculateWeakenTime(server: Server, player: Player): number { const weakenTimeMultiplier = 4; // Relative to hacking time return weakenTimeMultiplier * calculateHackingTime(server, player); +} + +export function canBeAccessBitNode(ns: NS): boolean { + try { + ns.getBitNodeMultipliers(); + return true; + } catch (error) { + return false; + } +} + +export function getBitNodeMultipliersSafe(ns: NS): BitNodeMultipliers { + try { + return ns.getBitNodeMultipliers(); + } catch (error) { + if (typeof error === "string") { + return DefaultBitNodeMultipliers; + } + throw error; + } +} + +export class FormulaHelper { + ns: NS; + constructor(ns: NS) { + this.ns = ns; + } + calculateWeakenThreadCountToTargetSecurity(targetSecurity: number, curSecurity: number, cores = 1): number { + const k = targetSecurity - targetSecurity; + const c = this.ns.weakenAnalyze(1, cores); + return k / c; + } + calculateGrowThreadCount(hostname: string, currentMoney: number, targetMoney: number, cores = 1): number { + const g = this.ns.growthAnalyze(hostname, targetMoney / currentMoney, cores); + return g; + } + calculateHackSecurity(nThread: number): number { + const ServerFortifyAmount = 0.002; + return ServerFortifyAmount * nThread; + } + calculuateGrowthSecurity(nThread: number, cores = 1): number { + const ServerWeakenAmount = 0.004; + const mul = getBitNodeMultipliersSafe(this.ns); + const coreBonus = 1 + (cores - 1) / 16; + return ServerWeakenAmount * nThread * coreBonus * mul.ServerWeakenRate; + } + /** + * Returns time it takes to complete a weaken operation on a server, in seconds + * @param server + * @returns seconds to complete a weaken operation + */ + calculateWeakenTime(server: Server): number { + const player = this.ns.getPlayer(); + return calculateWeakenTime(server, player); + } + calculateGrowTime(server: Server): number { + const player = this.ns.getPlayer(); + return calculateGrowTime(server, player); + } + calculateHackingTime(server: Server): number { + const player = this.ns.getPlayer(); + return calculateHackingTime(server, player); + } + /** + * Hack + * @param server server to get + * @returns percent of earned money (0~1 value) + * @example + * ```ts + * calculatePercentMoneyHacked(server) //0.2 mean 20% + * ``` + */ + calculatePercentMoneyHacked(server: Server): number { + const player = this.ns.getPlayer(); + const bit = getBitNodeMultipliersSafe(this.ns); + return calculatePercentMoneyHacked(server, player, bit); + } + calculateServerGrowth(server: Server, thread: number, cores = 1): number { + const player = this.ns.getPlayer(); + const bit = getBitNodeMultipliersSafe(this.ns); + return calculateServerGrowth(server, thread, player, cores, bit); + } + calculateHackingChance(server: Server): number { + const player = this.ns.getPlayer(); + return calculateHackingChance(server, player); + } + calculateHackingExpGain(server: Server): number{ + const player = this.ns.getPlayer(); + const bit = getBitNodeMultipliersSafe(this.ns); + return calculateHackingExpGain(server,player,bit); + } } \ No newline at end of file diff --git a/src/lib/scheduler.ts b/src/lib/scheduler.ts new file mode 100644 index 0000000..852a379 --- /dev/null +++ b/src/lib/scheduler.ts @@ -0,0 +1,38 @@ +import {NS } from "@ns"; + + +type WorkFunc = ()=>void; +type WorkChunk = { + onStart: WorkFunc[]; + onFinish: WorkFunc[]; +} + +interface Tickable { + tick(): void; +} + +class PidTerminateExecutor{ + ns: NS; + lastCheck: number; + pidSet: Map; + tick(): void{ + const current = this.ns.getTimeSinceLastAug(); + const deadScripts = this.ns.getRecentScripts().filter(x=>x.timeOfDeath >= current); + deadScripts.forEach(x=>{ + if(this.pidSet.has(x.pid)){ + const target = this.pidSet.get(x.pid); + target?.dispatchEvent(new ) + } + }); + } +} + +class TimerExecutor{ + ns: NS; + constructor(ns: NS){ + this.ns = ns; + } + asdf():{ + + } +} diff --git a/src/list-all-server.ts b/src/list-all-server.ts index ec98f19..e689f0a 100644 --- a/src/list-all-server.ts +++ b/src/list-all-server.ts @@ -39,10 +39,11 @@ function findServer(ns: NS, startServer: string, targetServer: string, depth: nu if(detail){ const moneyAvailable = ns.nFormat(info.moneyAvailable,"$0.000a"); const moneyMax = ns.nFormat(info.moneyMax,"$0.000a"); - ret.push(sprintf("%s └%s🛡️ %6.2f/%6.2f(%3d),💸 %10s/%10s",extendSymbol, + ret.push(sprintf("%s └%s🛡️ %6.2f/%6.2f(%3d),💸 %10s/%10s 💾%d/%d",extendSymbol, "-".repeat(20-depth), info.hackDifficulty,info.minDifficulty,info.requiredHackingSkill, - moneyAvailable,moneyMax)); + moneyAvailable,moneyMax, + info.maxRam, info.ramUsed)); } if (lock !== "impossible") { const s = findServer(ns, targetServer, server, depth + 1).map(x=>`${extendSymbol}${x}`); diff --git a/src/lsnode.ts b/src/lsnode.ts index 72144b2..97422bf 100644 --- a/src/lsnode.ts +++ b/src/lsnode.ts @@ -2,14 +2,18 @@ import { selectRootedServerList, ServerInfo } from "lib/servers"; import { calculateWeakenTime, calculatePercentMoneyHacked, + calculateHackingExpGain, calculateServerGrowth } from "lib/formula"; import { NS } from '@ns' import { sprintf } from "./lib/printf"; +import { parse } from "./lib/flag"; // eslint-disable-next-line require-await export async function main(ns: NS): Promise { + const flag = parse(ns.args.map(x=>x.toString())) + const list = selectRootedServerList(ns); const player = ns.getPlayer(); const m = list.map(x => { @@ -18,6 +22,7 @@ export async function main(ns: NS): Promise { } as ServerInfo; ns.print(optimalState.minDifficulty," ", optimalState.hackDifficulty ,""); const weakenTime = calculateWeakenTime(optimalState, player); + const expGain = calculateHackingExpGain(optimalState,player); const earnMoney = calculatePercentMoneyHacked(optimalState, player); //const growPercent = calculateServerGrowth() return { @@ -25,15 +30,29 @@ export async function main(ns: NS): Promise { info: x, weakenTime, earnMoney, - ce: earnMoney* x.moneyMax / weakenTime, + ce: earnMoney* x.moneyMax/weakenTime, + expCe: expGain / weakenTime, + expGain, } }); - m.sort((a,b)=>(b.ce-a.ce)); - - m.filter(x=>x.ce > 0).forEach(x=>{ - const msg = sprintf("%20s %8s %6.1fs %10.2f",x.hostname, - ns.nFormat(x.earnMoney * x.info.moneyMax,"$0.00a"),x.weakenTime, - x.ce*100); - ns.tprint(msg); - }) + if(flag.exp){ + m.sort((a,b)=>(b.expCe-a.expCe)); + + m.filter(x=>x.expCe > 0).forEach(x=>{ + const msg = sprintf("%20s %8s %6.1fs %8s",x.hostname, + ns.nFormat(x.expGain,"0.00a"),x.weakenTime, + ns.nFormat(x.expCe,"0.00a")); + ns.tprint(msg); + }) + } + else { + m.sort((a,b)=>(b.ce-a.ce)); + + m.filter(x=>x.ce > 0).forEach(x=>{ + const msg = sprintf("%20s %8s %6.1fs %10s",x.hostname, + ns.nFormat(x.earnMoney * x.info.moneyMax,"$0.00a"),x.weakenTime, + ns.nFormat(x.ce,"$0.000a")); + ns.tprint(msg); + }) + } } \ No newline at end of file diff --git a/src/repl.ts b/src/repl.ts new file mode 100644 index 0000000..16ae674 --- /dev/null +++ b/src/repl.ts @@ -0,0 +1,11 @@ +import { NS } from '@ns' + +export async function main(ns: NS) : Promise { + const cmd = ns.args.join(" "); + // @ts-ignore: disable-next-line + const fn = new Function(["ns"],cmd); + const v = await fn(ns); + if(v !== undefined){ + ns.tprint(v) + } +} \ No newline at end of file