This commit is contained in:
monoid 2022-08-20 10:09:52 +09:00
parent dd9664d4ee
commit d24d3a63c8
41 changed files with 5069 additions and 126 deletions

View File

@ -22,6 +22,9 @@ module.exports = {
"@typescript-eslint/explicit-module-boundary-types": ["off"], "@typescript-eslint/explicit-module-boundary-types": ["off"],
"@typescript-eslint/no-non-null-assertion": ["off"], "@typescript-eslint/no-non-null-assertion": ["off"],
"@typescript-eslint/no-use-before-define": ["off"], "@typescript-eslint/no-use-before-define": ["off"],
"@typescript-eslint/explicit-function-return-type":["off"],
"@typescript-eslint/no-parameter-properties": ["off"], "@typescript-eslint/no-parameter-properties": ["off"],
"@typescript-eslint/no-unused-vars": [ "@typescript-eslint/no-unused-vars": [
"error", "error",

View File

@ -15,10 +15,29 @@
"prefix": "autocomplete", "prefix": "autocomplete",
"body": [ "body": [
"// eslint-disable-next-line @typescript-eslint/no-unused-vars", "// eslint-disable-next-line @typescript-eslint/no-unused-vars",
"export function autocomplete(data : ServerData, args : string[]) : string[] {", "export function autocomplete(data : AutocompleteData, args : string[]) : string[] {",
"\treturn [...data.servers]", "\treturn [...data.servers]",
"}" "}"
], ],
"description": "autocomplete" "description": "autocomplete"
},
"template-advanced":{
"scope": "typescript",
"prefix": "template-advanced",
"body": [
"import { NS } from '@ns'",
"import { parse } from './lib/flag';",
"import { sprintf } from './lib/printf';",
"",
"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;",
" }",
"}"
],
"description": "autocomplete"
} }
} }

19
src/auto-contract.ts Normal file
View File

@ -0,0 +1,19 @@
import { NS } from '@ns'
import { selectAllContract, isSolvable, solveContract } from './lib/contract'
export async function main(ns : NS) : Promise<void> {
const remoteList = selectAllContract(ns);
for (const contract of remoteList) {
if(isSolvable(ns,contract.filename,contract.hostname)){
const [success, reward] = await solveContract(ns,contract.filename,contract.hostname);
if(success){
ns.tprint(`solve ${contract.filename}`);
ns.tprint(reward);
}
else{
ns.tprint("stop")
break;
}
}
}
}

39
src/batching.ts Normal file
View File

