This commit is contained in:
monoid 2023-05-14 18:02:37 +09:00
parent 374196acce
commit 48771f54dd
18 changed files with 1284 additions and 141 deletions

46
src/auto-backdoor.ts Normal file
View File

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

View File

@ -20,14 +20,19 @@ export async function main(ns: NS) : Promise<void> {
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
})

View File

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

View File

@ -21,7 +21,7 @@ export async function main(ns: NS): Promise<void> {
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.");

16
src/get-my-karma.ts Normal file
View File

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

46
src/growing.ts Normal file
View File

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

View File

@ -225,11 +225,11 @@ export async function main(ns: NS): Promise<void> {
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);
}
}

View File

@ -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<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;
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<ExecOption, "hostname">): 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<number[]> {
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<void> {
// const mgr = new HWGWBatchManager(ns, BATCH_KEY);
// for (; ;) {
// await mgr.tick();
// }
//}
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<void> {
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<void> {
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<void> => {
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<void> => {
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<void> {
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);
}
}
}

536
src/lib/colors.ts Normal file
View File

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

View File

@ -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() {

View File

@ -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

View File

@ -2,7 +2,8 @@ import {NS} from "@ns"
export async function logFile(ns:NS,logFilename:string, data: unknown):Promise<void>{
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");

View File

@ -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>) | (()=>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<void>{
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<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 )
}
});
}
}
pidSet: Map<number, Work>;
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<void>{
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<number, WorkSet>;
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<void> {
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<void>{
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<void>{
return new Promise<void>(resolve=>{
this.setTimeout({work(){
resolve();
} }, delay);
});
}
onPidTerminate(pid: number, work: Work): void{
this.pidWatcher.watchPid(pid, work);
}
async tick(): Promise<void>{
await this.pidWatcher.tick();
await this.timerWatcher.tick();
await this.timerWatcher.sleepForTick();
}
}

View File

@ -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]> = [

View File

@ -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"]
//]

View File

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

20
src/rainbow.ts Normal file
View File

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

30
src/runtest.ts Normal file
View File

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