From 48771f54ddee143d8c16e8b9f7b66220fc22c09a Mon Sep 17 00:00:00 2001 From: monoid Date: Sun, 14 May 2023 18:02:37 +0900 Subject: [PATCH] f --- src/auto-backdoor.ts | 46 +++ src/batch-cal.ts | 7 +- src/batching.ts | 21 +- src/deploy-share.ts | 2 +- src/get-my-karma.ts | 16 + src/growing.ts | 46 +++ src/hacknet-daemon.ts | 4 +- src/lib/batchbase.ts | 411 ++++++++++++++++++++------ src/lib/colors.ts | 536 ++++++++++++++++++++++++++++++++++ src/lib/contract.ts | 9 +- src/lib/formula.ts | 34 ++- src/lib/log.ts | 3 +- src/lib/scheduler.ts | 197 +++++++++++-- src/lib/solve/arrayJump2.ts | 7 +- src/lib/solve/caesarCipher.ts | 30 ++ src/lib/solve/graph2color.ts | 6 +- src/rainbow.ts | 20 ++ src/runtest.ts | 30 ++ 18 files changed, 1284 insertions(+), 141 deletions(-) create mode 100644 src/auto-backdoor.ts create mode 100644 src/get-my-karma.ts create mode 100644 src/growing.ts create mode 100644 src/lib/colors.ts create mode 100644 src/lib/solve/caesarCipher.ts create mode 100644 src/rainbow.ts create mode 100644 src/runtest.ts diff --git a/src/auto-backdoor.ts b/src/auto-backdoor.ts new file mode 100644 index 0000000..78456c3 --- /dev/null +++ b/src/auto-backdoor.ts @@ -0,0 +1,46 @@ +import { NS } from '@ns' +import { parse } from './lib/flag'; +import { sprintf } from './lib/printf'; +import { selectRootedServerList } from "./lib/servers" + + +async function propagateBackdoor(ns:NS, hostname: string): Promise{ + ns.singularity.connect(hostname); + const list = ns.scan(hostname); + for (const relname of list) { + const info = ns.getServer(relname); + if(info.backdoorInstalled){ + continue; + } + if(info.requiredHackingSkill > ns.getHackingLevel()){ + continue; + } + ns.tprint(`install backdoor ${relname}`); + ns.singularity.connect(relname); + await ns.singularity.installBackdoor(); + ns.singularity.connect(hostname); + } +} + +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'] + msg.forEach(x=>ns.tprint(x)); + return; + } + if(flag._.length === 0){ + const servers = selectRootedServerList(ns); + for(const server of servers){ + ns.tprint("prop backdoor to "+server.hostname); + await propagateBackdoor(ns,server.hostname); + } + } + else{ + // const hostname = flag._[0]; + // ns.tprint(sprintf("install backdoor %s",hostname)); + // await ns.singularity.installBackdoor(hostname); + // ns.tprint("done"); + } +} + diff --git a/src/batch-cal.ts b/src/batch-cal.ts index 9258010..28404b7 100644 --- a/src/batch-cal.ts +++ b/src/batch-cal.ts @@ -20,14 +20,19 @@ export async function main(ns: NS) : Promise { return; } const v = parseInt(flag._[0].toString()); + if(isNaN(v)){ + ns.tprint("Error. Require Integer. v = " + flag._[0]); + return; + } 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); + if(info === null) break; col.push({ - costEffect :(info.earnMoneyPerSec / info.totalThreadCount), + costEffect :(info.earnMoneyPerCycle / info.totalThreadCount), index: i, total: info.totalThreadCount }) diff --git a/src/batching.ts b/src/batching.ts index 933bb6b..cc36073 100644 --- a/src/batching.ts +++ b/src/batching.ts @@ -1,13 +1,15 @@ import { NS, AutocompleteData } from '@ns' -import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack } from './lib/batchbase'; +import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, batchDaemon, requestHWGW } from './lib/batchbase'; import { parse } from './lib/flag'; + function help(ns:NS):void{ - ns.tprint("run cmd [target(hostname)] -h hostname") + ns.tprint("run cmd [target(hostname)] --hostname hostname") } // eslint-disable-next-line require-await export async function main(ns : NS) : Promise { + const flag = parse(ns.args.map(String)) const hostname: string|undefined = flag.hostname ?? flag.n; @@ -22,15 +24,20 @@ export async function main(ns : NS) : Promise { return; } const target = flag._[0].toString(); - + ns.print("check batch file pack ...") if(!isBatchFilePackInstalled(ns)){ await installBatchFilePack(ns); } - + const server = ns.getServer(hostname) await scpBatchFilePack(ns,hostname); - - const server = ns.getServer(target); - ns.tprint(); + ns.print("request hwgw ...") + requestHWGW({ + hostname, + usableRam: server.maxRam - server.ramUsed, + },target); + ns.print("request hwgw done") + ns.print("start batch daemon ..."); + await batchDaemon(ns, target); } // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/src/deploy-share.ts b/src/deploy-share.ts index 588101d..10a399c 100644 --- a/src/deploy-share.ts +++ b/src/deploy-share.ts @@ -21,7 +21,7 @@ export async function main(ns: NS): Promise { nThread = th; } else { - nThread = calculateMaximumThread(ns, hostname, getRamOfScript(ns,"weakenLoop")); + nThread = calculateMaximumThread(ns, hostname, ns.getScriptRam("share-server.js",hostname)); } const pid = ns.exec("share-server.js",hostname, nThread); ns.tprint(pid," Process started."); diff --git a/src/get-my-karma.ts b/src/get-my-karma.ts new file mode 100644 index 0000000..a866f0a --- /dev/null +++ b/src/get-my-karma.ts @@ -0,0 +1,16 @@ +import { NS } from '@ns' +import { parse } from './lib/flag'; +import { sprintf } from './lib/printf'; + +// 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'] + msg.forEach(x=>ns.tprint(x)); + return; + } + // typescript error ignore + // @ts-ignore TS2339 + ns.tprint(sprintf("karma: %.3f",ns.heart.break())); +} diff --git a/src/growing.ts b/src/growing.ts new file mode 100644 index 0000000..ee864a9 --- /dev/null +++ b/src/growing.ts @@ -0,0 +1,46 @@ +import { NS, AutocompleteData } from '@ns' +import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, growDaemon, requestGrow } from './lib/batchbase'; +import { parse } from './lib/flag'; + + +function help(ns:NS):void{ + ns.tprint("run cmd [target(hostname)] --hostname hostname") +} + +// eslint-disable-next-line require-await +export async function main(ns : NS) : Promise { + const flag = parse(ns.args.map(String)) + + const hostname: string|undefined = flag.hostname ?? flag.n; + if(!hostname){ + ns.tprint("hostname is not set"); + help(ns); + return; + } + if(flag._.length == 0){ + ns.tprint("target is not set"); + help(ns); + return; + } + const target = flag._[0].toString(); + ns.print("check batch file pack ...") + if(!isBatchFilePackInstalled(ns)){ + await installBatchFilePack(ns); + } + const server = ns.getServer(hostname) + await scpBatchFilePack(ns,hostname); + ns.print("request hwgw ...") + requestGrow({ + hostname, + usableRam: server.maxRam - server.ramUsed, + },target); + ns.print("request hwgw done") + ns.print("start batch daemon ..."); + await growDaemon(ns, target); + ns.print("end grow daemon"); +} + +// 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/hacknet-daemon.ts b/src/hacknet-daemon.ts index 8165dfe..86e04c7 100644 --- a/src/hacknet-daemon.ts +++ b/src/hacknet-daemon.ts @@ -225,11 +225,11 @@ export async function main(ns: NS): Promise { const account = new Account(budget); ns.tail(); - const period = 1000; + const period = 100; for (let i = 0; i < iter; i++) { //ns.clearLog() cycle(ns, account); await ns.sleep(period); - account.upgradeIncrement(ns,rate,period); + account.upgradeIncrement(ns,rate*0.1,period); } } \ No newline at end of file diff --git a/src/lib/batchbase.ts b/src/lib/batchbase.ts index a317de6..fbdf337 100644 --- a/src/lib/batchbase.ts +++ b/src/lib/batchbase.ts @@ -1,6 +1,7 @@ import { NS, Server } from "@ns" +import { info } from "console"; import { calculateServerGrowth, FormulaHelper } from "./formula"; - +import { Scheduler } from "./scheduler"; export type BatchType = "hack" | "grow" | "weaken" | "weakenLoop"; @@ -104,6 +105,7 @@ export interface ExecOption { */ numThread?: number; /** + * milliseconds to sleep before exec * @default 0 */ sleepDuration?: number; @@ -115,8 +117,9 @@ export interface ExecOption { } function execBatchfile(ns: NS, path: string, option: ExecOption): number { + const uuid = crypto.randomUUID(); return ns.exec(path, option.hostname, option.numThread, - option.sleepDuration ?? 0, option.target, option.stock ?? false); + option.sleepDuration ?? 0, option.target, option.stock ?? false, uuid); } export function execHack(ns: NS, option: ExecOption): number { @@ -131,16 +134,13 @@ export function execWeaken(ns: NS, option: ExecOption): number { export function execWeakenLoop(ns: NS, option: ExecOption): number { return execBatchfile(ns, "/tmp/weakenLoop.js", option); } +export function execScriptOf(ns: NS,type: BatchType, option:ExecOption):number { + return execBatchfile(ns, "/tmp/" + type + ".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; @@ -156,83 +156,30 @@ interface HWGW { GWThreadCount: number; } -type WorkReserved = { - allocated:false; -} | { - allocated:true; - hwgw: HWGW; -}; +function getTotalRamOf(hwgw: HWGW): number{ + return hwgw.HThreadCount * 1.7 + hwgw.HWThreadCount * 1.75 + hwgw.GThreadCount * 1.75 + hwgw.GWThreadCount * 1.75; +} type Resource = { hostname: string; - usableRam: string; + usableRam: number; }; -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; + earnMoneyPerCycle: number; + earnExpPerCycle: number; /** - * cycle time in seconds. + * cycle time in milliseconds. */ cycleTime: number; totalThreadCount: number; + + weakenTime: number; + growTime: number; + hackTime: number; } -export function calculateBatchResource(ns: NS, hostname: string, hackThread: number, startMoney?: number, cores = 1): HackCalculation { +export function calculateBatchResource(ns: NS, hostname: string, hackThread: number, startMoney?: number, cores = 1): HackCalculation| null { const helper = new FormulaHelper(ns); const server = ns.getServer(hostname); startMoney ??= server.moneyMax; @@ -242,13 +189,14 @@ export function calculateBatchResource(ns: NS, hostname: string, hackThread: num 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 cycleTime = 1000 + helper.calculateWeakenTime(server); const increasedSecurityAfterHack = helper.calculateHackSecurity(hackThread) + server.minDifficulty; const nWeakenHackThreads = Math.ceil(helper.calculateWeakenThreadCountToTargetSecurity( server.minDifficulty, increasedSecurityAfterHack, cores)); - + if(startMoney <= earnMoneyPerCycle){ + return null; + } const nGrowThreads = Math.ceil(helper.calculateGrowThreadCount(hostname, startMoney - earnMoneyPerCycle, startMoney, cores)); const increasedSecurityAfterGrow = helper.calculuateGrowthSecurity(nGrowThreads, cores) + server.minDifficulty; @@ -257,8 +205,11 @@ export function calculateBatchResource(ns: NS, hostname: string, hackThread: num increasedSecurityAfterGrow, cores)); return { - earnMoneyPerSec: earnMoneyPerCycle / cycleTime, - earnExpPerSec: earnExpPerCycle / cycleTime, + weakenTime: helper.calculateWeakenTime(server), + growTime: helper.calculateGrowTime(server), + hackTime: helper.calculateHackingTime(server), + earnMoneyPerCycle, + earnExpPerCycle, cycleTime, totalThreadCount: hackThread + nWeakenHackThreads + nGrowThreads + nWeakenGrowThreads, HThreadCount: hackThread, @@ -267,14 +218,163 @@ export function calculateBatchResource(ns: NS, hostname: string, hackThread: num GWThreadCount: nWeakenGrowThreads } } +class BatchManager { + server: Server; + ns: NS; + totalRam: number + scheluder: Scheduler; + resources: Resource[] = []; -function calculateOptimal(ns: NS, target: string, req: ReserveRequest): HWGW { + constructor(ns: NS, hostname: string) { + this.ns = ns; + this.server = ns.getServer(hostname); + this.totalRam = 0; + this.scheluder = new Scheduler(ns, 250); + } - return { - GThreadCount: 0, - HThreadCount: 0, - GWThreadCount: 0, - HWThreadCount: 0, + reserve(hostname: string, usableRam: number): void { + this.resources.push({ + hostname, + usableRam, + }); + this.totalRam += usableRam; + } + + getUsableTotalRam(): number { + return this.resources.reduce((acc, cur) => acc + cur.usableRam, 0); + } + + execute(type: BatchType, opt: Omit): number[] { + const ret = []; + let numThread = opt.numThread ?? 1; + const ramUnit = getRamOfScript(this.ns, type); + for (let i = 0; i < this.resources.length; i++) { + const resource = this.resources[i] + let threadToRun = Math.floor(resource.usableRam / ramUnit); + if (threadToRun > numThread) { + threadToRun = numThread; + } + if (threadToRun <= 0) continue; + + const pid = execScriptOf(this.ns, type, { + hostname: resource.hostname, + numThread: threadToRun, + sleepDuration: opt.sleepDuration, + target: opt.target, + stock: opt.stock, + }); + resource.usableRam -= threadToRun * ramUnit; + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + const doWork = ()=>{ + resource.usableRam += threadToRun * ramUnit; + const msg = [ + `${pid} has finished.`, + `${resource.hostname} recover ${threadToRun * ramUnit} ram`] + this.ns.print(msg.join("\n")); + } + this.scheluder.onPidTerminate(pid, {work() {doWork();}}); + numThread -= threadToRun; + ret.push(pid); + } + + return ret; + } +} + +class HWGWBatchManager extends BatchManager { + constructor(ns: NS, hostname: string) { + super(ns, hostname); + } + + calcBatchResource(hackThread: number): HackCalculation| null { + return calculateBatchResource(this.ns, this.server.hostname, hackThread, this.server.moneyMax) + } + /** + * execute batch + * do not await this function. + * @param hwgw HackCalculation of HWGW + * @param stock if true, it influnces stock market. + * @returns set of pids of scripts runned. + */ + async execBatch(hwgw: HackCalculation, stock?: boolean): Promise { + stock ??= false; + //this.ns.print(`executing batch of ${hwgw.totalThreadCount} threads.`); + //this.ns.print(`total ram: ${this.totalRam}`); + + this.ns.print(`stage 1: executed ${hwgw.HThreadCount} threads.`); + const hpid = this.execute("hack",{ + target:this.server.hostname, + numThread:hwgw.HThreadCount, + sleepDuration: hwgw.cycleTime - hwgw.hackTime - 1000, + stock + }); + await this.scheluder.sleepInWork(250); + this.ns.print(`stage 2: executed ${hwgw.HWThreadCount} threads.`); + const wpid = this.execute("weaken",{ + target:this.server.hostname, + numThread:hwgw.HWThreadCount, + sleepDuration: hwgw.cycleTime - hwgw.weakenTime - 1000, + stock + }); + await this.scheluder.sleepInWork(250); + this.ns.print(`stage 3: executed ${hwgw.GThreadCount} threads.`); + const gpid = this.execute("grow",{ + target:this.server.hostname, + numThread:hwgw.GThreadCount, + sleepDuration: hwgw.cycleTime - hwgw.growTime - 1000, + stock + }); + await this.scheluder.sleepInWork(250); + this.ns.print(`stage 4: executed ${hwgw.GWThreadCount} threads.`); + const gwpid = this.execute("weaken",{ + target:this.server.hostname, + numThread:hwgw.GWThreadCount, + sleepDuration: hwgw.cycleTime - hwgw.weakenTime - 1000, + stock + }); + await this.scheluder.sleepInWork(250); + return [...hpid, ...wpid, ...gpid, ...gwpid]; + } + + calcHWGW(): HackCalculation { + const helper = new FormulaHelper(this.ns); + const cycleTime = 1 + helper.calculateWeakenTime(this.server); + const ramPerSec = Math.floor(this.totalRam / (cycleTime / 1000)); + const [min,minInfo] = this.getMinThreadCount(); + const minRam = getTotalRamOf(minInfo); + if(minRam > ramPerSec){ + return minInfo; + } + // linear search + let prev = minInfo; + for (let i = min+1; i < min + 100; i++) { + const info = this.calcBatchResource(i); + if(info === null) break; + if(getTotalRamOf(info) > ramPerSec){ + return prev; + } + prev = info; + } + return prev; + } + + getMinThreadCount():[number,HackCalculation]{ + let minCE = 0; + let minHC = 0; + let minInfo: HackCalculation; + for (let i = 1; i < 100; i++) { + const info = this.calcBatchResource(i); + if(info === null) break; + const ce = info.earnMoneyPerCycle / info.totalThreadCount; + if (ce >= minCE) { + minCE = ce; + minHC = i; + minInfo = info; + } + else break; + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return [minHC,minInfo!]; } } @@ -292,9 +392,140 @@ export function requestHWGW(req: ReserveRequest, target: string): void { 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 +export function popHWGWRequest(hostname: string): ReserveRequest[]{ + const key = getKeyOfHWGWBatchManager(hostname); + const text = localStorage.getItem(key) + if(text === null){ + return []; + } + localStorage.setItem(key,"[]"); + return JSON.parse(text) as ReserveRequest[]; +} + +export async function batchDaemon(ns: NS, hostname: string): Promise { + ns.disableLog("getBitNodeMultipliers"); + ns.disableLog("sleep"); + const mgr = new HWGWBatchManager(ns, hostname); + for (;;) { + const requests = popHWGWRequest(hostname); + for (const req of requests) { + mgr.reserve(req.hostname, req.usableRam); + } + const hwgw = mgr.calcHWGW(); + if (hwgw !== null && mgr.getUsableTotalRam() >= getTotalRamOf(hwgw)) { + ns.print(`hwgw: total ${hwgw.totalThreadCount} threads.`); + mgr.execBatch(hwgw); + } + for (let i = 0; i < 4; i++) { + await mgr.scheluder.tick(); + } + } +} + +function getKeyOfGrowBatchManager(hostname: string): string{ + return `Grow-${hostname}`; +} +export function popGrowRequest(hostname: string): ReserveRequest[]{ + const key = getKeyOfGrowBatchManager(hostname); + const text = localStorage.getItem(key) + if(text === null){ + return []; + } + localStorage.setItem(key,"[]"); + return JSON.parse(text) as ReserveRequest[]; +} +export function requestGrow(req: ReserveRequest, target: string): void { + const key = getKeyOfGrowBatchManager(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)); +} + + +class GrowManager extends BatchManager { + constructor(ns: NS, hostname: string) { + super(ns, hostname); + } + + async execStraigy(stock: boolean): Promise { + const helper = new FormulaHelper(this.ns); + const difficultyMargin = 1; + if(this.server.hackDifficulty > this.server.minDifficulty + difficultyMargin){ + const weakenThread = helper.calculateWeakenThreadCountToTargetSecurity( + this.server.hackDifficulty, this.server.minDifficulty); + const g = this.execute("weaken", { + target: this.server.hostname, + numThread: weakenThread, + sleepDuration: 1, + stock + }); + this.ns.print("[PROCESS] weakened"); + const work = async (): Promise => { + await this.execStraigy(stock); + } + this.scheluder.onPidTerminate(g[0], { + work + }); + } + + const moneyFactor = 0.8; + if(this.server.moneyAvailable > this.server.moneyMax * moneyFactor){ + return; + } + const totalRam = this.getUsableTotalRam(); + const growThread = Math.floor(totalRam / (getRamOfScript(this.ns, "grow") * 3)); + const weakenThread = growThread * 2; + const growTime = helper.calculateGrowTime(this.server); + const weakenTime = helper.calculateWeakenTime(this.server); + + this.execute("weaken", { + target: this.server.hostname, + numThread: weakenThread, + sleepDuration: 0, + }); + await this.scheluder.sleepInWork(250); + const g = this.execute("grow", { + target: this.server.hostname, + numThread: growThread, + sleepDuration: weakenTime - growTime, + stock + }); + + const work = async (): Promise => { + await this.execStraigy(stock); + } + this.scheluder.onPidTerminate(g[0], { + work + }); + } + + empty(): boolean { + return this.scheluder.empty(); + } +} + +export async function growDaemon(ns: NS, hostname: string): Promise { + ns.disableLog("getBitNodeMultipliers"); + ns.disableLog("sleep"); + const mgr = new GrowManager(ns, hostname); + processRequest(); + mgr.execStraigy(false); + while (!mgr.empty()) { + processRequest() + await mgr.scheluder.tick(); + } + + function processRequest(): void{ + const requests = popGrowRequest(hostname); + for (const req of requests) { + mgr.reserve(req.hostname, req.usableRam); + } + } +} \ No newline at end of file diff --git a/src/lib/colors.ts b/src/lib/colors.ts new file mode 100644 index 0000000..86150f2 --- /dev/null +++ b/src/lib/colors.ts @@ -0,0 +1,536 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +// A module to print ANSI terminal colors. Inspired by chalk, kleur, and colors +// on npm. + +/** + * String formatters and utilities for dealing with ANSI color codes. + * + * This module is browser compatible. + * + * ```ts + * import { bgBlue, red, bold } from "https://deno.land/std@$STD_VERSION/fmt/colors.ts"; + * console.log(bgBlue(red(bold("Hello world!")))); + * ``` + * + * This module supports `NO_COLOR` environmental variable disabling any coloring + * if `NO_COLOR` is set. + * + * @module + */ + +const noColor = false; + +interface Code { + open: string; + close: string; + regexp: RegExp; +} + +/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */ +interface Rgb { + r: number; + g: number; + b: number; +} + +let enabled = !noColor; + +/** + * Set changing text color to enabled or disabled + * @param value + */ +export function setColorEnabled(value: boolean) { + if (noColor) { + return; + } + + enabled = value; +} + +/** Get whether text color change is enabled or disabled. */ +export function getColorEnabled(): boolean { + return enabled; +} + +/** + * Builds color code + * @param open + * @param close + */ +function code(open: number[], close: number): Code { + return { + open: `\x1b[${open.join(";")}m`, + close: `\x1b[${close}m`, + regexp: new RegExp(`\\x1b\\[${close}m`, "g"), + }; +} + +/** + * Applies color and background based on color code and its associated text + * @param str text to apply color settings to + * @param code color code to apply + */ +function run(str: string, code: Code): string { + return enabled + ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` + : str; +} + +/** + * Reset the text modified + * @param str text to reset + */ +export function reset(str: string): string { + return run(str, code([0], 0)); +} + +/** + * Make the text bold. + * @param str text to make bold + */ +export function bold(str: string): string { + return run(str, code([1], 22)); +} + +/** + * The text emits only a small amount of light. + * @param str text to dim + */ +export function dim(str: string): string { + return run(str, code([2], 22)); +} + +/** + * Make the text italic. + * @param str text to make italic + */ +export function italic(str: string): string { + return run(str, code([3], 23)); +} + +/** + * Make the text underline. + * @param str text to underline + */ +export function underline(str: string): string { + return run(str, code([4], 24)); +} + +/** + * Invert background color and text color. + * @param str text to invert its color + */ +export function inverse(str: string): string { + return run(str, code([7], 27)); +} + +/** + * Make the text hidden. + * @param str text to hide + */ +export function hidden(str: string): string { + return run(str, code([8], 28)); +} + +/** + * Put horizontal line through the center of the text. + * @param str text to strike through + */ +export function strikethrough(str: string): string { + return run(str, code([9], 29)); +} + +/** + * Set text color to black. + * @param str text to make black + */ +export function black(str: string): string { + return run(str, code([30], 39)); +} + +/** + * Set text color to red. + * @param str text to make red + */ +export function red(str: string): string { + return run(str, code([31], 39)); +} + +/** + * Set text color to green. + * @param str text to make green + */ +export function green(str: string): string { + return run(str, code([32], 39)); +} + +/** + * Set text color to yellow. + * @param str text to make yellow + */ +export function yellow(str: string): string { + return run(str, code([33], 39)); +} + +/** + * Set text color to blue. + * @param str text to make blue + */ +export function blue(str: string): string { + return run(str, code([34], 39)); +} + +/** + * Set text color to magenta. + * @param str text to make magenta + */ +export function magenta(str: string): string { + return run(str, code([35], 39)); +} + +/** + * Set text color to cyan. + * @param str text to make cyan + */ +export function cyan(str: string): string { + return run(str, code([36], 39)); +} + +/** + * Set text color to white. + * @param str text to make white + */ +export function white(str: string): string { + return run(str, code([37], 39)); +} + +/** + * Set text color to gray. + * @param str text to make gray + */ +export function gray(str: string): string { + return brightBlack(str); +} + +/** + * Set text color to bright black. + * @param str text to make bright-black + */ +export function brightBlack(str: string): string { + return run(str, code([90], 39)); +} + +/** + * Set text color to bright red. + * @param str text to make bright-red + */ +export function brightRed(str: string): string { + return run(str, code([91], 39)); +} + +/** + * Set text color to bright green. + * @param str text to make bright-green + */ +export function brightGreen(str: string): string { + return run(str, code([92], 39)); +} + +/** + * Set text color to bright yellow. + * @param str text to make bright-yellow + */ +export function brightYellow(str: string): string { + return run(str, code([93], 39)); +} + +/** + * Set text color to bright blue. + * @param str text to make bright-blue + */ +export function brightBlue(str: string): string { + return run(str, code([94], 39)); +} + +/** + * Set text color to bright magenta. + * @param str text to make bright-magenta + */ +export function brightMagenta(str: string): string { + return run(str, code([95], 39)); +} + +/** + * Set text color to bright cyan. + * @param str text to make bright-cyan + */ +export function brightCyan(str: string): string { + return run(str, code([96], 39)); +} + +/** + * Set text color to bright white. + * @param str text to make bright-white + */ +export function brightWhite(str: string): string { + return run(str, code([97], 39)); +} + +/** + * Set background color to black. + * @param str text to make its background black + */ +export function bgBlack(str: string): string { + return run(str, code([40], 49)); +} + +/** + * Set background color to red. + * @param str text to make its background red + */ +export function bgRed(str: string): string { + return run(str, code([41], 49)); +} + +/** + * Set background color to green. + * @param str text to make its background green + */ +export function bgGreen(str: string): string { + return run(str, code([42], 49)); +} + +/** + * Set background color to yellow. + * @param str text to make its background yellow + */ +export function bgYellow(str: string): string { + return run(str, code([43], 49)); +} + +/** + * Set background color to blue. + * @param str text to make its background blue + */ +export function bgBlue(str: string): string { + return run(str, code([44], 49)); +} + +/** + * Set background color to magenta. + * @param str text to make its background magenta + */ +export function bgMagenta(str: string): string { + return run(str, code([45], 49)); +} + +/** + * Set background color to cyan. + * @param str text to make its background cyan + */ +export function bgCyan(str: string): string { + return run(str, code([46], 49)); +} + +/** + * Set background color to white. + * @param str text to make its background white + */ +export function bgWhite(str: string): string { + return run(str, code([47], 49)); +} + +/** + * Set background color to bright black. + * @param str text to make its background bright-black + */ +export function bgBrightBlack(str: string): string { + return run(str, code([100], 49)); +} + +/** + * Set background color to bright red. + * @param str text to make its background bright-red + */ +export function bgBrightRed(str: string): string { + return run(str, code([101], 49)); +} + +/** + * Set background color to bright green. + * @param str text to make its background bright-green + */ +export function bgBrightGreen(str: string): string { + return run(str, code([102], 49)); +} + +/** + * Set background color to bright yellow. + * @param str text to make its background bright-yellow + */ +export function bgBrightYellow(str: string): string { + return run(str, code([103], 49)); +} + +/** + * Set background color to bright blue. + * @param str text to make its background bright-blue + */ +export function bgBrightBlue(str: string): string { + return run(str, code([104], 49)); +} + +/** + * Set background color to bright magenta. + * @param str text to make its background bright-magenta + */ +export function bgBrightMagenta(str: string): string { + return run(str, code([105], 49)); +} + +/** + * Set background color to bright cyan. + * @param str text to make its background bright-cyan + */ +export function bgBrightCyan(str: string): string { + return run(str, code([106], 49)); +} + +/** + * Set background color to bright white. + * @param str text to make its background bright-white + */ +export function bgBrightWhite(str: string): string { + return run(str, code([107], 49)); +} + +/* Special Color Sequences */ + +/** + * Clam and truncate color codes + * @param n + * @param max number to truncate to + * @param min number to truncate from + */ +function clampAndTruncate(n: number, max = 255, min = 0): number { + return Math.trunc(Math.max(Math.min(n, max), min)); +} + +/** + * Set text color using paletted 8bit colors. + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + * @param str text color to apply paletted 8bit colors to + * @param color code + */ +export function rgb8(str: string, color: number): string { + return run(str, code([38, 5, clampAndTruncate(color)], 39)); +} + +/** + * Set background color using paletted 8bit colors. + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + * @param str text color to apply paletted 8bit background colors to + * @param color code + */ +export function bgRgb8(str: string, color: number): string { + return run(str, code([48, 5, clampAndTruncate(color)], 49)); +} + +/** + * Set text color using 24bit rgb. + * `color` can be a number in range `0x000000` to `0xffffff` or + * an `Rgb`. + * + * To produce the color magenta: + * + * ```ts + * import { rgb24 } from "./colors.ts"; + * rgb24("foo", 0xff00ff); + * rgb24("foo", {r: 255, g: 0, b: 255}); + * ``` + * @param str text color to apply 24bit rgb to + * @param color code + */ +export function rgb24(str: string, color: number | Rgb): string { + if (typeof color === "number") { + return run( + str, + code( + [38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], + 39, + ), + ); + } + return run( + str, + code( + [ + 38, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b), + ], + 39, + ), + ); +} + +/** + * Set background color using 24bit rgb. + * `color` can be a number in range `0x000000` to `0xffffff` or + * an `Rgb`. + * + * To produce the color magenta: + * + * ```ts + * import { bgRgb24 } from "./colors.ts"; + * bgRgb24("foo", 0xff00ff); + * bgRgb24("foo", {r: 255, g: 0, b: 255}); + * ``` + * @param str text color to apply 24bit rgb to + * @param color code + */ +export function bgRgb24(str: string, color: number | Rgb): string { + if (typeof color === "number") { + return run( + str, + code( + [48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], + 49, + ), + ); + } + return run( + str, + code( + [ + 48, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b), + ], + 49, + ), + ); +} + +// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js +const ANSI_PATTERN = new RegExp( + [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", + ].join("|"), + "g", +); + +/** + * Remove ANSI escape codes from the string. + * @param string to remove ANSI escape codes from + */ +export function stripColor(string: string): string { + return string.replace(ANSI_PATTERN, ""); +} diff --git a/src/lib/contract.ts b/src/lib/contract.ts index 6e8e65e..ebfbfb6 100644 --- a/src/lib/contract.ts +++ b/src/lib/contract.ts @@ -14,6 +14,7 @@ import {selectAllServerList} from "lib/servers"; import { logFile } from "./log"; import { SolveFailError } from "./solve/unsolved"; import { RLECompressionSolve, decompressLZSolve, compressLZSolve } from "./solve/compression"; +import {caesarCipherSolve} from "./solve/caesarCipher"; export { SolveFailError } from "./solve/unsolved"; @@ -152,13 +153,11 @@ const ContractMAP: {[key:string]:ContractSolver} = { "Compression III: LZ Compression": { name: "Compression III: LZ Compression", solve: compressLZSolve, - },/* + }, "Encryption I: Caesar Cipher": { name: "Encryption I: Caesar Cipher", - solve() { - notImplemented(); - } - }, + solve: caesarCipherSolve, + },/* "Encryption II: Vigenère Cipher": { name: "Encryption II: Vigenère Cipher", solve() { diff --git a/src/lib/formula.ts b/src/lib/formula.ts index 22d2511..a79fb2c 100644 --- a/src/lib/formula.ts +++ b/src/lib/formula.ts @@ -206,6 +206,11 @@ export function canBeAccessBitNode(ns: NS): boolean { } export function getBitNodeMultipliersSafe(ns: NS): BitNodeMultipliers { + const fnName = "getBitNodeMultipliers"; + const isEnabled = ns.isLogEnabled(fnName); + ns.disableLog("disableLog"); + ns.disableLog("enableLog"); + ns.disableLog("getBitNodeMultipliers"); try { return ns.getBitNodeMultipliers(); } catch (error) { @@ -213,6 +218,10 @@ export function getBitNodeMultipliersSafe(ns: NS): BitNodeMultipliers { return DefaultBitNodeMultipliers; } throw error; + } finally { + if (isEnabled) { + ns.enableLog("getBitNodeMultipliers"); + } } } @@ -222,7 +231,10 @@ export class FormulaHelper { this.ns = ns; } calculateWeakenThreadCountToTargetSecurity(targetSecurity: number, curSecurity: number, cores = 1): number { - const k = targetSecurity - targetSecurity; + if(curSecurity < targetSecurity) { + return 0; + } + const k = curSecurity - targetSecurity; const c = this.ns.weakenAnalyze(1, cores); return k / c; } @@ -241,21 +253,31 @@ export class FormulaHelper { return ServerWeakenAmount * nThread * coreBonus * mul.ServerWeakenRate; } /** - * 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 * @param server - * @returns seconds to complete a weaken operation + * @returns milliseconds to complete a weaken operation */ calculateWeakenTime(server: Server): number { const player = this.ns.getPlayer(); - return calculateWeakenTime(server, player); + return calculateWeakenTime(server, player) * 1000; } + /** + * Returns time it takes to complete a grow operation on a server, in milliseconds + * @param server + * @returns milliseconds to complete a grow operation + */ calculateGrowTime(server: Server): number { const player = this.ns.getPlayer(); - return calculateGrowTime(server, player); + return calculateGrowTime(server, player) * 1000; } + /** + * Returns time it takes to complete a hacking operation on a server, in milliseconds + * @param server + * @returns milliseconds to complete a hacking operation + */ calculateHackingTime(server: Server): number { const player = this.ns.getPlayer(); - return calculateHackingTime(server, player); + return calculateHackingTime(server, player) * 1000; } /** * Hack diff --git a/src/lib/log.ts b/src/lib/log.ts index e988a26..9d1fef2 100644 --- a/src/lib/log.ts +++ b/src/lib/log.ts @@ -2,7 +2,8 @@ import {NS} from "@ns" export async function logFile(ns:NS,logFilename:string, data: unknown):Promise{ logFilename = logFilename.replaceAll(" ","-"); - logFilename = `/log/${logFilename}${Date.now()}.txt`; + logFilename = logFilename.replaceAll(":","-"); + logFilename = `/log/${logFilename}.txt`; const loggedData = JSON.stringify(data); if(ns.fileExists(logFilename)){ await ns.write(logFilename,"\n"+loggedData,"a"); diff --git a/src/lib/scheduler.ts b/src/lib/scheduler.ts index 852a379..cf36223 100644 --- a/src/lib/scheduler.ts +++ b/src/lib/scheduler.ts @@ -1,38 +1,185 @@ -import {NS } from "@ns"; +import {NS} from "@ns"; - -type WorkFunc = ()=>void; -type WorkChunk = { - onStart: WorkFunc[]; - onFinish: WorkFunc[]; -} - -interface Tickable { +/** + * tick in scheduler + */ +export interface Tickable { + /** + * tick in scheduler + */ tick(): void; + /** + * no work to run in this tick. + */ + empty(): boolean; } -class PidTerminateExecutor{ +/** + * worker in scheduler + */ +export interface Work { + work: (() => Promise) | (()=>void); +} + +/** + * set of work to be executed at the same time + */ +export class WorkSet implements Work{ + works: Work[]; + constructor(){ + this.works = []; + } + add(work: Work): void{ + this.works.push(work); + } + remove(work: Work): void{ + this.works = this.works.filter(x=>x !== work); + } + concat(other: WorkSet): void{ + this.works = this.works.concat(other.works); + } + async work(): Promise{ + for(const work of this.works){ + await work.work(); + } + } +} + +export function mergeWork(...args: Work[]): WorkSet{ + const workSet = new WorkSet(); + for(const work of args){ + if(work instanceof WorkSet){ + workSet.concat(work); + } + else { + workSet.add(work); + } + } + return workSet; +} + +class PidTerminateExecutor implements Tickable{ 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 ) - } - }); - } -} + pidSet: Map; -class TimerExecutor{ - ns: NS; constructor(ns: NS){ this.ns = ns; + this.lastCheck = Date.now(); + this.pidSet = new Map(); + } + watchPid(pid: number, work: Work): void{ + if(this.pidSet.has(pid)){ + throw new Error(`pid ${pid} is already watched`); + } + this.pidSet.set(pid, work); + } + + empty(): boolean{ + return this.pidSet.size === 0; } - asdf():{ + async tick(): Promise{ + const current = Date.now(); + const deadScripts = this.ns.getRecentScripts().filter(x=>x.timeOfDeath.getTime() >= this.lastCheck); + for(const script of deadScripts){ + const target = this.pidSet.get(script.pid); + if(target){ + await target.work(); + } + this.pidSet.delete(script.pid); + } + this.lastCheck = current; } } + +function normalizeTime(time: number, timeResolution: number): number{ + return Math.floor(time / timeResolution) * timeResolution; +} + +class TimerExecutor implements Tickable{ + ns: NS; + timerResolution: number; + timerStart: number; + workBucket: Map; + constructor(ns: NS, timerResolution = 250){ + this.ns = ns; + this.workBucket = new Map; + this.timerResolution = timerResolution; + this.timerStart = performance.now(); + } + + empty(): boolean{ + return this.workBucket.size === 0; + } + + setTimeout(work: Work, delay: number): void { + const time = performance.now() + delay - this.timerStart; + const normalizedTime = normalizeTime(time, this.timerResolution); + const workSet = this.workBucket.get(normalizedTime); + if(workSet){ + workSet.add(work); + } + else{ + this.workBucket.set(normalizedTime, mergeWork(work)); + } + } + async tick(): Promise { + const current = performance.now(); + const normalizedCurrent = normalizeTime(current-this.timerStart, this.timerResolution); + const workSet = this.workBucket.get(normalizedCurrent); + if(workSet){ + await workSet.work(); + this.workBucket.delete(normalizedCurrent); + } + } + async sleepForTick(): Promise{ + const current = performance.now(); + + const normalizedCurrent = normalizeTime(current-this.timerStart, this.timerResolution); + const next = normalizedCurrent + this.timerResolution; + + const nextTime = this.timerStart + next; + const sleepTime = nextTime - current; + //this.ns.print(`Sleeping for ${sleepTime}ms`); + await this.ns.sleep(sleepTime); + } +} + +export class Scheduler{ + ns: NS; + timeResolution: number; + pidWatcher: PidTerminateExecutor; + timerWatcher: TimerExecutor; + constructor(ns: NS, timeResolution = 250){ + this.ns = ns; + this.timeResolution = timeResolution; + this.pidWatcher = new PidTerminateExecutor(ns); + this.timerWatcher = new TimerExecutor(ns, timeResolution); + } + + empty(): boolean{ + return this.pidWatcher.empty() && this.timerWatcher.empty(); + } + + setTimeout(work: Work, delay: number): void { + this.timerWatcher.setTimeout(work, delay); + } + + sleepInWork(delay: number): Promise{ + return new Promise(resolve=>{ + this.setTimeout({work(){ + resolve(); + } }, delay); + }); + } + onPidTerminate(pid: number, work: Work): void{ + this.pidWatcher.watchPid(pid, work); + } + + async tick(): Promise{ + await this.pidWatcher.tick(); + await this.timerWatcher.tick(); + await this.timerWatcher.sleepForTick(); + } +} \ No newline at end of file diff --git a/src/lib/solve/arrayJump2.ts b/src/lib/solve/arrayJump2.ts index d9e175b..643ce5f 100644 --- a/src/lib/solve/arrayJump2.ts +++ b/src/lib/solve/arrayJump2.ts @@ -7,12 +7,15 @@ export function arrayJump2(arr:number[]): number{ d[j] = Math.min(d[j],d[i]+1); } } - console.log(d); return d[arr.length - 1]; } export function arrayJump2Solve(arr:number[]): number{ - return arrayJump2(arr); + const v = arrayJump2(arr); + if (v === Infinity) { + return 0; + } + return v; } export const testcases: Array<[number[],number]> = [ diff --git a/src/lib/solve/caesarCipher.ts b/src/lib/solve/caesarCipher.ts new file mode 100644 index 0000000..97f976d --- /dev/null +++ b/src/lib/solve/caesarCipher.ts @@ -0,0 +1,30 @@ +const alphabet = 'abcdefghijklmnopqrstuvwxyz'.toUpperCase(); + +export function caesarCipher(str: string, key: number): string { + return str.split('').map((ch) => { + if (ch === ' ') return ch; + const idx = alphabet.indexOf(ch); + return alphabet[(idx + key) % alphabet.length]; + }).join(''); +} + +type CaesarCipherInput = [string, number]; + +export function caesarCipherSolve([plain, key]: CaesarCipherInput): string[] { + return [caesarCipher(plain, 26-key)]; +} + +//type TestCase = [CaesarCipherInput, string]; +// +//const TestCases: TestCase[] = [ +// [["ABC", -1], "BCD"], +// [["ABC", -2], "CDE"], +// [["ABC", -3], "DEF"], +// [["ABC", -4], "EFG"], +// [["ABC", -5], "FGH"], +// [["ABC", -6], "GHI"], +// [["HELLO WORLD", -1], "IFMMP XPSME"], +// [["Z", 1], "A"], +// [["MODEM FRAME VIRUS TRASH POPUP", -13], "ZBQRZ SENZR IVEHF GENFU CBCHC"] +// [["LOGIN MOUSE TABLE PRINT FLASH", -18], "TWOQV UWCAM BIJTM XZQVB NTIAP"] +//] diff --git a/src/lib/solve/graph2color.ts b/src/lib/solve/graph2color.ts index aec5e1d..62db1ba 100644 --- a/src/lib/solve/graph2color.ts +++ b/src/lib/solve/graph2color.ts @@ -50,7 +50,11 @@ export function graph2coloring([nVertex,edges]:Input):number[]{ } } export function graph2coloringSolve(input:Input):string[]{ - return graph2coloring(input).map(x=>x.toString()); + const ret = graph2coloring(input).map(x=>x.toString()); + if (ret.length === 0) { + return ["[]"]; + } + return ret; } export type TestCase = [ Input, diff --git a/src/rainbow.ts b/src/rainbow.ts new file mode 100644 index 0000000..a0b59d0 --- /dev/null +++ b/src/rainbow.ts @@ -0,0 +1,20 @@ +import { NS } from '@ns' +import { parse } from './lib/flag'; +import { sprintf } from './lib/printf'; + +function guess(ns:NS,text:string):void{ + // undocumented function + // @ts-ignore TS2339 + return ns.rainbow(text); +} + +// 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'] + msg.forEach(x=>ns.tprint(x)); + return; + } + guess(ns,"noodles") +} diff --git a/src/runtest.ts b/src/runtest.ts new file mode 100644 index 0000000..0b85719 --- /dev/null +++ b/src/runtest.ts @@ -0,0 +1,30 @@ +import { NS } from '@ns' +import { parse } from './lib/flag'; +import { red, rgb24, blue, bgBrightMagenta } from './lib/colors'; + + +// 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'] + msg.forEach(x=>ns.tprint(x)); + return; + } + ns.tprint(red("hello world red ")); + ns.tprint(blue("hello world blue")); + ns.tprint(bgBrightMagenta("hello world bgBrightMagenta")); + ns.tprint(rgb24("hello world #00ff00", 0x00ff00)); + ns.tprint(rgb24("hello world #f0f000", 0xf0f000)); + ns.tprint(rgb24("hello world #0000ff", 0x0000ff)); + ns.tprint(rgb24("hello world #0000ff", {r:0,g:0,b:255})); + //const d = "document" + //const doc = eval(d) as Document; + //const div = doc.querySelector(".css-1hamw82") as HTMLDivElement; + //if(!div){ + // ns.tprint("div not found"); + // return; + //} + //const text = div.innerText; + //ns.tprint(text); +} \ No newline at end of file