init 2
This commit is contained in:
parent
dd9664d4ee
commit
d24d3a63c8
@ -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",
|
||||||
|
21
.vscode/snippets.code-snippets
vendored
21
.vscode/snippets.code-snippets
vendored
@ -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
19
src/auto-contract.ts
Normal 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
39
src/batching.ts
Normal 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
45
src/deploy-miner.ts
Normal 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
17
src/get-cylce-size.ts
Normal 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}`);
|
||||||
|
}
|
@ -1,25 +1,31 @@
|
|||||||
import {NS} from "@ns"
|
import { NS } from "@ns"
|
||||||
import { parse } from "./lib/flag";
|
import { parse } from "./lib/flag";
|
||||||
import {range} from "./util/range";
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNodeProduction(level: number, ram: number, cores: number, mult: number, bitMult:number): number{
|
export function getNodeProduction(level: number, ram: number, cores: number, mult: number, bitMult: number): number {
|
||||||
const moneyGainPerLevel = 1.5;
|
const moneyGainPerLevel = 1.5;
|
||||||
const levelMult = level * moneyGainPerLevel;
|
const levelMult = level * moneyGainPerLevel;
|
||||||
const ramMult = Math.pow(1.035, ram - 1);
|
const ramMult = Math.pow(1.035, ram - 1);
|
||||||
@ -36,143 +42,194 @@ interface HacknetNodeComponent<T> {
|
|||||||
type ComponentKind = "level" | "core" | "ram";
|
type ComponentKind = "level" | "core" | "ram";
|
||||||
|
|
||||||
export interface CEInfo {
|
export interface CEInfo {
|
||||||
upgradeCost : HacknetNodeComponent<number>;
|
upgradeCost: HacknetNodeComponent<number>;
|
||||||
production : HacknetNodeComponent<number>;
|
production: HacknetNodeComponent<number>;
|
||||||
costEffective : HacknetNodeComponent<number>;
|
costEffective: HacknetNodeComponent<number>;
|
||||||
|
}
|
||||||
|
interface Factor<T> {
|
||||||
|
cost: T;
|
||||||
|
production: T;
|
||||||
|
costEffective: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCostEffective(ns:NS, nodeIndex: number, n:number): CEInfo{
|
function CEInfoToFactor(ceinfo: CEInfo):HacknetNodeComponent<Factor<number>>{
|
||||||
const coreUpgradeCost = ns.hacknet.getCoreUpgradeCost(nodeIndex,n);
|
return {
|
||||||
const ramUpgradeCost = ns.hacknet.getRamUpgradeCost(nodeIndex,n);
|
level:{
|
||||||
const levelUpgradeCost = ns.hacknet.getLevelUpgradeCost(nodeIndex,n);
|
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 {
|
||||||
|
const coreUpgradeCost = ns.hacknet.getCoreUpgradeCost(nodeIndex, n);
|
||||||
|
const ramUpgradeCost = ns.hacknet.getRamUpgradeCost(nodeIndex, n);
|
||||||
|
const levelUpgradeCost = ns.hacknet.getLevelUpgradeCost(nodeIndex, n);
|
||||||
const mult = ns.getHacknetMultipliers().production;
|
const mult = ns.getHacknetMultipliers().production;
|
||||||
const bitMult = 1//ns.getBitNodeMultipliers().HacknetNodeMoney;
|
const bitMult = 1//ns.getBitNodeMultipliers().HacknetNodeMoney;
|
||||||
const stat = ns.hacknet.getNodeStats(nodeIndex);
|
const stat = ns.hacknet.getNodeStats(nodeIndex);
|
||||||
|
|
||||||
const coreProduction = getNodeProduction(stat.level, stat.ram, stat.cores + n, mult, bitMult) - stat.production;
|
const coreProduction = getNodeProduction(stat.level, stat.ram, stat.cores + n, mult, bitMult) - stat.production;
|
||||||
const levelProduction = getNodeProduction(stat.level + n, stat.ram, stat.cores, mult, bitMult) - stat.production;
|
const levelProduction = getNodeProduction(stat.level + n, stat.ram, stat.cores, mult, bitMult) - stat.production;
|
||||||
const ramProduction = getNodeProduction(stat.level, stat.ram * Math.pow(2,n), stat.cores, mult, bitMult) - stat.production;
|
const ramProduction = getNodeProduction(stat.level, stat.ram * Math.pow(2, n), stat.cores, mult, bitMult) - stat.production;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
upgradeCost:{
|
upgradeCost: {
|
||||||
core: coreUpgradeCost,
|
core: coreUpgradeCost,
|
||||||
ram: ramUpgradeCost,
|
ram: ramUpgradeCost,
|
||||||
level: levelUpgradeCost,
|
level: levelUpgradeCost,
|
||||||
},
|
},
|
||||||
production:{
|
production: {
|
||||||
core: coreProduction,
|
core: coreProduction,
|
||||||
ram: ramProduction,
|
ram: ramProduction,
|
||||||
level: levelProduction,
|
level: levelProduction,
|
||||||
},
|
},
|
||||||
costEffective:{
|
costEffective: {
|
||||||
core: coreProduction/coreUpgradeCost,
|
core: coreProduction / coreUpgradeCost,
|
||||||
ram: ramProduction/ramUpgradeCost,
|
ram: ramProduction / ramUpgradeCost,
|
||||||
level: levelProduction/levelUpgradeCost
|
level: levelProduction / levelUpgradeCost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createUpgradeAction(ns:NS, index: number, n: number, kind:ComponentKind):(account: Account)=>void{
|
|
||||||
return (account: Account)=>{
|
|
||||||
|
function createUpgradeAction(ns: NS, index: number, n: number, kind: ComponentKind): (account: Account) => void {
|
||||||
|
return (account: Account) => {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case "core":
|
case "core":
|
||||||
account.value -= ns.hacknet.getCoreUpgradeCost(index,n);
|
account.value -= ns.hacknet.getCoreUpgradeCost(index, n);
|
||||||
ns.hacknet.upgradeCore(index,n)
|
ns.hacknet.upgradeCore(index, n)
|
||||||
break;
|
break;
|
||||||
case "level":
|
case "level":
|
||||||
account.value -= ns.hacknet.getLevelUpgradeCost(index,n);
|
account.value -= ns.hacknet.getLevelUpgradeCost(index, n);
|
||||||
ns.hacknet.upgradeLevel(index,n)
|
ns.hacknet.upgradeLevel(index, n)
|
||||||
break;
|
break;
|
||||||
case "ram":
|
case "ram":
|
||||||
account.value -= ns.hacknet.getRamUpgradeCost(index,n);
|
account.value -= ns.hacknet.getRamUpgradeCost(index, n);
|
||||||
ns.hacknet.upgradeRam(index,n)
|
ns.hacknet.upgradeRam(index, n)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
for(const it of getIndexOfHackNode(ns)){
|
costEffective:0,
|
||||||
const [c, k] = getMinCENode(ns,it,n);
|
};
|
||||||
if(c < optimalC){
|
let kind: ComponentKind = "core";
|
||||||
|
let maxIndex = 0;
|
||||||
|
for (const it of getIndexOfHackNode(ns)) {
|
||||||
|
const [c, k] = getMaxCENode(ns, it, n);
|
||||||
|
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;
|
||||||
const production = getNodeProduction(1,1,1,mult,bitMult);
|
const production = getNodeProduction(1, 1, 1, mult, bitMult);
|
||||||
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) {
|
||||||
const action = createUpgradeAction(ns,upgradeNodeIndex,chunkN,kind);
|
if(account.value > upgradeNodeCE.cost){
|
||||||
|
ns.print(`upgrade ${kind} index ${upgradeNodeIndex} : CE ${upgradeNodeCE.costEffective}`);
|
||||||
|
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(period);
|
||||||
await ns.sleep(1000);
|
account.upgradeIncrement(ns,rate,period);
|
||||||
}
|
}
|
||||||
}
|
}
|
95
src/lib/batchbase.ts
Normal file
95
src/lib/batchbase.ts
Normal 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
229
src/lib/contract.ts
Normal 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;
|
||||||
|
}
|
@ -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
146
src/lib/formula.ts
Normal 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
295
src/lib/fuse.esm.d.ts
vendored
Normal 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
1780
src/lib/fuse.esm.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
13
src/lib/log.ts
Normal 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
778
src/lib/printf.ts
Normal 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);
|
||||||
|
}
|
@ -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)
|
||||||
|
|
||||||
|
117
src/lib/solve/algorithmicTrader.ts
Normal file
117
src/lib/solve/algorithmicTrader.ts
Normal 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
|
30
src/lib/solve/arrayJump.ts
Normal file
30
src/lib/solve/arrayJump.ts
Normal 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);
|
||||||
|
//}
|
28
src/lib/solve/arrayJump2.ts
Normal file
28
src/lib/solve/arrayJump2.ts
Normal 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)
|
||||||
|
//}
|
154
src/lib/solve/compression.ts
Normal file
154
src/lib/solve/compression.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
0
src/lib/solve/findAllMathExpr.ts
Normal file
0
src/lib/solve/findAllMathExpr.ts
Normal file
82
src/lib/solve/graph2color.ts
Normal file
82
src/lib/solve/graph2color.ts
Normal 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)
|
||||||
|
//}
|
278
src/lib/solve/largestPrime.ts
Normal file
278
src/lib/solve/largestPrime.ts
Normal 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)
|
||||||
|
// }
|
||||||
|
//}
|
59
src/lib/solve/minsumTriangle.ts
Normal file
59
src/lib/solve/minsumTriangle.ts
Normal 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
|
||||||
|
}
|
||||||
|
];
|
161
src/lib/solve/shortestPathInGrid.ts
Normal file
161
src/lib/solve/shortestPathInGrid.ts
Normal 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
90
src/lib/solve/spiral.ts
Normal 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
|
||||||
|
];
|
58
src/lib/solve/submaxarr.ts
Normal file
58
src/lib/solve/submaxarr.ts
Normal 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);
|
||||||
|
// }
|
||||||
|
//}
|
1
src/lib/solve/unsolved.ts
Normal file
1
src/lib/solve/unsolved.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export class SolveFailError extends Error{}
|
@ -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 ? '✅': '❌';
|
||||||
if (lock !== "impossible") {
|
let ret: string[] = [];
|
||||||
findServer(ns, targetServer, server, depth + 1);
|
|
||||||
|
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") {
|
||||||
|
const s = findServer(ns, targetServer, server, depth + 1).map(x=>`${extendSymbol}${x}`);
|
||||||
|
ret = ret.concat(s)
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}).flat();
|
||||||
}
|
}
|
@ -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
63
src/list-stock.ts
Normal 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
87
src/ls-contract.ts
Normal 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(" "," ")}`,
|
||||||
|
`${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
39
src/lsnode.ts
Normal 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);
|
||||||
|
})
|
||||||
|
}
|
@ -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()
|
||||||
|
@ -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
|
function makeDetailServerReport(ns:NS,server:Server): string{
|
||||||
* @param {string} hostname
|
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]
|
||||||
}
|
}
|
35
src/stock-auto-sell-deamon.ts
Normal file
35
src/stock-auto-sell-deamon.ts
Normal 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
69
src/stock-daemon.ts
Normal 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
7
src/unpack-script.ts
Normal 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);
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user