@ -0,0 +1,39 @@
import { NS, AutocompleteData } from '@ns'
import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack } from './lib/batchbase';
import { parse } from './lib/flag';
function help(ns:NS):void{
ns.tprint("run cmd [target(hostname)] -h 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();
if(!isBatchFilePackInstalled(ns)){
await installBatchFilePack(ns);
}
await scpBatchFilePack(ns,hostname);
const server = ns.getServer(target);
ns.tprint();
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
return [...data.servers]
}

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

@ -0,0 +1,45 @@
import { AutocompleteData, NS } from "@ns";
import {parse} from "./lib/flag";
/** @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;
ns.tprint(`scp /script/mining.js ${src}`);
await ns.scp("/script/mining.js",src);
let nThread;
if(th){
nThread = th;
}
else {
nThread = calculateMaximumThread(ns, src, ns.getScriptRam("/script/mining.js", src));
}
const pid = ns.exec("/script/mining.js",src,nThread,nThread,dest);
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];
}

17
src/get-cylce-size.ts Normal file
View File

@ -0,0 +1,17 @@
import { NS } from '@ns'
export async function main(ns : NS) : Promise<void> {
const stat: number[] = [];
for (let i = 0; i < 100; i++) {
const start = performance.now();
await ns.sleep(0);
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}`);
}

View File

@ -4,18 +4,24 @@ import {range} from "./util/range";
class Account { class Account {
value: number; value: number;
timer?: NodeJS.Timer;
constructor(value: number) { constructor(value: number) {
this.value = value; this.value = value;
this.timer = undefined;
} }
startIncrement(ns:NS, rate: number): void{ /**
this.timer = setInterval(()=>{ *
this.value += ns.hacknet.getNodeStats().production * rate; * @param ns NS
},1000); * @param rate reinvestment rate
* @param period the time since last update. it's millisecond unit.
*/
upgradeIncrement(ns: NS, rate: number, period: number): void {
const production = [...getIndexOfHackNode(ns)]
.map(x => ns.hacknet.getNodeStats(x).production)
.reduce((x, y) => x + y);
this.value += production * rate * (period / 1000);
} }
stopIncrement(): void{ formatedValue(ns: NS): string{
clearInterval(this.timer) return ns.nFormat(this.value,"$0.0000a");
} }
} }
@ -40,6 +46,32 @@ export interface CEInfo {
production: HacknetNodeComponent<number>; production: HacknetNodeComponent<number>;
costEffective: HacknetNodeComponent<number>; costEffective: HacknetNodeComponent<number>;
} }
interface Factor<T> {
cost: T;
production: T;
costEffective: T;
}
function CEInfoToFactor(ceinfo: CEInfo):HacknetNodeComponent<Factor<number>>{
return {
level:{
cost:ceinfo.upgradeCost.level,
production:ceinfo.production.level,
costEffective:ceinfo.costEffective.level,
},
core:{
cost:ceinfo.upgradeCost.core,
production:ceinfo.production.core,
costEffective:ceinfo.costEffective.core,
},
ram:{
cost:ceinfo.upgradeCost.ram,
production:ceinfo.production.ram,
costEffective:ceinfo.costEffective.ram,
}
}
}
function getCostEffective(ns: NS, nodeIndex: number, n: number): CEInfo { function getCostEffective(ns: NS, nodeIndex: number, n: number): CEInfo {
const coreUpgradeCost = ns.hacknet.getCoreUpgradeCost(nodeIndex, n); const coreUpgradeCost = ns.hacknet.getCoreUpgradeCost(nodeIndex, n);
@ -72,6 +104,8 @@ function getCostEffective(ns:NS, nodeIndex: number, n:number): CEInfo{
} }
} }
function createUpgradeAction(ns: NS, index: number, n: number, kind: ComponentKind): (account: Account) => void { function createUpgradeAction(ns: NS, index: number, n: number, kind: ComponentKind): (account: Account) => void {
return (account: Account) => { return (account: Account) => {
switch (kind) { switch (kind) {
@ -91,48 +125,55 @@ function createUpgradeAction(ns:NS, index: number, n: number, kind:ComponentKind
} }
} }
function getMinCENode(ns:NS, index: number, n: number):[number,ComponentKind]{ function getMaxCENode(ns: NS, index: number, n: number): [Factor<number>, ComponentKind] {
const {costEffective: ce} = getCostEffective(ns,index,n); const a = getCostEffective(ns, index, n);
if(ce.core < ce.level){ const b = CEInfoToFactor(a);
if(ce.core < ce.ram){ const ce = a.costEffective;
return [ce.core, "core"]; if (ce.core > ce.level) {
if (ce.core > ce.ram) {
return [b.core, "core"];
} }
else { else {
return [ce.ram, "ram"]; return [b.ram, "ram"];
} }
} }
else { else {
if(ce.level < ce.ram){ if (ce.level > ce.ram) {
return [ce.level,"level"]; return [b.level, "level"];
} }
else { else {
return [ce.ram,"ram"]; return [b.ram, "ram"];
} }
} }
} }
function* getIndexOfHackNode(ns: NS): Generator<number> { function* getIndexOfHackNode(ns: NS): Generator<number> {
for (const it of range(ns.hacknet.numNodes())) { const numNodes = ns.hacknet.numNodes()
for (const it of range(numNodes)) {
yield it; yield it;
} }
} }
function getMinCEIndex(ns:NS, n: number):[number,number,ComponentKind]{ function getMaxCEIndex(ns: NS, n: number): [number, Factor<number>, ComponentKind] {
let optimalC = Infinity; let optimalC: Factor<number> = {
let kind = ""; cost:0,
let minIndex = 0; production:0,
costEffective:0,
};
let kind: ComponentKind = "core";
let maxIndex = 0;
for (const it of getIndexOfHackNode(ns)) { for (const it of getIndexOfHackNode(ns)) {
const [c, k] = getMinCENode(ns,it,n); const [c, k] = getMaxCENode(ns, it, n);
if(c < optimalC){ if (c.costEffective > optimalC.costEffective) {
optimalC = c; optimalC = c;
kind = k; kind = k;
minIndex = it; maxIndex = it;
} }
} }
return [minIndex,optimalC,kind]; return [maxIndex, optimalC, kind];
} }
function getBuyNodeCE(ns:NS):{cost:number;production:number;costEffective: number}{ function getBuyNodeCE(ns: NS): Factor<number> {
const mult = ns.getHacknetMultipliers().production; const mult = ns.getHacknetMultipliers().production;
const bitMult = 1//ns.getBitNodeMultipliers().HacknetNodeMoney; const bitMult = 1//ns.getBitNodeMultipliers().HacknetNodeMoney;
const cost = ns.hacknet.maxNumNodes() > ns.hacknet.numNodes() ? ns.hacknet.getPurchaseNodeCost() : Infinity; const cost = ns.hacknet.maxNumNodes() > ns.hacknet.numNodes() ? ns.hacknet.getPurchaseNodeCost() : Infinity;
@ -140,39 +181,55 @@ function getBuyNodeCE(ns:NS):{cost:number;production:number;costEffective: numbe
return { return {
cost, cost,
production, production,
costEffective: cost/production, costEffective: production/cost,
}; };
} }
function cycle(ns: NS, account: Account): void { function cycle(ns: NS, account: Account): void {
const chunkN = 1; const chunkN = 1;
const [upgradeNodeIndex,upgradeNodeCE,kind] = getMinCEIndex(ns,chunkN); const [upgradeNodeIndex, upgradeNodeCE, kind] = getMaxCEIndex(ns, chunkN);
const {costEffective: buyNew} = getBuyNodeCE(ns); const { costEffective: buyNewCE, cost: buyNewCost } = getBuyNodeCE(ns);
if(upgradeNodeCE < buyNew){ if (upgradeNodeCE.costEffective > buyNewCE) {
if(account.value > upgradeNodeCE.cost){
ns.print(`upgrade ${kind} index ${upgradeNodeIndex} : CE ${upgradeNodeCE.costEffective}`);
const action = createUpgradeAction(ns, upgradeNodeIndex, chunkN, kind); const action = createUpgradeAction(ns, upgradeNodeIndex, chunkN, kind);
action(account); action(account);
ns.print(`budget ${account.formatedValue(ns)}`);
}
} }
else { else {
if (account.value > buyNewCost) {
ns.print(`purchase node : CE ${buyNewCE}`);
account.value -= buyNewCost;
ns.hacknet.purchaseNode() ns.hacknet.purchaseNode()
ns.print(`budget ${account.formatedValue(ns)}`);
}
} }
} }
// eslint-disable-next-line require-await // eslint-disable-next-line require-await
export async function main(ns: NS): Promise<void> { export async function main(ns: NS): Promise<void> {
ns.disableLog("ALL") ns.disableLog("ALL")
const flag = parse(ns.args); const flag = parse(ns.args.map(String));
if(flag.h || flag.help){
ns.tprint(`run cmd [-i or --iter] [-r or --rate] [-b --budget]`);
ns.tprint(`-i : cycle iterating count`);
ns.tprint(`-r : reinvestment rate`);
ns.tprint(`-b : initial budget`);
return;
}
const iter = parseInt(flag.i ?? flag.iter ?? "10"); const iter = parseInt(flag.i ?? flag.iter ?? "10");
const rate = parseFloat(flag.r ?? flag.rate ?? "0.5"); const rate = parseFloat(flag.r ?? flag.rate ?? "0.5");
const budget = parseInt(flag.b ?? flag.budget ?? "0"); const budget = parseInt(flag.b ?? flag.budget ?? "0");
const account = new Account(budget); const account = new Account(budget);
account.startIncrement(ns,rate);
ns.tail(); ns.tail();
const period = 1000;
for (let i = 0; i < iter; i++) { for (let i = 0; i < iter; i++) {
ns.clearLog() //ns.clearLog()
ns.print(`budget ${account.value}`);
cycle(ns, account); cycle(ns, account);
await ns.sleep(1000); await ns.sleep(period);
account.upgradeIncrement(ns,rate,period);
} }
} }

95
src/lib/batchbase.ts Normal file
View File

@ -0,0 +1,95 @@
import { NS } from "@ns"
type FilePackEntry = {
path: string;
content: string;
};
type FilePack = FilePackEntry[];
const createScriptContent: (f: string) => string = (funcName: string) => `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";
await ns.sleep(sleepDuration);
await ns.${funcName}(hostname,{
stock,
});
}`
async function unpackFileEntry(ns: NS, filePack: FilePackEntry): Promise<void> {
await ns.write(filePack.path, filePack.content, "w");
}
export async function unpackFilePack(ns: NS, pack: FilePack): Promise<void> {
for (const entry of pack) {
await unpackFileEntry(ns, entry);
}
}
export const batchFilePack: FilePack = [
{
path: "/tmp/hack.js",
content: createScriptContent("hack")
},
{
path: "/tmp/weaken.js",
content: createScriptContent("weaken")
},
{
path: "/tmp/grow.js",
content: createScriptContent("grow")
}
]
export async function scpBatchFilePack(ns: NS,hostname :string): Promise<void> {
const list = batchFilePack.map(entry=>entry.path);
await installBatchFilePack(ns);
await ns.scp(list,hostname);
}
export async function installBatchFilePack(ns: NS): Promise<void> {
await unpackFilePack(ns, batchFilePack);
}
export function isBatchFilePackInstalled(ns: NS, hostname?: string): boolean {
return batchFilePack.every(entry => ns.fileExists(entry.path, hostname))
}
export interface ExecOption{
hostname: string;
/**
* @default 1
*/
numThread?: number;
/**
* @default 0
*/
sleepDuration?: number;
/**
* target hostname to operate "hack", "weaken" or "grow"
*/
target: number;
stock?: boolean;
}
function execBatchfile(ns: NS, path: string, option:ExecOption): number {
return ns.exec(path, option.hostname, option.numThread,
option.sleepDuration ?? 0, option.target, option.stock ?? false);
}
export function execHack(ns: NS,option: ExecOption): number{
return execBatchfile(ns,"/tmp/hack.js",option);
}
export function execGrow(ns: NS,option: ExecOption): number{
return execBatchfile(ns,"/tmp/grow.js",option);
}
export function execWeaken(ns: NS,option: ExecOption): number{
return execBatchfile(ns,"/tmp/weaken.js",option);
}

229
src/lib/contract.ts Normal file
View File

@ -0,0 +1,229 @@
import { NS } from "@ns";
import { subarrayMaxSolve } from "./solve/submaxarr";
import { largestPrimeSolve } from "./solve/largestPrime";
import { spiralSolve } from "./solve/spiral";
import { arrayJumpSolve } from "./solve/arrayJump";
import { arrayJump2Solve } from "./solve/arrayJump2";
import { minimumTrianglePathSumSolve} from "./solve/minsumTriangle"
import { graph2coloringSolve } from "./solve/graph2color";
import { shortestPathInGridSolve } from "./solve/shortestPathInGrid";
import { algorithmicTradeIIISolve, algorithmicTradeIISolve, algorithmicTradeISolve, algorithmicTradeIVSolve } from "./solve/algorithmicTrader";
import {selectAllServerList} from "lib/servers";
import { logFile } from "./log";
import { SolveFailError } from "./solve/unsolved";
import { RLECompressionSolve, decompressLZSolve, compressLZSolve } from "./solve/compression";
export { SolveFailError } from "./solve/unsolved";
export interface ContractSolver{
name: string;
solve(n :any): string[] | number;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const ContractMAP: {[key:string]:ContractSolver} = {
/**
* A prime factor is a factor that is a prime number.
* What is the largest prime factor of the given number?
*/
"Find Largest Prime Factor": {
name: "Find Largest Prime Factor",
solve: largestPrimeSolve
},
/**
* Given the following integer array, find the contiguous subarray
* (containing at least one number) which has the largest sum and return that sum.
* 'Sum' refers to the sum of all the numbers in the subarray.
*/
"Subarray with Maximum Sum": {
name: "Subarray with Maximum Sum",
solve: subarrayMaxSolve,
},
/*"Total Ways to Sum": {
name: "Total Ways to Sum",
solve() {
notImplemented();
}
},
"Total Ways to Sum II": {
name: "Total Ways to Sum II",
solve() {
notImplemented();
}
},*/
"Spiralize Matrix": {
name: "Spiralize Matrix",
solve: spiralSolve,
},
"Array Jumping Game": {
name: "Array Jumping Game",
solve: arrayJumpSolve,
},
"Array Jumping Game II": {
name: "Array Jumping Game II",
solve: arrayJump2Solve
},/*
"Merge Overlapping Intervals": {
name: "Merge Overlapping Intervals",
solve() {
notImplemented();
}
},
"Generate IP Addresses": {
name: "Generate IP Addresses",
solve() {
notImplemented();
}
},*/
"Algorithmic Stock Trader I": {
name: "Algorithmic Stock Trader I",
solve: algorithmicTradeISolve,
},
"Algorithmic Stock Trader II": {
name: "Algorithmic Stock Trader II",
solve: algorithmicTradeIISolve,
},
"Algorithmic Stock Trader III": {
name: "Algorithmic Stock Trader III",
solve: algorithmicTradeIIISolve
},
"Algorithmic Stock Trader IV": {
name: "Algorithmic Stock Trader IV",
solve: algorithmicTradeIVSolve
},
"Minimum Path Sum in a Triangle": {
name: "Minimum Path Sum in a Triangle",
solve: minimumTrianglePathSumSolve,
},/*
"Unique Paths in a Grid I": {
name: "Unique Paths in a Grid I",
solve() {
notImplemented();
}
},
"Unique Paths in a Grid II": {
name: "Unique Paths in a Grid II",
solve() {
notImplemented();
}
},*/
"Shortest Path in a Grid": {
name: "Shortest Path in a Grid",
solve: shortestPathInGridSolve
},/*
"Sanitize Parentheses in Expression": {
name: "Sanitize Parentheses in Expression",
solve() {
notImplemented();
}
},
"Find All Valid Math Expressions": {
name: "Find All Valid Math Expressions",
solve() {
notImplemented();
}
},
"HammingCodes: Integer to Encoded Binary": {
name: "HammingCodes: Integer to Encoded Binary",
solve() {
notImplemented();
}
},
"HammingCodes: Encoded Binary to Integer": {
name: "HammingCodes: Encoded Binary to Integer",
solve() {
notImplemented();
}
},*/
"Proper 2-Coloring of a Graph": {
name: "Proper 2-Coloring of a Graph",
solve: graph2coloringSolve
},
"Compression I: RLE Compression": {
name: "Compression I: RLE Compression",
solve: RLECompressionSolve,
},
"Compression II: LZ Decompression": {
name: "Compression II: LZ Decompression",
solve: decompressLZSolve,
},
"Compression III: LZ Compression": {
name: "Compression III: LZ Compression",
solve: compressLZSolve,
},/*
"Encryption I: Caesar Cipher": {
name: "Encryption I: Caesar Cipher",
solve() {
notImplemented();
}
},
"Encryption II: Vigenère Cipher": {
name: "Encryption II: Vigenère Cipher",
solve() {
notImplemented();
}
}*/
}
export function isSolvable(ns:NS,filename: string, hostname: string): boolean {
const ct = ns.codingcontract;
const type = ct.getContractType(filename,hostname);
return (type in ContractMAP)
}
export async function solveContract(ns:NS,filename: string, hostname: string): Promise<[boolean,string]>{
const ct = ns.codingcontract;
const type = ct.getContractType(filename,hostname);
if(type in ContractMAP){
const solver = ContractMAP[type];
const data = ct.getData(filename,hostname);
let ans: number|string[];
try {
ans = solver.solve(data);
} catch (error) {
if(error instanceof SolveFailError){
ns.print(`[ERROR] failed to solve problem ${type}. throw SolveFailError`);
ns.toast(`failed to solve problem ${type}`,"error");
await logFile(ns,type+"-Fail",data);
return [false,""];
}
else{
throw error;
}
}
const reward = ct.attempt(ans,filename,hostname,{returnReward:true});
if(reward === ""){
ns.print(`[ERROR] failed to solve problem ${type}`);
ns.toast(`failed to solve problem ${type}`,"error");
await logFile(ns,type,[data,ans]);
return [false,""];
}
else {
ns.print(`success to solve.\n${reward}`);
}
return [true,reward.toString()];
}
return [false,""];
}
export interface RemoteFilePath{
hostname: string;
filename: string;
}
export function getContractList(ns: NS, hostname: string): RemoteFilePath[]{
const ctFilenameList = ns.ls(hostname,".cct").filter(x=>x.endsWith(".cct"));
return ctFilenameList.map(filename=>({
hostname: hostname,
filename: filename
}));
}
export function selectAllContract(ns: NS): RemoteFilePath[] {
const serverList = selectAllServerList(ns);
const ctList = serverList.map(server=>getContractList(ns,server.hostname))
.flat();
return ctList;
}

View File

@ -89,6 +89,7 @@ function get<T>(obj: Record<string, T>, key: string): T | undefined {
if (hasOwn(obj, key)) { if (hasOwn(obj, key)) {
return obj[key]; return obj[key];
} }
return undefined;
} }
function getForce<T>(obj: Record<string, T>, key: string): T { function getForce<T>(obj: Record<string, T>, key: string): T {

146
src/lib/formula.ts Normal file
View File

@ -0,0 +1,146 @@
import { Server, Player } from "@ns";
const CONSTANTS = {
ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate
ServerMaxGrowthRate: 1.0035, // Maximum possible growth rate (max rate accounting for server security)
ServerFortifyAmount: 0.002, // Amount by which server's security increases when its hacked/grown
ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened
}
const BitNodeMultipliers = {
HackingLevelMultiplier: 1,
ServerGrowthRate: 1,
ServerMaxMoney: 1,
ServerStartingMoney: 1,
ServerStartingSecurity: 1,
ServerWeakenRate: 1,
HackExpGain: 1,
ScriptHackMoney: 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 {
const numServerGrowthCycles = Math.max(Math.floor(threads), 0);
//Get adjusted growth rate, which accounts for server security
const growthRate = CONSTANTS.ServerBaseGrowthRate;
let adjGrowthRate = 1 + (growthRate - 1) / server.hackDifficulty;
if (adjGrowthRate > CONSTANTS.ServerMaxGrowthRate) {
adjGrowthRate = CONSTANTS.ServerMaxGrowthRate;
}
//Calculate adjusted server growth rate based on parameters
const serverGrowthPercentage = server.serverGrowth / 100;
const numServerGrowthCyclesAdjusted =
numServerGrowthCycles * serverGrowthPercentage * BitNodeMultipliers.ServerGrowthRate;
//Apply serverGrowth for the calculated number of growth cycles
const coreBonus = 1 + (cores - 1) / 16;
return Math.pow(adjGrowthRate, numServerGrowthCyclesAdjusted * p.mults.hacking_grow * coreBonus);
}
/**
* Returns the chance the player has to successfully hack a server
*/
export function calculateHackingChance(server: Server, player: Player): number {
const hackFactor = 1.75;
const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = hackFactor * player.skills.hacking;
const skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
const chance =
skillChance *
difficultyMult *
player.mults.hacking_chance *
calculateIntelligenceBonus(player.skills.intelligence, 1);
if (chance > 1) {
return 1;
}
if (chance < 0) {
return 0;
}
return chance;
}
/**
* Returns the amount of hacking experience the player will gain upon
* successfully hacking a server
*/
export function calculateHackingExpGain(server: Server, player: Player): number {
const baseExpGain = 3;
const diffFactor = 0.3;
if (server.baseDifficulty == null) {
server.baseDifficulty = server.hackDifficulty;
}
let expGain = baseExpGain;
expGain += server.baseDifficulty * diffFactor;
return expGain * player.mults.hacking_exp * BitNodeMultipliers.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 {
// 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;
if (percentMoneyHacked < 0) {
return 0;
}
if (percentMoneyHacked > 1) {
return 1;
}
return percentMoneyHacked;
}
/**
* Returns time it takes to complete a hack on a server, in seconds
*/
export function calculateHackingTime(server: Server, player: Player): number {
const difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
const baseDiff = 500;
const baseSkill = 50;
const diffFactor = 2.5;
let skillFactor = diffFactor * difficultyMult + baseDiff;
// tslint:disable-next-line
skillFactor /= player.skills.hacking + baseSkill;
const hackTimeMultiplier = 5;
const hackingTime =
(hackTimeMultiplier * skillFactor) /
(player.mults.hacking_speed * calculateIntelligenceBonus(player.skills.intelligence, 1));
return hackingTime;
}
/**
* Returns time it takes to complete a grow operation on a server, in seconds
*/
export function calculateGrowTime(server: Server, player: Player): number {
const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2
return growTimeMultiplier * calculateHackingTime(server, player);
}
/**
* Returns time it takes to complete a weaken operation on a server, in seconds
*/
export function calculateWeakenTime(server: Server, player: Player): number {
const weakenTimeMultiplier = 4; // Relative to hacking time
return weakenTimeMultiplier * calculateHackingTime(server, player);
}

295
src/lib/fuse.esm.d.ts vendored Normal file
View File

@ -0,0 +1,295 @@
// Type definitions for Fuse.js v6.4.1
// TypeScript v3.9.5
export default Fuse
export as namespace Fuse
declare class Fuse<T> {
constructor(
list: readonly T[],
options?: Fuse.IFuseOptions<T>,
index?: FuseIndex<T>
)
/**
* Search function for the Fuse instance.
*
* ```typescript
* const list: MyType[] = [myType1, myType2, etc...]
* const options: Fuse.IFuseOptions<MyType> = {
* keys: ['key1', 'key2']
* }
*
* const myFuse = new Fuse(list, options)
* let result = myFuse.search('pattern')
* ```
*
* @param pattern The pattern to search
* @param options `Fuse.FuseSearchOptions`
* @returns An array of search results
*/
search<R = T>(
pattern: string | Fuse.Expression,
options?: Fuse.FuseSearchOptions
): Array<Fuse.FuseResult<R>>
setCollection(docs: readonly T[], index?: FuseIndex<T>): void
/**
* Adds a doc to the end the list.
*/
add(doc: T): void
/**
* Removes all documents from the list which the predicate returns truthy for,
* and returns an array of the removed docs.
* The predicate is invoked with two arguments: (doc, index).
*/
remove(predicate: (doc: T, idx: number) => boolean): T[]
/**
* Removes the doc at the specified index.
*/
removeAt(idx: number): void
/**
* Returns the generated Fuse index
*/
getIndex(): FuseIndex<T>
/**
* Return the current version.
*/
// eslint-disable-next-line @typescript-eslint/member-ordering
static version: string
/**
* Use this method to pre-generate the index from the list, and pass it
* directly into the Fuse instance.
*
* _Note that Fuse will automatically index the table if one isn't provided
* during instantiation._
*
* ```typescript
* const list: MyType[] = [myType1, myType2, etc...]
*
* const index = Fuse.createIndex<MyType>(
* keys: ['key1', 'key2']
* list: list
* )
*
* const options: Fuse.IFuseOptions<MyType> = {
* keys: ['key1', 'key2']
* }
*
* const myFuse = new Fuse(list, options, index)
* ```
* @param keys The keys to index
* @param list The list from which to create an index
* @param options?
* @returns An indexed list
*/
static createIndex<U>(
keys: Fuse.FuseOptionKey[],
list: readonly U[],
options?: Fuse.FuseIndexOptions<U>
): FuseIndex<U>
static parseIndex<U>(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
index: any,
options?: Fuse.FuseIndexOptions<U>
): FuseIndex<U>
}
declare class FuseIndex<T> {
constructor(options?: Fuse.FuseIndexOptions<T>)
setSources(docs: readonly T[]): void
setKeys(keys: readonly string[]): void
setIndexRecords(records: Fuse.FuseIndexRecords): void
create(): void
add(doc: T): void
toJSON(): {
keys: readonly string[];
collection: Fuse.FuseIndexRecords;
}
}
declare namespace Fuse {
type FuseGetFunction<T> = (
obj: T,
path: string | string[]
) => readonly string[] | string
export type FuseIndexOptions<T> = {
getFn: FuseGetFunction<T>;
}
// {
// title: { '$': "Old Man's War" },
// 'author.firstName': { '$': 'Codenar' }
// }
//
// OR
//
// {
// tags: [
// { $: 'nonfiction', idx: 0 },
// { $: 'web development', idx: 1 },
// ]
// }
export type FuseSortFunctionItem = {
[key: string]: { $: string } | Array<{ $: string; idx: number }>;
}
// {
// score: 0.001,
// key: 'author.firstName',
// value: 'Codenar',
// indices: [ [ 0, 3 ] ]
// }
export type FuseSortFunctionMatch = {
score: number;
key: string;
value: string;
indices: Array<readonly number[]>;
}
// {
// score: 0,
// key: 'tags',
// value: 'nonfiction',
// idx: 1,
// indices: [ [ 0, 9 ] ]
// }
export type FuseSortFunctionMatchList = FuseSortFunctionMatch & {
idx: number;
}
export type FuseSortFunctionArg = {
idx: number;
item: FuseSortFunctionItem;
score: number;
matches?: Array<FuseSortFunctionMatch | FuseSortFunctionMatchList>;
}
export type FuseSortFunction = (
a: FuseSortFunctionArg,
b: FuseSortFunctionArg
) => number
// title: {
// '$': "Old Man's War",
// 'n': 0.5773502691896258
// }
type RecordEntryObject = {
v: string; // The text value
n: number; // The field-length norm
}
// 'author.tags.name': [{
// 'v': 'pizza lover',
// 'i': 2,
// 'n: 0.7071067811865475
// }
type RecordEntryArrayItem = ReadonlyArray<RecordEntryObject & { i: number }>
// TODO: this makes it difficult to infer the type. Need to think more about this
type RecordEntry = { [key: string]: RecordEntryObject | RecordEntryArrayItem }
// {
// i: 0,
// '$': {
// '0': { v: "Old Man's War", n: 0.5773502691896258 },
// '1': { v: 'Codenar', n: 1 },
// '2': [
// { v: 'pizza lover', i: 2, n: 0.7071067811865475 },
// { v: 'helo wold', i: 1, n: 0.7071067811865475 },
// { v: 'hello world', i: 0, n: 0.7071067811865475 }
// ]
// }
// }
type FuseIndexObjectRecord = {
i: number; // The index of the record in the source list
$: RecordEntry;
}
// {
// keys: null,
// list: [
// { v: 'one', i: 0, n: 1 },
// { v: 'two', i: 1, n: 1 },
// { v: 'three', i: 2, n: 1 }
// ]
// }
type FuseIndexStringRecord = {
i: number; // The index of the record in the source list
v: string; // The text value
n: number; // The field-length norm
}
type FuseIndexRecords =
| readonly FuseIndexObjectRecord[]
| readonly FuseIndexStringRecord[]
// {
// name: 'title',
// weight: 0.7
// }
export type FuseOptionKeyObject = {
name: string | string[];
weight: number;
}
export type FuseOptionKey = FuseOptionKeyObject | string | string[]
export interface IFuseOptions<T> {
isCaseSensitive?: boolean;
distance?: number;
findAllMatches?: boolean;
getFn?: FuseGetFunction<T>;
ignoreLocation?: boolean;
ignoreFieldNorm?: boolean;
includeMatches?: boolean;
includeScore?: boolean;
keys?: FuseOptionKey[];
location?: number;
minMatchCharLength?: number;
shouldSort?: boolean;
sortFn?: FuseSortFunction;
threshold?: number;
useExtendedSearch?: boolean;
}
// Denotes the start/end indices of a match
// start end
// ↓ ↓
type RangeTuple = [number, number]
export type FuseResultMatch = {
indices: readonly RangeTuple[];
key?: string;
refIndex?: number;
value?: string;
}
export type FuseSearchOptions = {
limit: number;
}
export type FuseResult<T> = {
item: T;
refIndex: number;
score?: number;
matches?: readonly FuseResultMatch[];
}
export type Expression =
| { [key: string]: string }
| {
$path: readonly string[];
$val: string;
}
| { $and?: Expression[] }
| { $or?: Expression[] }
}

1780
src/lib/fuse.esm.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
import { NS, Server } from '@ns' import { NS, Server, AutocompleteData} from '@ns'
import {selectAllServerList, ServerInfo} from "./servers" import {selectAllServerList, ServerInfo} from "./servers"
export type Hackability = "hackable" | "rooted" | "impossible" export type Hackability = "hackable" | "rooted" | "impossible"
@ -52,7 +52,9 @@ export const hacks: Hack[] = [
* @param {NS} ns * @param {NS} ns
*/ */
export function hackablePorts(ns: NS): number { export function hackablePorts(ns: NS): number {
return hacks.map(x=>(ns.fileExists(x.file) ? 1 : 0)).reduce((x,y)=>x+y) function add(x:number,y:number):number{return x+y;}
return hacks.map(x=>(ns.fileExists(x.file) ? 1 : 0) as number)
.reduce(add);
} }
export function isHackableServer(ns: NS, target: Server): boolean{ export function isHackableServer(ns: NS, target: Server): boolean{
@ -65,8 +67,8 @@ export function selectHackableServerList(ns: NS): ServerInfo[]{
}) })
} }
export function hackServer(ns:NS,target:Server); export function hackServer(ns:NS,target:Server):void;
export function hackServer(ns:NS,target:string); export function hackServer(ns:NS,target:string):void;
export function hackServer(ns:NS,target:string|Server):void{ export function hackServer(ns:NS,target:string|Server):void{
const hostname = typeof target === "string" ? target : target.hostname const hostname = typeof target === "string" ? target : target.hostname
hacks.forEach(x=>{ hacks.forEach(x=>{
@ -85,11 +87,11 @@ export async function main(ns: NS):Promise<void> {
ns.tprint("run cmd [target]") ns.tprint("run cmd [target]")
return return
} }
const target = ns.args[0] const target = String(ns.args[0]);
hackServer(target) hackServer(ns,target)
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
export function autocomplete(data : ServerData, args : string[]) : string[] { export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
return [...data.servers] return [...data.servers]
} }

13
src/lib/log.ts Normal file
View File

@ -0,0 +1,13 @@
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`;
const loggedData = JSON.stringify(data);
if(ns.fileExists(logFilename)){
await ns.write(logFilename,"\n"+loggedData,"a");
}
else {
await ns.write(logFilename,loggedData,"w");
}
}

778
src/lib/printf.ts Normal file
View File

@ -0,0 +1,778 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
/**
* This implementation is inspired by POSIX and Golang but does not port
* implementation code. */
enum State {
PASSTHROUGH,
PERCENT,
POSITIONAL,
PRECISION,
WIDTH,
}
enum WorP {
WIDTH,
PRECISION,
}
class Flags {
plus?: boolean;
dash?: boolean;
sharp?: boolean;
space?: boolean;
zero?: boolean;
lessthan?: boolean;
width = -1;
precision = -1;
}
const min = Math.min;
const UNICODE_REPLACEMENT_CHARACTER = "\ufffd";
const DEFAULT_PRECISION = 6;
const FLOAT_REGEXP = /(-?)(\d)\.?(\d*)e([+-])(\d+)/;
enum F {
sign = 1,
mantissa,
fractional,
esign,
exponent,
}
class Printf {
format: string;
args: unknown[];
i: number;
state: State = State.PASSTHROUGH;
verb = "";
buf = "";
argNum = 0;
flags: Flags = new Flags();
haveSeen: boolean[];
// barf, store precision and width errors for later processing ...
tmpError?: string;
constructor(format: string, ...args: unknown[]) {
this.format = format;
this.args = args;
this.haveSeen = Array.from({ length: args.length });
this.i = 0;
}
doPrintf(): string {
for (; this.i < this.format.length; ++this.i) {
const c = this.format[this.i];
switch (this.state) {
case State.PASSTHROUGH:
if (c === "%") {
this.state = State.PERCENT;
} else {
this.buf += c;
}
break;
case State.PERCENT:
if (c === "%") {
this.buf += c;
this.state = State.PASSTHROUGH;
} else {
this.handleFormat();
}
break;
default:
throw Error("Should be unreachable, certainly a bug in the lib.");
}
}
// check for unhandled args
let extras = false;
let err = "%!(EXTRA";
for (let i = 0; i !== this.haveSeen.length; ++i) {
if (!this.haveSeen[i]) {
extras = true;
err += ` '${typeof (this.args[i])}'`;
}
}
err += ")";
if (extras) {
this.buf += err;
}
return this.buf;
}
// %[<positional>]<flag>...<verb>
handleFormat(): void {
this.flags = new Flags();
const flags = this.flags;
for (; this.i < this.format.length; ++this.i) {
const c = this.format[this.i];
switch (this.state) {
case State.PERCENT:
switch (c) {
case "[":
this.handlePositional();
this.state = State.POSITIONAL;
break;
case "+":
flags.plus = true;
break;
case "<":
flags.lessthan = true;
break;
case "-":
flags.dash = true;
flags.zero = false; // only left pad zeros, dash takes precedence
break;
case "#":
flags.sharp = true;
break;
case " ":
flags.space = true;
break;
case "0":
// only left pad zeros, dash takes precedence
flags.zero = !flags.dash;
break;
default:
if (("1" <= c && c <= "9") || c === "." || c === "*") {
if (c === ".") {
this.flags.precision = 0;
this.state = State.PRECISION;
this.i++;
} else {
this.state = State.WIDTH;
}
this.handleWidthAndPrecision(flags);
} else {
this.handleVerb();
return; // always end in verb
}
} // switch c
break;
case State.POSITIONAL:
// TODO(bartlomieju): either a verb or * only verb for now
if (c === "*") {
const worp = this.flags.precision === -1
? WorP.WIDTH
: WorP.PRECISION;
this.handleWidthOrPrecisionRef(worp);
this.state = State.PERCENT;
break;
} else {
this.handleVerb();
return; // always end in verb
}
default:
throw new Error(`Should not be here ${this.state}, library bug!`);
} // switch state
}
}
/**
* Handle width or precision
* @param wOrP
*/
handleWidthOrPrecisionRef(wOrP: WorP): void {
if (this.argNum >= this.args.length) {
// handle Positional should have already taken care of it...
return;
}
const arg = this.args[this.argNum];
this.haveSeen[this.argNum] = true;
if (typeof arg === "number") {
switch (wOrP) {
case WorP.WIDTH:
this.flags.width = arg;
break;
default:
this.flags.precision = arg;
}
} else {
const tmp = wOrP === WorP.WIDTH ? "WIDTH" : "PREC";
this.tmpError = `%!(BAD ${tmp} '${this.args[this.argNum]}')`;
}
this.argNum++;
}
/**
* Handle width and precision
* @param flags
*/
handleWidthAndPrecision(flags: Flags): void {
const fmt = this.format;
for (; this.i !== this.format.length; ++this.i) {
const c = fmt[this.i];
switch (this.state) {
case State.WIDTH:
switch (c) {
case ".":
// initialize precision, %9.f -> precision=0
this.flags.precision = 0;
this.state = State.PRECISION;
break;
case "*":
this.handleWidthOrPrecisionRef(WorP.WIDTH);
// force . or flag at this point
break;
default: {
const val = parseInt(c);
// most likely parseInt does something stupid that makes
// it unusable for this scenario ...
// if we encounter a non (number|*|.) we're done with prec & wid
if (isNaN(val)) {
this.i--;
this.state = State.PERCENT;
return;
}
flags.width = flags.width == -1 ? 0 : flags.width;
flags.width *= 10;
flags.width += val;
}
} // switch c
break;
case State.PRECISION: {
if (c === "*") {
this.handleWidthOrPrecisionRef(WorP.PRECISION);
break;
}
const val = parseInt(c);
if (isNaN(val)) {
// one too far, rewind
this.i--;
this.state = State.PERCENT;
return;
}
flags.precision *= 10;
flags.precision += val;
break;
}
default:
throw new Error("can't be here. bug.");
} // switch state
}
}
/** Handle positional */
handlePositional(): void {
if (this.format[this.i] !== "[") {
// sanity only
throw new Error("Can't happen? Bug.");
}
let positional = 0;
const format = this.format;
this.i++;
let err = false;
for (; this.i !== this.format.length; ++this.i) {
if (format[this.i] === "]") {
break;
}
positional *= 10;
const val = parseInt(format[this.i]);
if (isNaN(val)) {
//throw new Error(
// `invalid character in positional: ${format}[${format[this.i]}]`
//);
this.tmpError = "%!(BAD INDEX)";
err = true;
}
positional += val;
}
if (positional - 1 >= this.args.length) {
this.tmpError = "%!(BAD INDEX)";
err = true;
}
this.argNum = err ? this.argNum : positional - 1;
return;
}
/** Handle less than */
handleLessThan(): string {
// deno-lint-ignore no-explicit-any
const arg = this.args[this.argNum] as any;
if ((arg || {}).constructor.name !== "Array") {
throw new Error(`arg ${arg} is not an array. Todo better error handling`);
}
let str = "[ ";
for (let i = 0; i !== arg.length; ++i) {
if (i !== 0) str += ", ";
str += this._handleVerb(arg[i]);
}
return str + " ]";
}
/** Handle verb */
handleVerb(): void {
const verb = this.format[this.i];
this.verb = verb;
if (this.tmpError) {
this.buf += this.tmpError;
this.tmpError = undefined;
if (this.argNum < this.haveSeen.length) {
this.haveSeen[this.argNum] = true; // keep track of used args
}
} else if (this.args.length <= this.argNum) {
this.buf += `%!(MISSING '${verb}')`;
} else {
const arg = this.args[this.argNum]; // check out of range
this.haveSeen[this.argNum] = true; // keep track of used args
if (this.flags.lessthan) {
this.buf += this.handleLessThan();
} else {
this.buf += this._handleVerb(arg);
}
}
this.argNum++; // if there is a further positional, it will reset.
this.state = State.PASSTHROUGH;
}
// deno-lint-ignore no-explicit-any
_handleVerb(arg: any): string {
switch (this.verb) {
case "t":
return this.pad(arg.toString());
case "b":
return this.fmtNumber(arg as number, 2);
case "c":
return this.fmtNumberCodePoint(arg as number);
case "d":
return this.fmtNumber(arg as number, 10);
case "o":
return this.fmtNumber(arg as number, 8);
case "x":
return this.fmtHex(arg);
case "X":
return this.fmtHex(arg, true);
case "e":
return this.fmtFloatE(arg as number);
case "E":
return this.fmtFloatE(arg as number, true);
case "f":
case "F":
return this.fmtFloatF(arg as number);
case "g":
return this.fmtFloatG(arg as number);
case "G":
return this.fmtFloatG(arg as number, true);
case "s":
return this.fmtString(arg as string);
case "T":
return this.fmtString(typeof arg);
case "v":
return this.fmtV(arg);
case "j":
return this.fmtJ(arg);
default:
return `%!(BAD VERB '${this.verb}')`;
}
}
/**
* Pad a string
* @param s text to pad
*/
pad(s: string): string {
const padding = this.flags.zero ? "0" : " ";
if (this.flags.dash) {
return s.padEnd(this.flags.width, padding);
}
return s.padStart(this.flags.width, padding);
}
/**
* Pad a number
* @param nStr
* @param neg
*/
padNum(nStr: string, neg: boolean): string {
let sign: string;
if (neg) {
sign = "-";
} else if (this.flags.plus || this.flags.space) {
sign = this.flags.plus ? "+" : " ";
} else {
sign = "";
}
const zero = this.flags.zero;
if (!zero) {
// sign comes in front of padding when padding w/ zero,
// in from of value if padding with spaces.
nStr = sign + nStr;
}
const pad = zero ? "0" : " ";
const len = zero ? this.flags.width - sign.length : this.flags.width;
if (this.flags.dash) {
nStr = nStr.padEnd(len, pad);
} else {
nStr = nStr.padStart(len, pad);
}
if (zero) {
// see above
nStr = sign + nStr;
}
return nStr;
}
/**
* Format a number
* @param n
* @param radix
* @param upcase
*/
fmtNumber(n: number, radix: number, upcase = false): string {
let num = Math.abs(n).toString(radix);
const prec = this.flags.precision;
if (prec !== -1) {
this.flags.zero = false;
num = n === 0 && prec === 0 ? "" : num;
while (num.length < prec) {
num = "0" + num;
}
}
let prefix = "";
if (this.flags.sharp) {
switch (radix) {
case 2:
prefix += "0b";
break;
case 8:
// don't annotate octal 0 with 0...
prefix += num.startsWith("0") ? "" : "0";
break;
case 16:
prefix += "0x";
break;
default:
throw new Error("cannot handle base: " + radix);
}
}
// don't add prefix in front of value truncated by precision=0, val=0
num = num.length === 0 ? num : prefix + num;
if (upcase) {
num = num.toUpperCase();
}
return this.padNum(num, n < 0);
}
/**
* Format number with code points
* @param n
*/
fmtNumberCodePoint(n: number): string {
let s = "";
try {
s = String.fromCodePoint(n);
} catch {
s = UNICODE_REPLACEMENT_CHARACTER;
}
return this.pad(s);
}
/**
* Format special float
* @param n
*/
fmtFloatSpecial(n: number): string {
// formatting of NaN and Inf are pants-on-head
// stupid and more or less arbitrary.
if (isNaN(n)) {
this.flags.zero = false;
return this.padNum("NaN", false);
}
if (n === Number.POSITIVE_INFINITY) {
this.flags.zero = false;
this.flags.plus = true;
return this.padNum("Inf", false);
}
if (n === Number.NEGATIVE_INFINITY) {
this.flags.zero = false;
return this.padNum("Inf", true);
}
return "";
}
/**
* Round fraction to precision
* @param fractional
* @param precision
* @returns tuple of fractional and round
*/
roundFractionToPrecision(
fractional: string,
precision: number,
): [string, boolean] {
let round = false;
if (fractional.length > precision) {
fractional = "1" + fractional; // prepend a 1 in case of leading 0
let tmp = parseInt(fractional.substr(0, precision + 2)) / 10;
tmp = Math.round(tmp);
fractional = Math.floor(tmp).toString();
round = fractional[0] === "2";
fractional = fractional.substr(1); // remove extra 1
} else {
while (fractional.length < precision) {
fractional += "0";
}
}
return [fractional, round];
}
/**
* Format float E
* @param n
* @param upcase
*/
fmtFloatE(n: number, upcase = false): string {
const special = this.fmtFloatSpecial(n);
if (special !== "") {
return special;
}
const m = n.toExponential().match(FLOAT_REGEXP);
if (!m) {
throw Error("can't happen, bug");
}
let fractional = m[F.fractional];
const precision = this.flags.precision !== -1
? this.flags.precision
: DEFAULT_PRECISION;
let rounding = false;
[fractional, rounding] = this.roundFractionToPrecision(
fractional,
precision,
);
let e = m[F.exponent];
let esign = m[F.esign];
// scientific notation output with exponent padded to minlen 2
let mantissa = parseInt(m[F.mantissa]);
if (rounding) {
mantissa += 1;
if (10 <= mantissa) {
mantissa = 1;
const r = parseInt(esign + e) + 1;
e = r.toString();
esign = r < 0 ? "-" : "+";
}
}
e = e.length == 1 ? "0" + e : e;
const val = `${mantissa}.${fractional}${upcase ? "E" : "e"}${esign}${e}`;
return this.padNum(val, n < 0);
}
/**
* Format float F
* @param n
*/
fmtFloatF(n: number): string {
const special = this.fmtFloatSpecial(n);
if (special !== "") {
return special;
}
// stupid helper that turns a number into a (potentially)
// VERY long string.
function expandNumber(n: number): string {
if (Number.isSafeInteger(n)) {
return n.toString() + ".";
}
const t = n.toExponential().split("e");
let m = t[0].replace(".", "");
const e = parseInt(t[1]);
if (e < 0) {
let nStr = "0.";
for (let i = 0; i !== Math.abs(e) - 1; ++i) {
nStr += "0";
}
return (nStr += m);
} else {
const splIdx = e + 1;
while (m.length < splIdx) {
m += "0";
}
return m.substr(0, splIdx) + "." + m.substr(splIdx);
}
}
// avoiding sign makes padding easier
const val = expandNumber(Math.abs(n)) as string;
const arr = val.split(".");
let dig = arr[0];
let fractional = arr[1];
const precision = this.flags.precision !== -1
? this.flags.precision
: DEFAULT_PRECISION;
let round = false;
[fractional, round] = this.roundFractionToPrecision(fractional, precision);
if (round) {
dig = (parseInt(dig) + 1).toString();
}
return this.padNum(`${dig}.${fractional}`, n < 0);
}
/**
* Format float G
* @param n
* @param upcase
*/
fmtFloatG(n: number, upcase = false): string {
const special = this.fmtFloatSpecial(n);
if (special !== "") {
return special;
}
// The double argument representing a floating-point number shall be
// converted in the style f or e (or in the style F or E in
// the case of a G conversion specifier), depending on the
// value converted and the precision. Let P equal the
// precision if non-zero, 6 if the precision is omitted, or 1
// if the precision is zero. Then, if a conversion with style E would
// have an exponent of X:
// - If P > X>=-4, the conversion shall be with style f (or F )
// and precision P -( X+1).
// - Otherwise, the conversion shall be with style e (or E )
// and precision P -1.
// Finally, unless the '#' flag is used, any trailing zeros shall be
// removed from the fractional portion of the result and the
// decimal-point character shall be removed if there is no
// fractional portion remaining.
// A double argument representing an infinity or NaN shall be
// converted in the style of an f or F conversion specifier.
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
let P = this.flags.precision !== -1
? this.flags.precision
: DEFAULT_PRECISION;
P = P === 0 ? 1 : P;
const m = n.toExponential().match(FLOAT_REGEXP);
if (!m) {
throw Error("can't happen");
}
const X = parseInt(m[F.exponent]) * (m[F.esign] === "-" ? -1 : 1);
let nStr = "";
if (P > X && X >= -4) {
this.flags.precision = P - (X + 1);
nStr = this.fmtFloatF(n);
if (!this.flags.sharp) {
nStr = nStr.replace(/\.?0*$/, "");
}
} else {
this.flags.precision = P - 1;
nStr = this.fmtFloatE(n);
if (!this.flags.sharp) {
nStr = nStr.replace(/\.?0*e/, upcase ? "E" : "e");
}
}
return nStr;
}
/**
* Format string
* @param s
*/
fmtString(s: string): string {
if (this.flags.precision !== -1) {
s = s.substr(0, this.flags.precision);
}
return this.pad(s);
}
/**
* Format hex
* @param val
* @param upper
*/
fmtHex(val: string | number, upper = false): string {
// allow others types ?
switch (typeof val) {
case "number":
return this.fmtNumber(val as number, 16, upper);
case "string": {
const sharp = this.flags.sharp && val.length !== 0;
let hex = sharp ? "0x" : "";
const prec = this.flags.precision;
const end = prec !== -1 ? min(prec, val.length) : val.length;
for (let i = 0; i !== end; ++i) {
if (i !== 0 && this.flags.space) {
hex += sharp ? " 0x" : " ";
}
// TODO(bartlomieju): for now only taking into account the
// lower half of the codePoint, ie. as if a string
// is a list of 8bit values instead of UCS2 runes
const c = (val.charCodeAt(i) & 0xff).toString(16);
hex += c.length === 1 ? `0${c}` : c;
}
if (upper) {
hex = hex.toUpperCase();
}
return this.pad(hex);
}
default:
throw new Error(
"currently only number and string are implemented for hex",
);
}
}
/**
* Format value
* @param val
*/
fmtV(val: Record<string, unknown>): string {
if (this.flags.sharp) {
const options = this.flags.precision !== -1
? { depth: this.flags.precision }
: {};
return this.pad(val.toString());
} else {
const p = this.flags.precision;
return p === -1 ? val.toString() : val.toString().substr(0, p);
}
}
/**
* Format JSON
* @param val
*/
fmtJ(val: unknown): string {
return JSON.stringify(val);
}
}
/**
* Converts and format a variable number of `args` as is specified by `format`.
* `sprintf` returns the formatted string.
*
* @param format
* @param args
*/
export function sprintf(format: string, ...args: unknown[]): string {
const printf = new Printf(format, ...args);
return printf.doPrintf();
}
import {NS} from "@ns";
/**
* Converts and format a variable number of `args` as is specified by `format`.
* `printf` writes the formatted string to standard output.
* @param format
* @param args
*/
export function printf(ns: NS, format: string, ...args: unknown[]): void {
const s = sprintf(format, ...args);
ns.tprint(s);
}

View File

@ -8,8 +8,8 @@ export async function main(ns: NS): Promise<void> {
ns.tprint("run cmd [filePath]") ns.tprint("run cmd [filePath]")
ns.exit() ns.exit()
} }
const filePath = ns.args[0] const filePath = String(ns.args[0])
const collectedData = selectAllServerList() const collectedData = selectAllServerList(ns)
const buf = JSON.stringify(collectedData, undefined, 2) const buf = JSON.stringify(collectedData, undefined, 2)
await ns.write(filePath, buf, "w") await ns.write(filePath, buf, "w")
} }
@ -24,14 +24,15 @@ export interface ServerInfo extends Server{
*/ */
export function visitAllServerList(ns: NS, func:(data: ServerInfo)=>void):void { export function visitAllServerList(ns: NS, func:(data: ServerInfo)=>void):void {
/** @type {string[]} */ /** @type {string[]} */
const queue = ["home"] const queue: string[] = ["home"]
/** @type {Set<string>} */ /** @type {Set<string>} */
const visited = new Set() const visited = new Set()
//breadth first search //breadth first search
while (queue.length > 0) { while (queue.length > 0) {
const vHost = queue.pop() const vHost = queue.pop()
const data = getServersInfo(ns, vHost) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const data = getServersInfo(ns, vHost!)
func(data) func(data)

View File

@ -0,0 +1,117 @@
export function algorithmicTradeISolve(arr:number[]):number{
const localMax = arr.map((v,i,ar)=>{
return Math.max(...ar.slice(i)) - v;
});
//console.log(localMax)
return Math.max(...localMax, 0);
}
//console.log(algorithmicTrade1Solve([130,188,29,178,176,54,157,177,151,184,105,22,185,165,151,80,58,25]))
//163
export function algorithmicTradeIISolve(arr:number[]):number{
const memo: number[][] = (new Array(arr.length)).fill([])
.map(_=>new Array(arr.length).fill(-1));
const v = solve(arr,0,0);
return v;
function solve(arr:number[],i: number,sellChance: number):number{
if(arr.length == i){
return 0;
}
if(memo[i][sellChance] >= 0){
return memo[i][sellChance];
}
const result = [];
if(sellChance == 1){
const sell = arr[i];
result.push(sell + solve(arr,i+1,0));
}
else {
const buy = arr[i];
result.push(-buy + solve(arr,i+1, 1));
}
result.push(solve(arr,i+1,sellChance));
const v = Math.max(...result);
memo[i][sellChance] = v;
return v;
}
}
//console.log(algorithmicTrade2Solve([17,172,46,80,159,16,159,182,60,27,114,75,176,106,5,30,130,87,22]))
//747
export function algorithmicTradeIIISolve(arr:number[]):number{
const memo: number[][] = (new Array(arr.length)).fill([])
.map(_=>new Array(5).fill(-1));
const v = solve(arr,0,4);
console.log(memo)
return v;
function solve(arr:number[],i: number,transaction: number):number{
if(arr.length == i){
return 0;
}
if(transaction == 0){
return 0;
}
if(memo[i][transaction] >= 0){
return memo[i][transaction];
}
const result = [];
const isBuy = transaction % 2 === 0;
if(isBuy){
const buy = arr[i];
result.push(-buy + solve(arr,i+1, transaction - 1));
}
else {
const sell = arr[i];
result.push(sell + solve(arr,i+1, transaction - 1));
}
result.push(solve(arr,i+1,transaction));
const v = Math.max(...result);
memo[i][transaction] = v;
return v;
}
}
//console.log(algorithmicTradeIIISolve([25,166,22,187,140,184,118,71,36,43,127,68,94,133,141]))
//306
export function algorithmicTradeIVSolve([transaction,arr]:[number,number[]]):number{
const memo: number[][] = (new Array(arr.length)).fill([])
.map(_=>new Array(transaction * 2 + 4).fill(-1));
const v = solve(arr,0,transaction * 2);
console.log(memo)
return v;
function solve(arr:number[],i: number,transaction: number):number{
if(arr.length == i){
return 0;
}
if(transaction == 0){
return 0;
}
if(memo[i][transaction - 1] >= 0){
return memo[i][transaction - 1];
}
const result = [];
const isBuy = transaction % 2 === 0;
if(isBuy){
const buy = arr[i];
result.push(-buy + solve(arr,i+1, transaction - 1));
}
else {
const sell = arr[i];
result.push(sell + solve(arr,i+1, transaction - 1));
}
result.push(solve(arr,i+1,transaction));
const v = Math.max(...result);
memo[i][transaction - 1] = v;
return v;
}
}
//const v = algorithmicTradeIVSolve([7, [86,97,78,46,113,13,79,42,153,96,93,116,64,190,5,76,66,121,149,177,56,33,75,107,99,106,117]])
//console.log(v);
//649

View File

@ -0,0 +1,30 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
//Array Jumping Game
export function isJumpPossible(arr:number[]){
let possible = 0;
for (let i = 0; i <= possible && i < arr.length; i++) {
const element = arr[i];
possible = Math.max(possible,i+element);
if(possible >= arr.length - 1) return true;
}
return false;
}
export function arrayJumpSolve(arr:number[]): number{
return isJumpPossible(arr) ? 1 : 0;
}
export const testcases:Array<[number[],string]> = [
[[5,4,7,0,0,0,0],"1"],
[[0,0,0,5,7],"0"],
[[1,1,1,1],"1"],
[[3,0,0,1],"1"],
[[2,0,1,0],"1"],
[[2,0,1,0,0],"0"]
]
//for (const [data,ans] of testcases) {
// console.log(solve(data), ans);
//}

View File

@ -0,0 +1,28 @@
export function arrayJump2(arr:number[]): number{
const d = new Array(arr.length).fill(Infinity);
d[0] = 0;
for (let i = 0; i < arr.length; i++) {
const element = arr[i];
for (let j = i+1; j <= element+i && j < arr.length; j++) {
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);
}
export const testcases: Array<[number[],number]> = [
[[1,4,2,5,7,2,2,2,4,4,5,5,3,6,2,6,4,4,1,3], 5],
[[1,1,1,1],3],
[[3,1,1,1],1],
]
//for (const testcase of testcases) {
// const [data,ans] = testcase;
// const step = arrayJump2(data);
// console.log(step,ans)
//}

View File

@ -0,0 +1,154 @@
/**
* Compression I: RLE Compression
* Run-length encoding (RLE) is a data compression technique which encodes data as
* a series of runs of a repeated single character. Runs are encoded as a length,
* followed by the character itself. Lengths are encoded as a single ASCII digit;
* runs of 10 characters or more are encoded by splitting them into multiple runs.
*
* You are given the following input string:
* iiiiiii225RHHuu2222fddgxxxxxxxxxxxxVVVVVVVVVVVVVooooNNDDDDDDDDDGGgqqpSSSy3FFFFFF
* Encode it using run-length encoding with the minimum possible output length.
*
* Examples:
* aaaaabccc -> 5a1b3c
* aAaAaA -> 1a1A1a1A1a1A
* 111112333 -> 511233
* zzzzzzzzzzzzzzzzzzz -> 9z9z1z (or 9z8z2z, etc.)
*/
function matchLength(str: string, start: number): number {
const char = str.charAt(start);
let i;
for (i = 0; start + i < str.length; i++) {
const element = str.charAt(i + start);
if (element !== char) {
return i;
}
}
return i;
}
export function* RLECompression(str: string): Generator<[string, number]> {
let i = 0;
while (i < str.length) {
const char = str[i];
let v = matchLength(str, i);
while (v > 9) {
yield [char, 9];
v -= 9;
i += 9;
}
yield [char, v];
i += v;
}
return;
}
export function RLECompressionSolve(data: string): string[] {
return [[...RLECompression(data)].map(([ch, v]) => `${v}${ch}`).join("")]
}
//const RLETestCase = [
//["aaaaabccc", "5a1b3c"],
//["aAaAaA", "1a1A1a1A1a1A"],
//["111112333", "511233"],
//["zzzzzzzzzzzzzzzzzzz", "9z9z1z"],
//]
//for (const [data,ans] of RLETestCase) {
// const predict = [...RLECompression(data)].map(([ch,v])=>`${v}${ch}`).join("")
// console.log(predict,ans);
//}
export function decompressLZSolve(data: string): string[] {
return [decompressLZ(data)];
}
// code from
// https://github.com/mirkoconsiglio/Bitburner-scripts/blob/master/contracts/contractor.js
//
function decompressLZ(str: string): string {
let decoded = '', type = 0, len, ref, pos, i = 0, j;
while (i < str.length) {
if (i > 0) type ^= 1;
len = parseInt(str[i]);
ref = parseInt(str[++i]);
if (len === 0) continue;
if (!isNaN(ref) && type === 1) {
i++;
for (j = 0; j < len; j++) decoded += decoded[decoded.length - ref];
} else {
pos = i;
for (; i < len + pos; i++) decoded += str[i];
}
}
return decoded;
}
export function compressLZSolve(str: string): string[] {
return [compressLZ(str)];
}
export function compressLZ(str: string): string {
// state [i][j] contains a backreference of offset i and length j
let cur_state = Array.from(Array(10), _ => Array(10)), new_state, tmp_state, result;
cur_state[0][1] = ''; // initial state is a literal of length 1
for (let i = 1; i < str.length; i++) {
new_state = Array.from(Array(10), _ => Array(10));
const c = str[i];
// handle literals
for (let len = 1; len <= 9; len++) {
const input = cur_state[0][len];
if (input === undefined) continue;
if (len < 9) set(new_state, 0, len + 1, input); // extend current literal
else set(new_state, 0, 1, input + '9' + str.substring(i - 9, i) + '0'); // start new literal
for (let offset = 1; offset <= Math.min(9, i); offset++) { // start new backreference
if (str[i - offset] === c) set(new_state, offset, 1, input + len + str.substring(i - len, i));
}
}
// handle backreferences
for (let offset = 1; offset <= 9; offset++) {
for (let len = 1; len <= 9; len++) {
const input = cur_state[offset][len];
if (input === undefined) continue;
if (str[i - offset] === c) {
if (len < 9) set(new_state, offset, len + 1, input); // extend current backreference
else set(new_state, offset, 1, input + '9' + offset + '0'); // start new backreference
}
set(new_state, 0, 1, input + len + offset); // start new literal
// end current backreference and start new backreference
for (let new_offset = 1; new_offset <= Math.min(9, i); new_offset++) {
if (str[i - new_offset] === c) set(new_state, new_offset, 1, input + len + offset + '0');
}
}
}
tmp_state = new_state;
new_state = cur_state;
cur_state = tmp_state;
}
for (let len = 1; len <= 9; len++) {
let input = cur_state[0][len];
if (input === undefined) continue;
input += len + str.substring(str.length - len, str.length);
// noinspection JSUnusedAssignment
if (result === undefined || input.length < result.length) result = input;
}
for (let offset = 1; offset <= 9; offset++) {
for (let len = 1; len <= 9; len++) {
let input = cur_state[offset][len];
if (input === undefined) continue;
input += len + '' + offset;
if (result === undefined || input.length < result.length) result = input;
}
}
return result ?? '';
/**
*
* @param {string[][]} state
* @param {number} i
* @param {number} j
* @param {string} str
*/
function set(state: string[][], i:number, j:number, str:string): void {
if (state[i][j] === undefined || str.length < state[i][j].length) state[i][j] = str;
}
}

View File

View File

@ -0,0 +1,82 @@
//
// You are given the following data, representing a graph:
// [6,[[0,3],[0,4],[4,5],[0,1],[1,2],[1,4]]]
// Note that "graph", as used here, refers to the field of graph theory,
// and has no relation to statistics or plotting. The first element of
// the data represents the number of vertices in the graph. Each vertex
// is a unique number between 0 and 5. The next element of the data represents
// the edges of the graph. Two vertices u,v in a graph are said to be adjacent
// if there exists an edge [u,v]. Note that an edge [u,v] is the same as
// an edge [v,u], as order does not matter. You must construct a 2-coloring
// of the graph, meaning that you have to assign each vertex in the graph
// a "color", either 0 or 1, such that no two adjacent vertices have the
// same color. Submit your answer in the form of an array, where element i
// represents the color of vertex i. If it is impossible to construct a
// 2-coloring of the given graph, instead submit an empty array.
//
/**
* input
*/
type Input = [number,
Array<[number,number]>
];
export function graph2coloring([nVertex,edges]:Input):number[]{
const colors = new Array(nVertex).fill(null);
const queue = [0];
colors[0] = false;
while(queue.length > 0){
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const elem = queue.pop()!;
const neighbors = getNeighborhood(elem);
const chcolor = !colors[elem];
for (const v of neighbors) {
//propagate color;
if(colors[v] === null){
colors[v] = chcolor;
queue.push(v);
}
else if(colors[v] == !chcolor){
return [];
}
}
}
return colors.map(x=> x ? 1 : 0);
function getNeighborhood(i: number): number[]{
return edges.filter(([a,b])=> a==i || b==i)
.map(([a,b])=> a == i ? b : a);
}
}
export function graph2coloringSolve(input:Input):string[]{
return graph2coloring(input).map(x=>x.toString());
}
export type TestCase = [
Input,
number[]
]
export const testcases:TestCase[] =
[
[
[4, [[0, 2], [0, 3], [1, 2], [1, 3]]],
[0, 0, 1, 1]
],
[
[3, [[0, 1], [0, 2], [1, 2]]],
[]
],
[
[6,[[0,3],[0,4],[4,5],[0,1],[1,2],[1,4]]],
[]
],
[
[9,[[1,4],[3,6],[5,8],[0,1],[7,8],[2,8],[1,8],[2,6],[1,2],[4,6],[4,8]]],
[]
]
]
//for (const [data,ans] of testcases) {
// console.log(graph2coloringSolve(data), ans)
//}

View File

@ -0,0 +1,278 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
const primeTable = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013,
1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889,
1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129,
2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213,
2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287,
2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423,
2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531,
2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617,
2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741,
2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819,
2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903,
2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079,
3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181,
3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257,
3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413,
3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511,
3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571,
3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727,
3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821,
3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907,
3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057,
4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139,
4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231,
4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409,
4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493,
4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583,
4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831,
4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003,
5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179,
5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387,
5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639,
5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693,
5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857,
5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939,
5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053,
6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301,
6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367,
6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571,
6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761,
6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917,
6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297,
7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499,
7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643,
7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017,
8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111,
8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219,
8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291,
8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387,
8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501,
8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597,
8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677,
8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741,
8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831,
8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929,
8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109,
9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199,
9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283,
9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377,
9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439,
9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533,
9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631,
9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733,
9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811,
9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887,
9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007
]
function getNthPrime(i: number) {
if (i < primeTable.length) {
return primeTable[i];
}
for (let j = 0; j < i + 1 - primeTable.length; j++) {
makeNextPrime();
}
return primeTable[i];
}
function makeNextPrime() {
const lastPrime = primeTable[primeTable.length - 1];
let candidate = lastPrime + 2;
for (; ;) {
if (!primeTable.some(p => (candidate % p) == 0)) {
break;
}
candidate += 2;
}
primeTable.push(candidate);
}
export function getLargestPrimeFactor(n: number) {
let rest = n;
let i = 0;
let largest = 0;
for (; ;) {
const end = Math.floor(Math.sqrt(rest));
const p = getNthPrime(i);
if (p > end) {
break;
}
while (rest % p === 0) {
rest = Math.floor(rest / p);
largest = p;
}
i++;
}
if (largest < rest) largest = rest;
return largest;
}
export function largestPrimeSolve(n: number): number {
return getLargestPrimeFactor(n)
}
export const testcases:Array<[number,number]> = [
[2, 2],
[3, 3],
[4, 2],
[5, 5],
[6, 3],
[7, 7],
[8, 2],
[9, 3],
[10, 5],
[11, 11],
[12, 3],
[13, 13],
[14, 7],
[15, 5],
[16, 2],
[17, 17],
[18, 3],
[19, 19],
[20, 5],
[21, 7],
[22, 11],
[23, 23],
[24, 3],
[25, 5],
[26, 13],
[27, 3],
[28, 7],
[29, 29],
[30, 5],
[31, 31],
[32, 2],
[33, 11],
[34, 17],
[35, 7],
[36, 3],
[37, 37],
[38, 19],
[39, 13],
[40, 5],
[41, 41],
[42, 7],
[43, 43],
[44, 11],
[45, 5],
[46, 23],
[47, 47],
[48, 3],
[49, 7],
[50, 5],
[51, 17],
[52, 13],
[53, 53],
[54, 3],
[55, 11],
[56, 7],
[57, 19],
[58, 29],
[59, 59],
[60, 5],
[61, 61],
[62, 31],
[63, 7],
[64, 2],
[65, 13],
[66, 11],
[67, 67],
[68, 17],
[69, 23],
[70, 7],
[71, 71],
[72, 3],
[73, 73],
[74, 37],
[75, 5],
[76, 19],
[77, 11],
[78, 13],
[79, 79],
[80, 5],
[81, 3],
[82, 41],
[83, 83],
[84, 7],
[85, 17],
[86, 43],
[87, 29],
[88, 11],
[89, 89],
[90, 5],
[91, 13],
[92, 23],
[93, 31],
[94, 47],
[95, 19],
[96, 3],
[97, 97],
[98, 7],
[99, 11],
]
//if (import.meta.main) {
// for (const [data, ans] of testcases) {
// console.log(data,"\t",getLargestPrimeFactor(data),"\t", ans)
// }
//}

View File

@ -0,0 +1,59 @@
//Minimum Path Sum in a Triangle
export function minimumTrianglePathSumSolve(triangle:number[][]):number{
const minSum = [[triangle[0][0]]];
for (let i = 1; i < triangle.length; i++) {
const arr = triangle[i];
const tmp = [];
for (let j = 0; j < arr.length; j++) {
const cur = triangle[i][j];
if(j - 1 < 0){
tmp.push(minSum[i-1][j]+ cur);
}
else if(j >= triangle[i-1].length){
tmp.push(minSum[i-1][j-1] + cur);
}
else {
tmp.push(Math.min(minSum[i-1][j-1],minSum[i-1][j])+ cur);
}
}
minSum.push(tmp);
}
return Math.min(...minSum[minSum.length - 1]);
}
const case1 = [
[2],
[3,4],
[6,5,7],
[4,1,8,3]
];
const case2 = [
[3],
[8,5],
[3,5,4],
[2,6,3,4],
[9,6,5,5,3],
[6,1,6,5,2,4],
[6,7,7,1,1,8,6],
[7,6,5,9,3,9,2,2],
[6,3,4,6,9,2,8,8,5],
[1,4,6,3,6,5,5,1,9,2],
[1,6,3,6,6,7,2,9,6,6,1],
[1,3,9,8,3,7,1,7,4,5,6,6]
];
interface TestCase{
data: number[][];
ans: number;
}
export const testcases: TestCase[] = [
{
data:case1,
ans:6,
},
{
data:case2,
ans:35
}
];

View File

@ -0,0 +1,161 @@
/**
* You are located in the top-left corner of the following grid:
*
* [[0,0,0,0,0,0,0,0,1,0],
* [0,0,0,1,0,1,0,0,1,0],
* [0,0,0,1,0,0,0,0,0,0],
* [0,0,1,0,0,0,0,0,0,1],
* [0,0,0,0,1,0,0,0,1,0],
* [0,0,1,1,0,1,0,0,0,0],
* [1,1,0,0,0,0,0,0,1,0],
* [0,0,1,0,0,0,0,0,0,0],
* [0,1,1,1,0,0,1,0,0,1],
* [0,1,1,0,0,1,0,0,0,0],
* [1,0,0,1,0,1,0,0,0,0],
* [0,1,0,0,0,0,0,0,0,0]]
*
* You are trying to find the shortest path to the bottom-right
* corner of the grid, but there are obstacles on the grid that
* you cannot move onto. These obstacles are denoted by '1', wh
* ile empty spaces are denoted by 0.
*
* Determine the shortest path from start to finish, if one exi
* sts. The answer should be given as a string of UDLR characte
* rs, indicating the moves along the path
*
* NOTE: If there are multiple equally short paths, any of them
* is accepted as answer. If there is no path, the answer
* should be an empty string.
* NOTE: The data returned for this contract is an 2D array of
* numbers representing the grid.
*
* Examples:
*
* [[0,1,0,0,0],
* [0,0,0,1,0]]
*
* Answer: 'DRRURRD'
*
* [[0,1],
* [1,0]]
*
* Answer: ''
*/
import { SolveFailError } from "./unsolved";
export function shortestPathInGrid(map: number[][]): string[] {
const distMap = makeDistanceMap(map, 0, 0);
if (!isFinite(distMap[map.length - 1][map[map.length - 1].length - 1])) {
return [];
}
const pathes = rewindMap(distMap, map, map[map.length - 1].length - 1, map.length - 1)
return reversePath(pathes);
}
export function shortestPathInGridSolve(map: number[][]): string[] {
return [shortestPathInGrid(map).join("")];
}
function copyMap<T>(map: number[][], fillValue: T): T[][] {
return map.map(x =>
x.map(_ => fillValue)
);
}
function makeDistanceMap(map: number[][], initPosX: number, initPosY: number): number[][] {
const distMap = copyMap(map, Infinity);
const queue: Array<[number, number]> = [];
queue.push([initPosX, initPosY]);
distMap[initPosY][initPosX] = 0;
while (queue.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const [x, y] = queue.pop()!;
const currentDist = distMap[y][x];
//visit neighbor
const neighborhood: Array<[number, number]> = [[x + 1, y], [x, y + 1], [x, y - 1], [x - 1, y]]
for (const [nextX, nextY] of neighborhood) {
if (isVisitable(map, nextX, nextY)) {
if (distMap[nextY][nextX] > currentDist + 1) {
//set distance
distMap[nextY][nextX] = currentDist + 1;
//next nodes
queue.push([nextX, nextY]);
}
}
}
}
return distMap;
}
function isVisitable(map: number[][], x: number, y: number): boolean {
return 0 <= y && y < map.length && 0 <= x && x < map[y].length && map[y][x] === 0
}
type PathDir = "R" | "D" | "U" | "L";
function rewindMap(distMap: number[][], map: number[][], initPosX: number, initPosY: number): PathDir[] {
let x = initPosX, y = initPosY;
const ret: PathDir[] = [];
while (getMapDist([x, y]) > 0) {
//walk
const neighborhood: Array<[number, number]> = [[x + 1, y], [x, y + 1], [x, y - 1], [x - 1, y]]
const dirNames: PathDir[] = ["R", "D", "U", "L"];
const candidate = neighborhood.map((x, i) => ({ p: x, index: i }))
.filter(({ p: [cx, cy] }) => isVisitable(map, cx, cy))
if (candidate.length === 0) {
throw new SolveFailError("candidate 0");
}
const next = candidate.reduce((v1, v2) => getMapDist(v1.p) < getMapDist(v2.p) ? v1 : v2)
x = next.p[0];
y = next.p[1];
ret.push(dirNames[next.index]);
}
return ret;
function getMapDist([x, y]: [number, number]): number {
return distMap[y][x];
}
}
const REVERSE_TABLE: { [key in PathDir]: PathDir } = {
"R": "L",
"L": "R",
"D": "U",
"U": "D",
}
/**
* this method mutate array.
* @param path path to reverse
* @returns same reference of input array
*/
function reversePath(path: PathDir[]): PathDir[] {
return path.reverse().map(x => REVERSE_TABLE[x]);
}
//const map = [
// [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
// [0, 0, 0, 1, 0, 1, 0, 0, 1, 0],
// [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
// [0, 0, 1, 0, 0, 0, 0, 0, 0, 1],
// [0, 0, 0, 0, 1, 0, 0, 0, 1, 0],
// [0, 0, 1, 1, 0, 1, 0, 0, 0, 0],
// [1, 1, 0, 0, 0, 0, 0, 0, 1, 0],
// [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
// [0, 1, 1, 1, 0, 0, 1, 0, 0, 1],
// [0, 1, 1, 0, 0, 1, 0, 0, 0, 0],
// [1, 0, 0, 1, 0, 1, 0, 0, 0, 0],
// [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
//]
//
//import {sprintf} from "https://deno.land/std@0.152.0/fmt/printf.ts";
//function sprintDistMap(distMap:number[][]){
// return (distMap.map(x=>x.map(y=>isFinite(y)? y : "I")
// .map(y=>sprintf("%2s",y.toString())).join(",")).join("\n"));
//}
//const distMap = makeDistanceMap(map,0,0);
//console.log(sprintDistMap(distMap));
//const pathes = rewindMap(distMap,map,map[map.length - 1].length - 1, map.length - 1)
//console.log(reversePath(pathes));

90
src/lib/solve/spiral.ts Normal file
View File

@ -0,0 +1,90 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
enum Direction{
RIGHT = 0,
DOWN = 1,
LEFT = 2,
UP = 3
}
export function spiral(arr: number[][]):number[] {
const w = arr[0].length;
const h = arr.length;
let x = -1, y = 0;
let wdepth = w;
let hdepth = h - 1;
let dir:Direction = Direction.RIGHT;
const ret: number[] = [];
//ret.push(arr[y][x]);
for (;;) {
if(wdepth == 0) break;
//console.log(wdepth);
stepN(wdepth);
dir = turn90(dir);
if(hdepth == 0) break;
//console.log(hdepth);
stepN(hdepth);
dir = turn90(dir);
wdepth--;
hdepth--;
}
return ret;
function turn90(d: Direction){
return (d + 1)% 4;
}
function stepN(n:number){
for (let i = 0; i < n; i++) {
step();
}
}
function step(){
switch(dir){
case Direction.RIGHT:
x++;
break;
case Direction.DOWN:
y++;
break;
case Direction.LEFT:
x--;
break;
case Direction.UP:
y--;
break;
}
ret.push(arr[y][x]);
}
}
export function spiralSolve(arr:number[][]):string[]{
return spiral(arr).map(x=>x.toString());
}
const case1: [number[][],number[]] = [[[1,2,3],
[4,5,6],
[7,8,9]],
[1, 2, 3, 6, 9, 8, 7, 4, 5]
];
const case2: [number[][],number[]] =[
[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],],
[1, 2, 3, 4, 8,12, 11, 10, 9, 5,6, 7]
];
const case3: [number[][],number[]] = [[
[33,11,20,23,11,47,31,16,41,49],
[37,32,14,34,30,18,15,17,45,36],
[ 9,22,40,35,19,12,23,16,37, 5],
],
[33, 11, 20, 23, 11, 47, 31, 16, 41,49, 36, 5, 37, 16, 23, 12, 19, 35,40, 22, 9, 37, 32, 14, 34, 30, 18,15, 17, 45]
]
export const testcases: Array<[number[][],number[]]> = [
case1,
case2,
case3
];

View File

@ -0,0 +1,58 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
function* localmaxSum(arr: number[], first: number, last: number){
const firstIndex = arr.findIndex(x=> x > 0);
if(firstIndex < 0) return;
let sum = 0;
let blockSum = 0;
for (let index = first; index <= last; index++) {
const element = arr[index];
blockSum += element;
if(blockSum > 0){
sum += blockSum;
blockSum = 0;
}
else if(sum + blockSum < 0){
yield sum;
blockSum = 0;
sum = 0;
}
}
yield sum;
}
export function getMaximumlSumSubArray(arr:number[]): number{
let maxsum = 0;
for(const g of localmaxSum(arr,0,arr.length - 1)){
if(maxsum < g){
maxsum = g;
}
}
if(maxsum === 0){
return Math.max(...arr);
}
return maxsum;
}
export function subarrayMaxSolve(arr:number[]): number{
return getMaximumlSumSubArray(arr);
}
type TestCase = [number[],number];
export const testcases:TestCase[]=[
[[1,5,-9,4],6],
[[9,4,4,0],17],
[[1,-90,5],5],
[[1,2,-50,6],6],
[[1,2,-90,1,4],5],
[[1,2,-90,1,1,1,1],4],
[[1,-90,91],91],
[[-1,-2,-4],-1],
[[-1,3,6],9],
[[5,-50,4,-1,3],6]
]
//if(import.meta.main){
// for (const [arr,s] of testcases) {
// console.log("predict: ",solve(arr)," actual: ", s);
// }
//}

View File

@ -0,0 +1 @@
export class SolveFailError extends Error{}

View File

@ -1,10 +1,19 @@
import {NS} from "@ns" import {NS} from "@ns"
import { parse } from "./lib/flag";
import {getHackability,getLockSymbol} from "./lib/hack"; import {getHackability,getLockSymbol} from "./lib/hack";
import { sprintf } from "./lib/printf";
let detail = false;
/** @param {NS} ns */ /** @param {NS} ns */
// eslint-disable-next-line require-await // eslint-disable-next-line require-await
export async function main(ns: NS): Promise<void> { export async function main(ns: NS): Promise<void> {
findServer(ns, 'home', 'home', 1); const flag = parse(ns.args.map(String));
detail = flag.d || flag.detail || false;
const a = findServer(ns, 'home', 'home', 1);
ns.tprint("\n"+a.join("\n"));
} }
/** @param {NS} ns /** @param {NS} ns
@ -12,16 +21,33 @@ export async function main(ns: NS): Promise<void> {
* @param {string} targetServer * @param {string} targetServer
* @param {number} depth * @param {number} depth
*/ */
function findServer(ns: NS, startServer: string, targetServer: string, depth: number): void { function findServer(ns: NS, startServer: string, targetServer: string, depth: number): string[] {
const servers = ns.scan(targetServer) const servers = ns.scan(targetServer)
.filter((server) => server !== startServer); .filter((server) => server !== startServer);
servers.forEach((server) => { return servers.map((server, i, arr) => {
const lock = getHackability(ns, server); const lock = getHackability(ns, server);
const lock_symbol = getLockSymbol(lock); const lock_symbol = getLockSymbol(lock);
const info = ns.getServer(server); const info = ns.getServer(server);
ns.tprint(`😹${'>'.repeat(depth)} ${lock_symbol} ${server} ${info.backdoorInstalled ? '✅': '❌'}`); const backdoorSymbol = info.backdoorInstalled ? '✅': '❌';
let ret: string[] = [];
const startSymbol = (i + 1 == arr.length) ? "└" : "├";
const extendSymbol = (i + 1 == arr.length) ? " " : "│";
//printf(ns, `😹${'>'.repeat(depth)} %s %s %s`,lock_symbol,server,info.backdoorInstalled ? '✅': '❌')
const fmsg = (`${startSymbol}${lock_symbol} ${backdoorSymbol} ${server}`);
ret.push(fmsg);
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,
"-".repeat(20-depth),
info.hackDifficulty,info.minDifficulty,info.requiredHackingSkill,
moneyAvailable,moneyMax));
}
if (lock !== "impossible") { if (lock !== "impossible") {
findServer(ns, targetServer, server, depth + 1); const s = findServer(ns, targetServer, server, depth + 1).map(x=>`${extendSymbol}${x}`);
ret = ret.concat(s)
} }
}); return ret;
}).flat();
} }

View File

@ -4,7 +4,7 @@ import {parse} from "lib/flag"
/** @param {NS} ns */ /** @param {NS} ns */
// eslint-disable-next-line require-await // eslint-disable-next-line require-await
export async function main(ns: NS):Promise<void> { export async function main(ns: NS):Promise<void> {
const flag = parse(ns.args); const flag = parse(ns.args.map(x=>String(x)));
if(flag.help || flag.h){ if(flag.help || flag.h){
ns.tprint(`* list all purchased server`); ns.tprint(`* list all purchased server`);
ns.exit(); ns.exit();

63
src/list-stock.ts Normal file
View File

@ -0,0 +1,63 @@
import { NS } from '@ns'
import { parse } from './lib/flag';
import { sprintf } from './lib/printf';
interface StockStatsticsInfo{
stockName: string;
price: number;
forecast: number;
vaolatility: number;
expectation: number;
variation: number;
std:number;
askPrice:number;
bidPrice:number;
maxShare:number;
}
export function getStockStatsticsList(ns:NS): StockStatsticsInfo[]{
return ns.stock.getSymbols().map(x=>getStockStatstics(ns,x));
}
export function getStockStatstics(ns:NS, stockName:string):StockStatsticsInfo{
const forecast = ns.stock.getForecast(stockName);
const val = ns.stock.getVolatility(stockName);
const price = ns.stock.getPrice(stockName);
const askPrice = ns.stock.getAskPrice(stockName);
const bidPrice = ns.stock.getBidPrice(stockName);
const maxShare = ns.stock.getMaxShares(stockName);
const priceVar = val * price;
const expectation = (2*forecast - 1) * priceVar;
const variation = 4*priceVar*(1-forecast)*forecast;
return {
stockName,
price,
forecast,
vaolatility: val,
expectation: expectation,
variation: variation,
std: Math.sqrt(variation),
askPrice,
bidPrice,
maxShare,
};
}
// 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;
}
const list = getStockStatsticsList(ns);
list.sort((a,b)=>b.expectation - a.expectation );
list.forEach(info=>{
ns.tprint(sprintf("%6s %8s %2.1f%% %8s %8s",info.stockName,
ns.nFormat(info.price,"$0.00a"),
info.forecast*100,
ns.nFormat(info.expectation,"$0.00a"),
ns.nFormat(info.std,"$0.00a")))
})
}

87
src/ls-contract.ts Normal file
View File

@ -0,0 +1,87 @@
import { NS, AutocompleteData } from '@ns'
import {selectAllContract, getContractList, RemoteFilePath} from "lib/contract";
import { sprintf } from './lib/printf';
import { parse } from './lib/flag';
import Fuse from "lib/fuse.esm";
function searchFilename(ns: NS, filename: string, hostname?: string): RemoteFilePath | null{
let p: RemoteFilePath[];
if(hostname){
p = getContractList(ns,hostname);
}
else{
p = selectAllContract(ns);
}
const fuse = new Fuse(p, {includeScore:true, keys:["filename"]});
const candiates = fuse.search(filename);
if(candiates.length === 0){
return null;
}
const candiate = candiates[0];
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if(candiate!.score! > 0.8){
return null;
}
return candiate.item;
}
// eslint-disable-next-line require-await
export async function main(ns : NS) : Promise<void> {
const capi = ns.codingcontract;
if(ns.args.length === 0){
const ctList = selectAllContract(ns);
ctList.map(ct=>{
const type = capi.getContractType(ct.filename,ct.hostname);
return {
...ct,
type
};
}).sort((a,b)=>(a.type>b.type) ? 1 : -1)
.forEach(ct=>{
const msg = sprintf("%17s %40s %s",ct.hostname,ct.filename,ct.type)
ns.tprint(msg);
})
return;
}
const flag = parse(ns.args.map(x=>x.toString()));
if(flag.h || flag.help){
ns.tprint("HELP : ")
ns.tprint("run cmd [filename] [--host|-t]");
return;
}
let hostFlag : string|undefined;
if(flag.host || flag.t){
hostFlag = flag.host.toString() || flag.t.toString();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if(!ns.serverExists(hostFlag!)){
ns.tprint("unexist hostname");
return;
}
}
const inputFilename = flag._.toString();
const p = searchFilename(ns,inputFilename, hostFlag);
if(p === null){
ns.tprint(`could not file ${inputFilename}`);
return;
}
const {filename, hostname: target} = p;
ns.tprint(`detail of ${target}:${filename}`);
const msg = [
`${filename}(${capi.getNumTriesRemaining(filename,target)}/10)`,
`${capi.getContractType(filename,target)}`,
`${capi.getDescription(filename,target).replaceAll("&nbsp;"," ")}`,
`${JSON.stringify(capi.getData(filename,target))}`
].join("\n");
ns.tprint("\n",msg);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
if(args.length == 1){
return [...data.servers]
}
return [];
}

39
src/lsnode.ts Normal file
View File

@ -0,0 +1,39 @@
import { selectRootedServerList, ServerInfo } from "lib/servers";
import {
calculateWeakenTime,
calculatePercentMoneyHacked,
calculateServerGrowth
} from "lib/formula";
import { NS } from '@ns'
import { sprintf } from "./lib/printf";
// eslint-disable-next-line require-await
export async function main(ns: NS): Promise<void> {
const list = selectRootedServerList(ns);
const player = ns.getPlayer();
const m = list.map(x => {
const optimalState: ServerInfo = {...x,
hackDifficulty: x.minDifficulty
} as ServerInfo;
ns.print(optimalState.minDifficulty," ", optimalState.hackDifficulty ,"");
const weakenTime = calculateWeakenTime(optimalState, player);
const earnMoney = calculatePercentMoneyHacked(optimalState, player);
//const growPercent = calculateServerGrowth()
return {
hostname: x.hostname,
info: x,
weakenTime,
earnMoney,
ce: earnMoney* x.moneyMax / weakenTime,
}
});
m.sort((a,b)=>(b.ce-a.ce));
m.filter(x=>x.ce > 0).forEach(x=>{
const msg = sprintf("%20s %8s %6.1fs %10.2f",x.hostname,
ns.nFormat(x.earnMoney * x.info.moneyMax,"$0.00a"),x.weakenTime,
x.ce*100);
ns.tprint(msg);
})
}

View File

@ -5,7 +5,7 @@ import {range} from "./util/range";
/** @param {NS} ns */ /** @param {NS} ns */
export async function main(ns: NS): Promise<void> { export async function main(ns: NS): Promise<void> {
const flag = parse(ns.args); const flag = parse(ns.args.map(x=>String(x)));
if(flag.h || flag.help){ if(flag.h || flag.help){
ns.tprint("script : purchase server") ns.tprint("script : purchase server")
ns.tprint(""); ns.tprint("");
@ -13,22 +13,23 @@ export async function main(ns: NS): Promise<void> {
} }
if(Boolean(flag.i) || Boolean(flag.interactive)){ if(Boolean(flag.i) || Boolean(flag.interactive)){
const ramLimit = ns.getPurchasedServerMaxRam() const ramLimit = ns.getPurchasedServerMaxRam()
const choices = [...range(3,20)].map(x=>Math.pow(2,x)).filter(x=>x <= ramLimit).map((x)=>{ const choices = [...range(3,21)].map(x=>Math.pow(2,x)).filter(x=>x <= ramLimit).map((x)=>{
const cost = ns.getPurchasedServerCost(x); const cost = ns.getPurchasedServerCost(x);
return `${x}GB (${ns.nFormat(cost,"$0.000a")})` return `${x}GB (${ns.nFormat(cost,"$0.000a")})`
}); });
const choice = await ns.prompt("which server do you purchase?",{ const choice = await ns.prompt("which server do you purchase?",{
type:"select", type:"select",
choices choices
}); }) as string;
if(choice === ""){ if(choice === ""){
ns.tprint("canceled"); ns.tprint("canceled");
ns.exit(); ns.exit();
return; return;
} }
const gb = parseInt(/^(\d+)GB/.exec(choice)[1]); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const gb = parseInt(/^(\d+)GB/.exec(choice)![1]);
ns.tprint("you select ",gb,"GB"); ns.tprint("you select ",gb,"GB");
const hostname = await ns.prompt("name your server",{type:"text"}); const hostname = await ns.prompt("name your server",{type:"text"}) as string;
if(hostname === ""){ if(hostname === ""){
ns.tprint("canceled"); ns.tprint("canceled");
ns.exit(); ns.exit();
@ -39,7 +40,7 @@ export async function main(ns: NS): Promise<void> {
ns.nFormat(ns.getPurchasedServerCost(gb),"$0.000a")})?`, ns.nFormat(ns.getPurchasedServerCost(gb),"$0.000a")})?`,
{ type: "boolean" }); { type: "boolean" });
if (p) { if (p) {
const l = ns.purchaseServer(hostname, ram) const l = ns.purchaseServer(hostname, gb);
ns.tprint(l, " purchased"); ns.tprint(l, " purchased");
} }
ns.exit() ns.exit()

View File

@ -1,4 +1,4 @@
import {NS} from "@ns"; import {NS, AutocompleteData, Server} from "@ns";
import {hackablePorts, getHackability, getLockSymbol} from "./lib/hack"; import {hackablePorts, getHackability, getLockSymbol} from "./lib/hack";
import {parse} from "./lib/flag"; import {parse} from "./lib/flag";
@ -13,29 +13,33 @@ type ReportOption = {
*/ */
// eslint-disable-next-line require-await // eslint-disable-next-line require-await
export async function main(ns: NS): Promise<void> { export async function main(ns: NS): Promise<void> {
const flag = parse(ns.args); const flag = parse(ns.args.map(x=>String(x)));
if (flag._.length == 0) { if (flag._.length == 0) {
ns.tprint("argumented required"); ns.tprint("argumented required");
return; return;
} }
const hostname = flag._[0]; const hostname = String(flag._[0]);
const detail = Boolean(flag.d) || Boolean(flag.detail); const detail = Boolean(flag.d) || Boolean(flag.detail);
const realtime = Boolean(flag.realtime);
if(realtime){
ns.tail();
for(;;){
const server = ns.getServer(hostname);
ns.clearLog();
const msg = makeDetailServerReport(ns,server);
ns.print(msg);
await ns.sleep(1000);
}
}
else{
serverReport(ns, hostname,{ serverReport(ns, hostname,{
detail: detail, detail: detail,
}); });
} }
/** }
* @param {NS} ns
* @param {string} hostname function makeDetailServerReport(ns:NS,server:Server): string{
*/ return [`hostname : ${server.hostname}(${server.ip})`,
export function serverReport(ns: NS, hostname: string, options?:ReportOption):void {
options ??= {};
const serverLock = getHackability(ns, hostname);
ns.tprint(`${getLockSymbol(serverLock)} ${hostname}`);
if(options.detail){
const server = ns.getServer(hostname);
const msg = ['',
`hostname : ${server.hostname}(${server.ip})`,
`🛡️${ns.nFormat(server.hackDifficulty,"0[.]000")}/${server.minDifficulty}(${server.baseDifficulty})`, `🛡️${ns.nFormat(server.hackDifficulty,"0[.]000")}/${server.minDifficulty}(${server.baseDifficulty})`,
`💸${ns.nFormat(server.moneyAvailable,"$0.000a")}/${ns.nFormat(server.moneyMax,"$0.000a")}`, `💸${ns.nFormat(server.moneyAvailable,"$0.000a")}/${ns.nFormat(server.moneyMax,"$0.000a")}`,
`💾${server.ramUsed}GB/${server.maxRam}GB`, `💾${server.ramUsed}GB/${server.maxRam}GB`,
@ -47,7 +51,19 @@ export function serverReport(ns: NS, hostname: string, options?:ReportOption):vo
`required hacking skill\t: ${server.requiredHackingSkill}`, `required hacking skill\t: ${server.requiredHackingSkill}`,
`ports \t\t\t: ${server.openPortCount}/${server.numOpenPortsRequired}` `ports \t\t\t: ${server.openPortCount}/${server.numOpenPortsRequired}`
].join("\n"); ].join("\n");
ns.tprint(msg); }
/**
* @param {NS} ns
* @param {string} hostname
*/
export function serverReport(ns: NS, hostname: string, options?:ReportOption):void {
options ??= {};
const serverLock = getHackability(ns, hostname);
ns.tprint(`${getLockSymbol(serverLock)} ${hostname}`);
if(options.detail){
const server = ns.getServer(hostname);
ns.tprint("\n"+makeDetailServerReport(ns, server));
} }
else{ else{
if (serverLock == "rooted") { if (serverLock == "rooted") {
@ -74,6 +90,6 @@ export function serverHackStatus(ns: NS, server: string): string {
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
export function autocomplete(data : ServerData, args : string[]) : string[] { export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
return [...data.servers] return [...data.servers]
} }

View File

@ -0,0 +1,35 @@
import { NS } from '@ns'
// eslint-disable-next-line require-await
export async function main(ns : NS) : Promise<void> {
const api = ns.stock;
if(!api.hasWSEAccount() || !api.has4SDataTIXAPI()){
ns.tprint("api need")
ns.tprint("purchase stock API!");
return;
}
ns.print("start stock-auto-sell-daemon");
ns.disableLog("ALL")
ns.tail();
for(;;){
for(const stock of api.getSymbols()){
const p = api.getForecast(stock);
if(p < 0.5){
const share = api.getPosition(stock)[0]
if(share > 0){
ns.print(`forecast: ${p}, sell ${stock} amount of ${share}`);
const v = api.sellStock(stock, share);
if(v == 0){
ns.print("failed to sell stock!");
ns.toast("Failed To Sell Stock","error",6000);
}
else{
ns.print(`avg sold price ${v}`);
ns.toast(`Sell ${stock} amount of ${ns.nFormat(share,"0.000a")}`,"info",6000);
}
}
}
}
await ns.sleep(6000);
}
}

69
src/stock-daemon.ts Normal file
View File

@ -0,0 +1,69 @@
import { NS } from '@ns'
// eslint-disable-next-line require-await
export async function main(ns : NS) : Promise<void> {
const stock = ns.stock;
if(!stock.hasWSEAccount() || !stock.has4SDataTIXAPI()){
ns.tprint("api need")
ns.tprint("purchase stock API!");
return;
}
if(ns.args.length == 0){
ns.tprint("argument need")
return;
}
ns.print("start stock-auto-sell-daemon");
ns.disableLog("ALL")
ns.tail();
const account = ns.args[0];
const tradableStocks = stock.getSymbols();
const p = tradableStocks.map(st=>{
const forecast = stock.getForecast(st);
const val = stock.getVolatility(st);
const price = stock.getPrice(st);
const priceVar = val * price;
const expectation = (2*forecast - 1) * priceVar;
const variation = 4*priceVar*(1-forecast)*forecast;
return {
stockName: st,
expectation: expectation,
variation: variation
};
});
}
//// eslint-disable-next-line require-await
//export async function main(ns : NS) : Promise<void> {
// const api = ns.stock;
// if(!api.hasWSEAccount() || !api.has4SDataTIXAPI()){
// ns.tprint("api need")
// ns.tprint("purchase stock API!");
// return;
// }
// ns.print("start stock-auto-sell-daemon");
// ns.disableLog("ALL")
// ns.tail();
// for(;;){
// for(const stock of api.getSymbols()){
// const p = api.getForecast(stock);
// if(p < 0.5){
// const share = api.getPosition(stock)[0]
// if(share > 0){
// ns.print(`forecast: ${p}, sell ${stock} amount of ${share}`);
// const v = api.sellStock(stock, share);
// if(v == 0){
// ns.print("failed to sell stock!");
// ns.toast("Failed To Sell Stock","error",6000);
// }
// else{
// ns.print(`avg sold price ${v}`);
// ns.toast(`Sell ${stock} amount of ${ns.nFormat(share,"0.000a")}`,"info",6000);
// }
// }
// }
// }
// await ns.sleep(6000);
// }
//}

7
src/unpack-script.ts Normal file
View File

@ -0,0 +1,7 @@
import { NS } from '@ns'
import { installBatchFilePack } from './lib/batchbase';
// eslint-disable-next-line require-await
export async function main(ns : NS) : Promise<void> {
await installBatchFilePack(ns);
}

View File

@ -1,5 +1,3 @@
import assert from "assert";
export class AssertionError extends Error{ export class AssertionError extends Error{
constructor(msg:string){ constructor(msg:string){
super(msg); super(msg);
@ -12,3 +10,9 @@ export function assert(expr: unknown, msg=""): asserts expr{
throw new AssertionError(msg) throw new AssertionError(msg)
} }
} }
export class NotImplementError extends Error{}
export function notImplemented(): never {
throw new NotImplementError();
}

View File

@ -1,5 +1,4 @@
export function* range(end: number): Generator<number>;
export function* range(begin:number,end:number): Generator<number>;
export function* range(arg1: number, arg2?: number): Generator<number>{ export function* range(arg1: number, arg2?: number): Generator<number>{
const begin = arg2 ? arg1 : 0; const begin = arg2 ? arg1 : 0;
const end = arg2 ?? arg1; const end = arg2 ?? arg1;