This commit is contained in:
monoid 2022-08-22 14:23:34 +09:00
parent d24d3a63c8
commit 374196acce
11 changed files with 617 additions and 48 deletions

View File

@ -5,6 +5,7 @@
"main": "index.js",
"scripts": {
"build": "node build.js",
"check": "tsc --noEmit",
"defs": "node ./updateDefs.js"
},
"repository": {

49
src/batch-cal.ts Normal file
View File

@ -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<void> {
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]
}

45
src/deploy-share.ts Normal file
View File

@ -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<void> {
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];
}

57
src/deploy-weaken.ts Normal file
View File

@ -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<void> {
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];
}

View File

@ -2,16 +2,17 @@ import { NS } from '@ns'
export async function main(ns : NS) : Promise<void> {
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}`);
}

View File

@ -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";
if(sleepDuration > 0){
await ns.sleep(sleepDuration);
}
await ns.${funcName}(hostname,{
stock,
});
}`
async function unpackFileEntry(ns: NS, filePack: FilePackEntry): Promise<void> {
async function unpackFileEntry(ns: NS, filePack: BatchFilePackEntry): Promise<void> {
await ns.write(filePack.path, filePack.content, "w");
}
@ -35,16 +43,43 @@ export async function unpackFilePack(ns: NS, pack: FilePack): Promise<void> {
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,
});
}
}`,
}
]
@ -75,7 +110,7 @@ export interface ExecOption{
/**
* target hostname to operate "hack", "weaken" or "grow"
*/
target: number;
target: string;
stock?: boolean;
}
@ -93,3 +128,173 @@ export function execGrow(ns: NS,option: ExecOption): number{
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<void> {
const data = this.willExecuted.shift();
await this.sleepCorrect();
}
/**
* exact time Sleep
*/
async sleepCorrect(): Promise<void> {
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<void> {
// const mgr = new HWGWBatchManager(ns, BATCH_KEY);
// for (; ;) {
// await mgr.tick();
// }
//}

View File

@ -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;
}
@ -144,3 +195,94 @@ export function calculateWeakenTime(server: Server, player: Player): number {
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);
}
}

38
src/lib/scheduler.ts Normal file
View File

@ -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<number, EventTarget>;
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():{
}
}

View File

@ -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}`);

View File

@ -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<void> {
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<void> {
} 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 {
@ -26,14 +31,28 @@ export async function main(ns: NS): Promise<void> {
weakenTime,
earnMoney,
ce: earnMoney* x.moneyMax/weakenTime,
expCe: expGain / weakenTime,
expGain,
}
});
m.sort((a,b)=>(b.ce-a.ce));
if(flag.exp){
m.sort((a,b)=>(b.expCe-a.expCe));
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);
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);
})
}
}

11
src/repl.ts Normal file
View File

@ -0,0 +1,11 @@
import { NS } from '@ns'
export async function main(ns: NS) : Promise<void> {
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)
}
}