Compare commits
10 Commits
4cea34bfae
...
48771f54dd
Author | SHA1 | Date | |
---|---|---|---|
48771f54dd | |||
374196acce | |||
d24d3a63c8 | |||
dd9664d4ee | |||
|
41e3b3fbcd | ||
|
510ddf4b22 | ||
|
e0f16244db | ||
|
f2e297f1e0 | ||
|
59e5eef9b1 | ||
|
7aa19ac2c6 |
333
.eslintrc.js
333
.eslintrc.js
@ -14,323 +14,28 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
plugins: ["@typescript-eslint"],
|
||||
ignorePatterns: ['NetscriptDefinitions.d.ts', '*.js'],
|
||||
rules: {
|
||||
"accessor-pairs": [
|
||||
"@typescript-eslint/array-type": ["error", { "default": "array-simple" }],
|
||||
"@typescript-eslint/ban-ts-comment": ["off"],
|
||||
"@typescript-eslint/explicit-member-accessibility": ["off"],
|
||||
"@typescript-eslint/explicit-module-boundary-types": ["off"],
|
||||
"@typescript-eslint/no-non-null-assertion": ["off"],
|
||||
"@typescript-eslint/no-use-before-define": ["off"],
|
||||
|
||||
"@typescript-eslint/explicit-function-return-type":["off"],
|
||||
|
||||
"@typescript-eslint/no-parameter-properties": ["off"],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
setWithoutGet: true,
|
||||
getWithoutSet: false,
|
||||
},
|
||||
{ "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
|
||||
],
|
||||
"array-bracket-newline": ["off"],
|
||||
"array-bracket-spacing": ["off"],
|
||||
"array-callback-return": ["off"],
|
||||
"array-element-newline": ["off"],
|
||||
"arrow-body-style": ["off"],
|
||||
"arrow-parens": ["off"],
|
||||
"arrow-spacing": ["off"],
|
||||
"block-scoped-var": ["off"],
|
||||
"block-spacing": ["off"],
|
||||
"brace-style": ["off"],
|
||||
"callback-return": ["error"],
|
||||
camelcase: ["off"],
|
||||
"capitalized-comments": ["off"],
|
||||
"class-methods-use-this": ["off"],
|
||||
complexity: ["off"],
|
||||
"consistent-return": ["off"],
|
||||
"consistent-this": ["off"],
|
||||
"constructor-super": ["error"],
|
||||
curly: ["off"],
|
||||
"default-case": ["off"],
|
||||
"dot-notation": ["off"],
|
||||
"eol-last": ["off"],
|
||||
eqeqeq: ["off"],
|
||||
"for-direction": ["error"],
|
||||
"func-call-spacing": ["off"],
|
||||
"func-name-matching": ["error"],
|
||||
"func-names": ["off", "never"],
|
||||
"func-style": ["off"],
|
||||
"function-paren-newline": ["off"],
|
||||
"getter-return": [
|
||||
"error",
|
||||
{
|
||||
allowImplicit: false,
|
||||
},
|
||||
],
|
||||
"global-require": ["off"],
|
||||
"guard-for-in": ["off"],
|
||||
"handle-callback-err": ["error"],
|
||||
"id-blacklist": ["error"],
|
||||
"id-length": ["off"],
|
||||
"id-match": ["error"],
|
||||
indent: ["off"],
|
||||
"indent-legacy": ["off"],
|
||||
"init-declarations": ["off"],
|
||||
"key-spacing": ["off"],
|
||||
"keyword-spacing": ["off"],
|
||||
"line-comment-position": ["off"],
|
||||
"linebreak-style": [
|
||||
"off", // Line endings automatically converted to LF on git commit so probably shouldn't care about it here
|
||||
],
|
||||
"lines-around-comment": ["off"],
|
||||
"lines-around-directive": ["error"],
|
||||
"lines-between-class-members": ["error"],
|
||||
"max-depth": ["off"],
|
||||
"max-len": ["off"],
|
||||
"max-lines": ["off"],
|
||||
"max-nested-callbacks": ["error"],
|
||||
"max-params": ["off"],
|
||||
"max-statements": ["off"],
|
||||
"max-statements-per-line": ["off"],
|
||||
"multiline-comment-style": ["off", "starred-block"],
|
||||
"multiline-ternary": ["off", "never"],
|
||||
"new-cap": ["off"],
|
||||
"new-parens": ["off"],
|
||||
"newline-after-var": ["off"],
|
||||
"newline-before-return": ["off"],
|
||||
"newline-per-chained-call": ["off"],
|
||||
"no-alert": ["error"],
|
||||
"no-array-constructor": ["error"],
|
||||
"no-await-in-loop": ["off"],
|
||||
"no-bitwise": ["off"],
|
||||
"no-buffer-constructor": ["error"],
|
||||
"no-caller": ["error"],
|
||||
"no-case-declarations": ["error"],
|
||||
"no-catch-shadow": ["error"],
|
||||
"no-class-assign": ["error"],
|
||||
"no-compare-neg-zero": ["error"],
|
||||
"no-confusing-arrow": ["error"],
|
||||
"no-console": ["off"],
|
||||
"no-const-assign": ["error"],
|
||||
"no-constant-condition": [
|
||||
"error",
|
||||
{
|
||||
checkLoops: false,
|
||||
},
|
||||
],
|
||||
"no-continue": ["off"],
|
||||
"no-control-regex": ["error"],
|
||||
"no-debugger": ["error"],
|
||||
"no-delete-var": ["error"],
|
||||
"no-div-regex": ["error"],
|
||||
"no-dupe-args": ["error"],
|
||||
"no-dupe-class-members": ["error"],
|
||||
"no-dupe-keys": ["error"],
|
||||
"no-duplicate-case": ["error"],
|
||||
"no-duplicate-imports": [
|
||||
"error",
|
||||
{
|
||||
includeExports: true,
|
||||
},
|
||||
],
|
||||
"no-else-return": ["off"],
|
||||
"no-empty": [
|
||||
"off",
|
||||
{
|
||||
allowEmptyCatch: false,
|
||||
},
|
||||
],
|
||||
"no-empty-character-class": ["error"],
|
||||
"no-empty-function": ["off"],
|
||||
"no-empty-pattern": ["error"],
|
||||
"no-eq-null": ["off"],
|
||||
"no-ex-assign": ["off"],
|
||||
"no-extra-boolean-cast": ["error"],
|
||||
"no-extra-parens": ["off"],
|
||||
"no-extra-semi": ["off"],
|
||||
"no-eval": ["off"],
|
||||
"no-extend-native": ["off"],
|
||||
"no-extra-bind": ["error"],
|
||||
"no-extra-label": ["error"],
|
||||
"no-fallthrough": ["off"],
|
||||
"no-floating-decimal": ["off"],
|
||||
"no-func-assign": ["error"],
|
||||
"no-global-assign": ["error"],
|
||||
"no-implicit-coercion": ["off"],
|
||||
"no-implicit-globals": ["error"],
|
||||
"no-implied-eval": ["error"],
|
||||
"no-inline-comments": ["off"],
|
||||
"no-inner-declarations": ["off", "both"],
|
||||
"no-invalid-regexp": ["error"],
|
||||
"no-invalid-this": ["off"],
|
||||
"no-irregular-whitespace": [
|
||||
"error",
|
||||
{
|
||||
skipStrings: false,
|
||||
skipComments: false,
|
||||
skipRegExps: false,
|
||||
skipTemplates: false,
|
||||
},
|
||||
],
|
||||
"no-iterator": ["error"],
|
||||
"no-label-var": ["error"],
|
||||
"no-labels": ["off"],
|
||||
"no-lone-blocks": ["error"],
|
||||
"no-lonely-if": ["off"],
|
||||
"no-loop-func": ["off"],
|
||||
"no-magic-numbers": ["off"],
|
||||
"no-mixed-operators": ["off"],
|
||||
"no-mixed-requires": ["error"],
|
||||
"no-mixed-spaces-and-tabs": ["off"],
|
||||
"no-multi-assign": ["off"],
|
||||
"no-multi-spaces": ["off"],
|
||||
"no-multi-str": ["error"],
|
||||
"no-multiple-empty-lines": [
|
||||
"off",
|
||||
{
|
||||
max: 1,
|
||||
},
|
||||
],
|
||||
"no-native-reassign": ["error"],
|
||||
"no-negated-condition": ["off"],
|
||||
"no-negated-in-lhs": ["error"],
|
||||
"no-nested-ternary": ["off"],
|
||||
"no-new": ["error"],
|
||||
"no-new-func": ["error"],
|
||||
"no-new-object": ["error"],
|
||||
"no-new-require": ["error"],
|
||||
"no-new-symbol": ["error"],
|
||||
"no-new-wrappers": ["error"],
|
||||
"no-octal": ["error"],
|
||||
"no-octal-escape": ["error"],
|
||||
"no-obj-calls": ["error"],
|
||||
"no-param-reassign": ["off"],
|
||||
"no-path-concat": ["error"],
|
||||
"no-plusplus": ["off"],
|
||||
"no-process-env": ["off"],
|
||||
"no-process-exit": ["error"],
|
||||
"no-proto": ["error"],
|
||||
"no-prototype-builtins": ["off"],
|
||||
"no-redeclare": ["off"],
|
||||
"no-regex-spaces": ["error"],
|
||||
"no-restricted-globals": ["error"],
|
||||
"no-restricted-imports": ["error"],
|
||||
"no-restricted-modules": ["error"],
|
||||
"no-restricted-properties": [
|
||||
"off",
|
||||
{
|
||||
object: "console",
|
||||
property: "log",
|
||||
message: "'log' is too general, use an appropriate level when logging.",
|
||||
},
|
||||
],
|
||||
"no-restricted-syntax": ["error"],
|
||||
"no-return-assign": ["off"],
|
||||
"no-return-await": ["error"],
|
||||
"no-script-url": ["error"],
|
||||
"no-self-assign": [
|
||||
"error",
|
||||
{
|
||||
props: false,
|
||||
},
|
||||
],
|
||||
"no-self-compare": ["error"],
|
||||
"no-sequences": ["error"],
|
||||
"no-shadow": ["off"],
|
||||
"no-shadow-restricted-names": ["error"],
|
||||
"no-spaced-func": ["off"],
|
||||
"no-sparse-arrays": ["error"],
|
||||
"no-sync": ["error"],
|
||||
"no-tabs": ["error"],
|
||||
"no-template-curly-in-string": ["error"],
|
||||
"no-ternary": ["off"],
|
||||
"no-this-before-super": ["off"],
|
||||
"no-throw-literal": ["error"],
|
||||
"no-trailing-spaces": ["off"],
|
||||
"no-undef": ["off"],
|
||||
"no-undef-init": ["error"],
|
||||
"no-undefined": ["off"],
|
||||
"no-underscore-dangle": ["off"],
|
||||
"no-unexpected-multiline": ["error"],
|
||||
"no-unmodified-loop-condition": ["error"],
|
||||
"no-unneeded-ternary": ["off"],
|
||||
"no-unreachable": ["off"],
|
||||
"no-unsafe-finally": ["error"],
|
||||
"no-unsafe-negation": ["error"],
|
||||
"no-unused-expressions": ["off"],
|
||||
"no-unused-labels": ["error"],
|
||||
"no-unused-vars": ["off"],
|
||||
"no-use-before-define": ["off"],
|
||||
"no-useless-call": ["off"],
|
||||
"no-useless-computed-key": ["error"],
|
||||
"no-useless-concat": ["off"],
|
||||
"no-useless-constructor": ["error"],
|
||||
"no-useless-escape": ["off"],
|
||||
"no-useless-rename": [
|
||||
"error",
|
||||
{
|
||||
ignoreDestructuring: false,
|
||||
ignoreExport: false,
|
||||
ignoreImport: false,
|
||||
},
|
||||
],
|
||||
"no-useless-return": ["off"],
|
||||
"no-var": ["off"],
|
||||
"no-void": ["off"],
|
||||
"no-warning-comments": ["off"],
|
||||
"no-whitespace-before-property": ["error"],
|
||||
"no-with": ["error"],
|
||||
"nonblock-statement-body-position": ["off", "below"],
|
||||
"object-curly-newline": ["off"],
|
||||
"object-curly-spacing": ["off"],
|
||||
"object-property-newline": ["off"],
|
||||
"object-shorthand": ["off"],
|
||||
"one-var": ["off"],
|
||||
"one-var-declaration-per-line": ["off"],
|
||||
"operator-assignment": ["off"],
|
||||
"operator-linebreak": ["off", "none"],
|
||||
"padded-blocks": ["off"],
|
||||
"padding-line-between-statements": ["error"],
|
||||
"prefer-arrow-callback": ["off"],
|
||||
"prefer-const": ["off"],
|
||||
"prefer-destructuring": ["off"],
|
||||
"prefer-numeric-literals": ["error"],
|
||||
"prefer-promise-reject-errors": ["off"],
|
||||
"prefer-reflect": ["off"],
|
||||
"prefer-rest-params": ["off"],
|
||||
"prefer-spread": ["off"],
|
||||
"prefer-template": ["off"],
|
||||
"quote-props": ["off"],
|
||||
quotes: ["off"],
|
||||
radix: ["off", "as-needed"],
|
||||
"require-await": ["off"],
|
||||
"require-jsdoc": ["off"],
|
||||
"require-yield": ["error"],
|
||||
"rest-spread-spacing": ["error", "never"],
|
||||
semi: ["warn", "never"],
|
||||
"semi-spacing": ["off"],
|
||||
"semi-style": ["error", "last"],
|
||||
"sort-imports": ["off"],
|
||||
"sort-keys": ["off"],
|
||||
"sort-vars": ["off"],
|
||||
"space-before-blocks": ["off"],
|
||||
"space-before-function-paren": ["off"],
|
||||
"space-in-parens": ["off"],
|
||||
"space-infix-ops": ["off"],
|
||||
"space-unary-ops": ["off"],
|
||||
"spaced-comment": ["off"],
|
||||
strict: ["off"],
|
||||
"switch-colon-spacing": [
|
||||
"error",
|
||||
{
|
||||
after: true,
|
||||
before: false,
|
||||
},
|
||||
],
|
||||
"symbol-description": ["error"],
|
||||
"template-curly-spacing": ["error"],
|
||||
"template-tag-spacing": ["error"],
|
||||
"unicode-bom": ["error", "never"],
|
||||
"use-isnan": ["error"],
|
||||
"valid-jsdoc": ["off"],
|
||||
"valid-typeof": ["error"],
|
||||
"vars-on-top": ["off"],
|
||||
"wrap-iife": ["error", "any"],
|
||||
"wrap-regex": ["off"],
|
||||
"yield-star-spacing": ["error", "before"],
|
||||
yoda: ["error", "never"],
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": ["off"],
|
||||
"@typescript-eslint/no-empty-function": ["off"],
|
||||
"no-return-await": "error",
|
||||
"require-await": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"no-constant-condition": ["off"]
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
package-lock.json
|
||||
OAuth.js
|
||||
NetscriptDefinitions.d.ts
|
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -2,6 +2,6 @@
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"Gruntfuggly.auto-snippet",
|
||||
"hexnaught.vscode-bitburner-connector"
|
||||
"bitburner.bitburner-vscode-integration"
|
||||
]
|
||||
}
|
20
.vscode/launch.json
vendored
Normal file
20
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Bitburner Render Process",
|
||||
"address": "127.0.0.1",
|
||||
"port": 9222,
|
||||
"request": "attach",
|
||||
"type": "pwa-chrome",
|
||||
"timeout": 15000,
|
||||
"webRoot": "${workspaceRoot}/src",
|
||||
"sourceMapPathOverrides": {
|
||||
"http://localhost:8000/sources/*": "${workspaceRoot}/src/*"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -6,9 +6,12 @@
|
||||
"eslint.workingDirectories": ["./src"],
|
||||
"editor.codeActionsOnSave": {"source.fixAll.eslint": true},
|
||||
|
||||
"bitburner.authToken": "MOG9GEp4MGcw4aupPiqC6C9kd8jtFR2JgWcuS9JMyJjYfRCv0sqWdoB1H0G7HMQY",
|
||||
"bitburner.fileWatcher.enable": true,
|
||||
// Bitburner Extension Settings
|
||||
"bitburner.scriptRoot": "./dist/",
|
||||
|
||||
|
||||
// Autosave Settings
|
||||
"files.autoSave": "off",
|
||||
|
||||
|
23
.vscode/snippets.code-snippets
vendored
23
.vscode/snippets.code-snippets
vendored
@ -3,7 +3,7 @@
|
||||
"scope": "typescript",
|
||||
"prefix": "template",
|
||||
"body": [
|
||||
"import { NS } from '../NetscriptDefinitions'",
|
||||
"import { NS } from '@ns'",
|
||||
"",
|
||||
"export async function main(ns : NS) : Promise<void> {",
|
||||
"\t//",
|
||||
@ -15,10 +15,29 @@
|
||||
"prefix": "autocomplete",
|
||||
"body": [
|
||||
"// 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]",
|
||||
"}"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
# This repository has been archived
|
||||
**For the most up to date version of this template, visit the official repository at https://github.com/bitburner-official/vscode-template**
|
||||
|
||||
## Extension Recommendations
|
||||
[vscode-bitburner-connector](https://github.com/hexnaught/vscode-bitburner-connector) ([vscode extension marketplace](https://marketplace.visualstudio.com/items?itemName=hexnaught.vscode-bitburner-connector)) to upload your files into the game
|
||||
[vscode-bitburner-connector](https://github.com/bitburner-official/bitburner-vscode) ([vscode extension marketplace](https://marketplace.visualstudio.com/items?itemName=bitburner.bitburner-vscode-integration)) to upload your files into the game
|
||||
|
||||
[vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) to use live linting in editor
|
||||
|
||||
@ -30,3 +33,7 @@ To update your Netscript Definitions, run `npm run defs` in a terminal
|
||||
Press F1 and Select `Bitburner: Enable File Watcher` to enable auto uploading to the game
|
||||
|
||||
If you run `watcher.js` in game, the game will automatically detect file changes and restart the associated scripts
|
||||
|
||||
## Deugging
|
||||
|
||||
For debugging bitburner on Steam you will need to enable a remote debugging port. This can be done by rightclicking bitburner in your Steam library and selecting properties. There you need to add `--remote-debugging-port=9222` [Thanks @DarkMio]
|
||||
|
37
build.js
Normal file
37
build.js
Normal file
@ -0,0 +1,37 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const esbuild = require("esbuild");
|
||||
const yargs = require("yargs");
|
||||
yargs.scriptName("builder")
|
||||
.usage("$0 <cmd> [args]")
|
||||
.command("watch","watch and build",(yargs)=>{
|
||||
|
||||
},(argv)=>{
|
||||
build({
|
||||
watch:true,
|
||||
});
|
||||
})
|
||||
.command("build","build",(yargs)=>{},
|
||||
(argv)=>{
|
||||
build();
|
||||
})
|
||||
.help()
|
||||
.argv;
|
||||
|
||||
async function build(options){
|
||||
options = options || {};
|
||||
options.watch = options.watch || false;
|
||||
let dir = fs.readdirSync("src");
|
||||
dir = dir.filter(x=>x.endsWith(".ts"));
|
||||
|
||||
await esbuild.build({
|
||||
entryPoints:dir,
|
||||
bundle:true,
|
||||
write: true,
|
||||
treeShaking: true,
|
||||
watch: options.watch,
|
||||
platform: "neutral",
|
||||
target:"es2020",
|
||||
outdir:"./dist",
|
||||
});
|
||||
}
|
14
package.json
14
package.json
@ -4,8 +4,8 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"watch": "npx tsc -w",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"build": "node build.js",
|
||||
"check": "tsc --noEmit",
|
||||
"defs": "node ./updateDefs.js"
|
||||
},
|
||||
"repository": {
|
||||
@ -19,10 +19,12 @@
|
||||
"homepage": "https://github.com/SlyCedix/bitburner-typescript-template#readme",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.4.3",
|
||||
"@types/yargs": "^17.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.4",
|
||||
"@typescript-eslint/parser": "^4.28.4",
|
||||
"eslint": "^7.31.0",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.3.5"
|
||||
"esbuild": "^0.15.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"yargs": "^17.5.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
46
src/auto-backdoor.ts
Normal file
46
src/auto-backdoor.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { NS } from '@ns'
|
||||
import { parse } from './lib/flag';
|
||||
import { sprintf } from './lib/printf';
|
||||
import { selectRootedServerList } from "./lib/servers"
|
||||
|
||||
|
||||
async function propagateBackdoor(ns:NS, hostname: string): Promise<void>{
|
||||
ns.singularity.connect(hostname);
|
||||
const list = ns.scan(hostname);
|
||||
for (const relname of list) {
|
||||
const info = ns.getServer(relname);
|
||||
if(info.backdoorInstalled){
|
||||
continue;
|
||||
}
|
||||
if(info.requiredHackingSkill > ns.getHackingLevel()){
|
||||
continue;
|
||||
}
|
||||
ns.tprint(`install backdoor ${relname}`);
|
||||
ns.singularity.connect(relname);
|
||||
await ns.singularity.installBackdoor();
|
||||
ns.singularity.connect(hostname);
|
||||
}
|
||||
}
|
||||
|
||||
export async function main(ns: NS) : Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>x.toString()));
|
||||
if(flag.h || flag.help){
|
||||
const msg = ['run cmd']
|
||||
msg.forEach(x=>ns.tprint(x));
|
||||
return;
|
||||
}
|
||||
if(flag._.length === 0){
|
||||
const servers = selectRootedServerList(ns);
|
||||
for(const server of servers){
|
||||
ns.tprint("prop backdoor to "+server.hostname);
|
||||
await propagateBackdoor(ns,server.hostname);
|
||||
}
|
||||
}
|
||||
else{
|
||||
// const hostname = flag._[0];
|
||||
// ns.tprint(sprintf("install backdoor %s",hostname));
|
||||
// await ns.singularity.installBackdoor(hostname);
|
||||
// ns.tprint("done");
|
||||
}
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
src/auto-hacking.ts
Normal file
15
src/auto-hacking.ts
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
import { selectHackableServerList, hackServer } from "./lib/hack"
|
||||
import {isRootedServer} from "./lib/servers"
|
||||
import {NS} from "@ns"
|
||||
|
||||
/** @param {NS} ns */
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
//ns.disableLog("ALL")
|
||||
const servers = selectHackableServerList(ns).filter(x => !isRootedServer(x))
|
||||
servers.forEach(x => {
|
||||
ns.tprint(`hack ${x.hostname}`)
|
||||
hackServer(ns, x)
|
||||
})
|
||||
}
|
54
src/batch-cal.ts
Normal file
54
src/batch-cal.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { NS, AutocompleteData } from '@ns'
|
||||
import { parse } from './lib/flag';
|
||||
import { sprintf } from './lib/printf';
|
||||
import {calculateBatchResource} from './lib/batchbase';
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS) : Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>x.toString()));
|
||||
if(flag.h || flag.help){
|
||||
const msg = ['run cmd [startThread] [--hostname || -h] [--graph]']
|
||||
msg.forEach(x=>ns.tprint(x));
|
||||
return;
|
||||
}
|
||||
if(flag._.length === 0){
|
||||
ns.tprint("Error. Require Argument");
|
||||
return;
|
||||
}
|
||||
if(!(flag.host)){
|
||||
ns.tprint("host required");
|
||||
return;
|
||||
}
|
||||
const v = parseInt(flag._[0].toString());
|
||||
if(isNaN(v)){
|
||||
ns.tprint("Error. Require Integer. v = " + flag._[0]);
|
||||
return;
|
||||
}
|
||||
const hostname = flag.host ?? flag.t;
|
||||
const server = ns.getServer(hostname)
|
||||
if(flag.graph){
|
||||
const col = [];
|
||||
for (let i = 1; i < v; i++) {
|
||||
const info = calculateBatchResource(ns,hostname,i,server.moneyMax,1);
|
||||
if(info === null) break;
|
||||
col.push({
|
||||
costEffect :(info.earnMoneyPerCycle / info.totalThreadCount),
|
||||
index: i,
|
||||
total: info.totalThreadCount
|
||||
})
|
||||
}
|
||||
col.forEach(({costEffect,index,total})=>{
|
||||
ns.tprint(sprintf("%3d %10.2f %4d",index,costEffect,total));
|
||||
})
|
||||
ns.tprint(col.map(x=>x.costEffect))
|
||||
return;
|
||||
}
|
||||
const info = calculateBatchResource(ns,hostname,v,server.moneyMax,1);
|
||||
ns.tprint(JSON.stringify(info,undefined,2));
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
|
||||
return [...data.servers]
|
||||
}
|
46
src/batching.ts
Normal file
46
src/batching.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { NS, AutocompleteData } from '@ns'
|
||||
import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, batchDaemon, requestHWGW } from './lib/batchbase';
|
||||
import { parse } from './lib/flag';
|
||||
|
||||
|
||||
function help(ns:NS):void{
|
||||
ns.tprint("run cmd [target(hostname)] --hostname hostname")
|
||||
}
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns : NS) : Promise<void> {
|
||||
|
||||
const flag = parse(ns.args.map(String))
|
||||
|
||||
const hostname: string|undefined = flag.hostname ?? flag.n;
|
||||
if(!hostname){
|
||||
ns.tprint("hostname is not set");
|
||||
help(ns);
|
||||
return;
|
||||
}
|
||||
if(flag._.length == 0){
|
||||
ns.tprint("target is not set");
|
||||
help(ns);
|
||||
return;
|
||||
}
|
||||
const target = flag._[0].toString();
|
||||
ns.print("check batch file pack ...")
|
||||
if(!isBatchFilePackInstalled(ns)){
|
||||
await installBatchFilePack(ns);
|
||||
}
|
||||
const server = ns.getServer(hostname)
|
||||
await scpBatchFilePack(ns,hostname);
|
||||
ns.print("request hwgw ...")
|
||||
requestHWGW({
|
||||
hostname,
|
||||
usableRam: server.maxRam - server.ramUsed,
|
||||
},target);
|
||||
ns.print("request hwgw done")
|
||||
ns.print("start batch daemon ...");
|
||||
await batchDaemon(ns, target);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
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];
|
||||
}
|
45
src/deploy-share.ts
Normal file
45
src/deploy-share.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { AutocompleteData, NS } from "@ns";
|
||||
import {parse} from "./lib/flag";
|
||||
import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, execWeakenLoop, getRamOfScript } from './lib/batchbase';
|
||||
|
||||
/** @param {NS} ns */
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
const flag = parse(ns.args.map(String));
|
||||
if(flag._.length < 1){
|
||||
ns.tprint("argument required");
|
||||
ns.tprint("run cmd [src(hostname)]");
|
||||
ns.exit();
|
||||
}
|
||||
const hostname = String(flag._[0]);
|
||||
|
||||
const th = flag.thread || false;
|
||||
|
||||
ns.scp("share-server.js",hostname);
|
||||
|
||||
let nThread;
|
||||
if(th){
|
||||
nThread = th;
|
||||
}
|
||||
else {
|
||||
nThread = calculateMaximumThread(ns, hostname, ns.getScriptRam("share-server.js",hostname));
|
||||
}
|
||||
const pid = ns.exec("share-server.js",hostname, nThread);
|
||||
ns.tprint(pid," Process started.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NS} ns
|
||||
* @param {string} dst
|
||||
* @param {number} amount the program ram
|
||||
*/
|
||||
export function calculateMaximumThread(ns:NS,host:string,amount:number):number{
|
||||
const maxRam = ns.getServerMaxRam(host);
|
||||
const usedRam = ns.getServerUsedRam(host);
|
||||
const restRam = maxRam - usedRam;
|
||||
return Math.floor(restRam / amount);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function autocomplete(data: AutocompleteData,args: string[]): string[]{
|
||||
return [...data.servers];
|
||||
}
|
57
src/deploy-weaken.ts
Normal file
57
src/deploy-weaken.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { AutocompleteData, NS } from "@ns";
|
||||
import {parse} from "./lib/flag";
|
||||
import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, execWeakenLoop, getRamOfScript } from './lib/batchbase';
|
||||
|
||||
/** @param {NS} ns */
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
const flag = parse(ns.args.map(String));
|
||||
if(flag._.length < 2){
|
||||
ns.tprint("argument required");
|
||||
ns.tprint("run cmd [src(hostname)] [dest(hostname)]");
|
||||
ns.exit();
|
||||
}
|
||||
const src = String(flag._[0]);
|
||||
const dest = String(flag._[1]);
|
||||
|
||||
const th = flag.thread || false;
|
||||
|
||||
if(!isBatchFilePackInstalled(ns)){
|
||||
await installBatchFilePack(ns);
|
||||
}
|
||||
|
||||
await scpBatchFilePack(ns,src);
|
||||
|
||||
const stock = flag.stock ?? false;
|
||||
|
||||
let nThread;
|
||||
if(th){
|
||||
nThread = th;
|
||||
}
|
||||
else {
|
||||
nThread = calculateMaximumThread(ns, src, getRamOfScript(ns,"weakenLoop"));
|
||||
}
|
||||
const pid = execWeakenLoop(ns,{
|
||||
hostname: src,
|
||||
target: dest,
|
||||
numThread: nThread,
|
||||
stock: stock,
|
||||
});
|
||||
ns.tprint(pid," Process started.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NS} ns
|
||||
* @param {string} dst
|
||||
* @param {number} amount the program ram
|
||||
*/
|
||||
export function calculateMaximumThread(ns:NS,host:string,amount:number):number{
|
||||
const maxRam = ns.getServerMaxRam(host);
|
||||
const usedRam = ns.getServerUsedRam(host);
|
||||
const restRam = maxRam - usedRam;
|
||||
return Math.floor(restRam / amount);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function autocomplete(data: AutocompleteData,args: string[]): string[]{
|
||||
return [...data.servers];
|
||||
}
|
18
src/get-cylce-size.ts
Normal file
18
src/get-cylce-size.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { NS } from '@ns'
|
||||
|
||||
export async function main(ns : NS) : Promise<void> {
|
||||
const stat: number[] = [];
|
||||
const M = 10;
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const start = performance.now();
|
||||
await ns.sleep(M);
|
||||
const end = performance.now();
|
||||
stat.push(end- start);
|
||||
}
|
||||
const avg = stat.reduce((x,y)=>x+y)/stat.length;
|
||||
const max = Math.max(...stat);
|
||||
const min = Math.min(...stat);
|
||||
ns.tprint(`avg : ${avg - M}`);
|
||||
ns.tprint(`max : ${max - M}`);
|
||||
ns.tprint(`min : ${min - M}`);
|
||||
}
|
16
src/get-my-karma.ts
Normal file
16
src/get-my-karma.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { NS } from '@ns'
|
||||
import { parse } from './lib/flag';
|
||||
import { sprintf } from './lib/printf';
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS) : Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>x.toString()));
|
||||
if(flag.h || flag.help){
|
||||
const msg = ['run cmd']
|
||||
msg.forEach(x=>ns.tprint(x));
|
||||
return;
|
||||
}
|
||||
// typescript error ignore
|
||||
// @ts-ignore TS2339
|
||||
ns.tprint(sprintf("karma: %.3f",ns.heart.break()));
|
||||
}
|
6
src/get-share-power.ts
Normal file
6
src/get-share-power.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {NS} from "@ns"
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns:NS):Promise<void>{
|
||||
ns.tprint(ns.getSharePower());
|
||||
}
|
46
src/growing.ts
Normal file
46
src/growing.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { NS, AutocompleteData } from '@ns'
|
||||
import { installBatchFilePack, isBatchFilePackInstalled, scpBatchFilePack, growDaemon, requestGrow } from './lib/batchbase';
|
||||
import { parse } from './lib/flag';
|
||||
|
||||
|
||||
function help(ns:NS):void{
|
||||
ns.tprint("run cmd [target(hostname)] --hostname hostname")
|
||||
}
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns : NS) : Promise<void> {
|
||||
const flag = parse(ns.args.map(String))
|
||||
|
||||
const hostname: string|undefined = flag.hostname ?? flag.n;
|
||||
if(!hostname){
|
||||
ns.tprint("hostname is not set");
|
||||
help(ns);
|
||||
return;
|
||||
}
|
||||
if(flag._.length == 0){
|
||||
ns.tprint("target is not set");
|
||||
help(ns);
|
||||
return;
|
||||
}
|
||||
const target = flag._[0].toString();
|
||||
ns.print("check batch file pack ...")
|
||||
if(!isBatchFilePackInstalled(ns)){
|
||||
await installBatchFilePack(ns);
|
||||
}
|
||||
const server = ns.getServer(hostname)
|
||||
await scpBatchFilePack(ns,hostname);
|
||||
ns.print("request hwgw ...")
|
||||
requestGrow({
|
||||
hostname,
|
||||
usableRam: server.maxRam - server.ramUsed,
|
||||
},target);
|
||||
ns.print("request hwgw done")
|
||||
ns.print("start batch daemon ...");
|
||||
await growDaemon(ns, target);
|
||||
ns.print("end grow daemon");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
|
||||
return [...data.servers]
|
||||
}
|
235
src/hacknet-daemon.ts
Normal file
235
src/hacknet-daemon.ts
Normal file
@ -0,0 +1,235 @@
|
||||
import { NS } from "@ns"
|
||||
import { parse } from "./lib/flag";
|
||||
import { range } from "./util/range";
|
||||
|
||||
class Account {
|
||||
value: number;
|
||||
constructor(value: number) {
|
||||
this.value = value;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param ns NS
|
||||
* @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);
|
||||
|
||||
}
|
||||
formatedValue(ns: NS): string{
|
||||
return ns.nFormat(this.value,"$0.0000a");
|
||||
}
|
||||
}
|
||||
|
||||
export function getNodeProduction(level: number, ram: number, cores: number, mult: number, bitMult: number): number {
|
||||
const moneyGainPerLevel = 1.5;
|
||||
const levelMult = level * moneyGainPerLevel;
|
||||
const ramMult = Math.pow(1.035, ram - 1);
|
||||
const coreMult = ((cores + 5) / 6);
|
||||
return levelMult * ramMult * coreMult * mult * bitMult;
|
||||
}
|
||||
|
||||
interface HacknetNodeComponent<T> {
|
||||
level: T;
|
||||
core: T;
|
||||
ram: T;
|
||||
}
|
||||
|
||||
type ComponentKind = "level" | "core" | "ram";
|
||||
|
||||
export interface CEInfo {
|
||||
upgradeCost: HacknetNodeComponent<number>;
|
||||
production: HacknetNodeComponent<number>;
|
||||
costEffective: HacknetNodeComponent<number>;
|
||||
}
|
||||
interface Factor<T> {
|
||||
cost: T;
|
||||
production: T;
|
||||
costEffective: T;
|
||||
}
|
||||
|
||||
function CEInfoToFactor(ceinfo: CEInfo):HacknetNodeComponent<Factor<number>>{
|
||||
return {
|
||||
level:{
|
||||
cost:ceinfo.upgradeCost.level,
|
||||
production:ceinfo.production.level,
|
||||
costEffective:ceinfo.costEffective.level,
|
||||
},
|
||||
core:{
|
||||
cost:ceinfo.upgradeCost.core,
|
||||
production:ceinfo.production.core,
|
||||
costEffective:ceinfo.costEffective.core,
|
||||
},
|
||||
ram:{
|
||||
cost:ceinfo.upgradeCost.ram,
|
||||
production:ceinfo.production.ram,
|
||||
costEffective:ceinfo.costEffective.ram,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getCostEffective(ns: NS, nodeIndex: number, n: number): CEInfo {
|
||||
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 bitMult = 1//ns.getBitNodeMultipliers().HacknetNodeMoney;
|
||||
const stat = ns.hacknet.getNodeStats(nodeIndex);
|
||||
|
||||
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 ramProduction = getNodeProduction(stat.level, stat.ram * Math.pow(2, n), stat.cores, mult, bitMult) - stat.production;
|
||||
|
||||
return {
|
||||
upgradeCost: {
|
||||
core: coreUpgradeCost,
|
||||
ram: ramUpgradeCost,
|
||||
level: levelUpgradeCost,
|
||||
},
|
||||
production: {
|
||||
core: coreProduction,
|
||||
ram: ramProduction,
|
||||
level: levelProduction,
|
||||
},
|
||||
costEffective: {
|
||||
core: coreProduction / coreUpgradeCost,
|
||||
ram: ramProduction / ramUpgradeCost,
|
||||
level: levelProduction / levelUpgradeCost
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function createUpgradeAction(ns: NS, index: number, n: number, kind: ComponentKind): (account: Account) => void {
|
||||
return (account: Account) => {
|
||||
switch (kind) {
|
||||
case "core":
|
||||
account.value -= ns.hacknet.getCoreUpgradeCost(index, n);
|
||||
ns.hacknet.upgradeCore(index, n)
|
||||
break;
|
||||
case "level":
|
||||
account.value -= ns.hacknet.getLevelUpgradeCost(index, n);
|
||||
ns.hacknet.upgradeLevel(index, n)
|
||||
break;
|
||||
case "ram":
|
||||
account.value -= ns.hacknet.getRamUpgradeCost(index, n);
|
||||
ns.hacknet.upgradeRam(index, n)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMaxCENode(ns: NS, index: number, n: number): [Factor<number>, ComponentKind] {
|
||||
const a = getCostEffective(ns, index, n);
|
||||
const b = CEInfoToFactor(a);
|
||||
const ce = a.costEffective;
|
||||
if (ce.core > ce.level) {
|
||||
if (ce.core > ce.ram) {
|
||||
return [b.core, "core"];
|
||||
}
|
||||
else {
|
||||
return [b.ram, "ram"];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ce.level > ce.ram) {
|
||||
return [b.level, "level"];
|
||||
}
|
||||
else {
|
||||
return [b.ram, "ram"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function* getIndexOfHackNode(ns: NS): Generator<number> {
|
||||
const numNodes = ns.hacknet.numNodes()
|
||||
for (const it of range(numNodes)) {
|
||||
yield it;
|
||||
}
|
||||
}
|
||||
|
||||
function getMaxCEIndex(ns: NS, n: number): [number, Factor<number>, ComponentKind] {
|
||||
let optimalC: Factor<number> = {
|
||||
cost:0,
|
||||
production:0,
|
||||
costEffective:0,
|
||||
};
|
||||
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;
|
||||
kind = k;
|
||||
maxIndex = it;
|
||||
}
|
||||
}
|
||||
return [maxIndex, optimalC, kind];
|
||||
}
|
||||
|
||||
function getBuyNodeCE(ns: NS): Factor<number> {
|
||||
const mult = ns.getHacknetMultipliers().production;
|
||||
const bitMult = 1//ns.getBitNodeMultipliers().HacknetNodeMoney;
|
||||
const cost = ns.hacknet.maxNumNodes() > ns.hacknet.numNodes() ? ns.hacknet.getPurchaseNodeCost() : Infinity;
|
||||
const production = getNodeProduction(1, 1, 1, mult, bitMult);
|
||||
return {
|
||||
cost,
|
||||
production,
|
||||
costEffective: production/cost,
|
||||
};
|
||||
}
|
||||
|
||||
function cycle(ns: NS, account: Account): void {
|
||||
const chunkN = 1;
|
||||
|
||||
const [upgradeNodeIndex, upgradeNodeCE, kind] = getMaxCEIndex(ns, chunkN);
|
||||
const { costEffective: buyNewCE, cost: buyNewCost } = getBuyNodeCE(ns);
|
||||
if (upgradeNodeCE.costEffective > buyNewCE) {
|
||||
if(account.value > upgradeNodeCE.cost){
|
||||
ns.print(`upgrade ${kind} index ${upgradeNodeIndex} : CE ${upgradeNodeCE.costEffective}`);
|
||||
const action = createUpgradeAction(ns, upgradeNodeIndex, chunkN, kind);
|
||||
action(account);
|
||||
ns.print(`budget ${account.formatedValue(ns)}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (account.value > buyNewCost) {
|
||||
ns.print(`purchase node : CE ${buyNewCE}`);
|
||||
account.value -= buyNewCost;
|
||||
ns.hacknet.purchaseNode()
|
||||
ns.print(`budget ${account.formatedValue(ns)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
ns.disableLog("ALL")
|
||||
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 rate = parseFloat(flag.r ?? flag.rate ?? "0.5");
|
||||
const budget = parseInt(flag.b ?? flag.budget ?? "0");
|
||||
|
||||
const account = new Account(budget);
|
||||
ns.tail();
|
||||
const period = 100;
|
||||
for (let i = 0; i < iter; i++) {
|
||||
//ns.clearLog()
|
||||
cycle(ns, account);
|
||||
await ns.sleep(period);
|
||||
account.upgradeIncrement(ns,rate*0.1,period);
|
||||
}
|
||||
}
|
531
src/lib/batchbase.ts
Normal file
531
src/lib/batchbase.ts
Normal file
@ -0,0 +1,531 @@
|
||||
import { NS, Server } from "@ns"
|
||||
import { info } from "console";
|
||||
import { calculateServerGrowth, FormulaHelper } from "./formula";
|
||||
import { Scheduler } from "./scheduler";
|
||||
|
||||
export type BatchType = "hack" | "grow" | "weaken" | "weakenLoop";
|
||||
|
||||
type BatchFilePackEntry = {
|
||||
type: BatchType;
|
||||
path: string;
|
||||
content: string;
|
||||
ram: number;
|
||||
};
|
||||
|
||||
type FilePack = BatchFilePackEntry[];
|
||||
|
||||
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";
|
||||
if(sleepDuration > 0){
|
||||
await ns.sleep(sleepDuration);
|
||||
}
|
||||
await ns.${funcName}(hostname,{
|
||||
stock,
|
||||
});
|
||||
}`
|
||||
|
||||
async function unpackFileEntry(ns: NS, filePack: BatchFilePackEntry): 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 = [
|
||||
{
|
||||
type: "hack",
|
||||
path: "/tmp/hack.js",
|
||||
content: createScriptContent("hack"),
|
||||
ram:1.7,
|
||||
},
|
||||
{
|
||||
type: "weaken",
|
||||
path: "/tmp/weaken.js",
|
||||
content: createScriptContent("weaken"),
|
||||
ram: 1.75,
|
||||
},
|
||||
{
|
||||
type: "grow",
|
||||
path: "/tmp/grow.js",
|
||||
content: createScriptContent("grow"),
|
||||
ram: 1.75,
|
||||
},
|
||||
{
|
||||
type:"weakenLoop",
|
||||
path: "/tmp/weakenLoop.js",
|
||||
ram: 1.75,
|
||||
content: `export async function main(ns) {
|
||||
if(ns.args.length < 3){
|
||||
ns.print("[ERROR] args required");
|
||||
ns.print("run cmd [sleepDuration] [hostname] [effectStock]")
|
||||
ns.exit();
|
||||
return;
|
||||
}
|
||||
const sleepDuration = parseInt(ns.args[0]);
|
||||
const hostname = ns.args[1].toString();
|
||||
const stock = ns.args[2] == "true";
|
||||
for(;;){
|
||||
await ns.weaken(hostname,{
|
||||
stock,
|
||||
});
|
||||
}
|
||||
}`,
|
||||
}
|
||||
]
|
||||
|
||||
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;
|
||||
/**
|
||||
* milliseconds to sleep before exec
|
||||
* @default 0
|
||||
*/
|
||||
sleepDuration?: number;
|
||||
/**
|
||||
* target hostname to operate "hack", "weaken" or "grow"
|
||||
*/
|
||||
target: string;
|
||||
stock?: boolean;
|
||||
}
|
||||
|
||||
function execBatchfile(ns: NS, path: string, option: ExecOption): number {
|
||||
const uuid = crypto.randomUUID();
|
||||
return ns.exec(path, option.hostname, option.numThread,
|
||||
option.sleepDuration ?? 0, option.target, option.stock ?? false, uuid);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
export function execWeakenLoop(ns: NS, option: ExecOption): number {
|
||||
return execBatchfile(ns, "/tmp/weakenLoop.js", option);
|
||||
}
|
||||
export function execScriptOf(ns: NS,type: BatchType, option:ExecOption):number {
|
||||
return execBatchfile(ns, "/tmp/" + type + ".js", option);
|
||||
}
|
||||
export function getRamOfScript(ns: NS, type: BatchType): number{
|
||||
return batchFilePack.filter(x=>x.type == type)[0].ram;
|
||||
}
|
||||
|
||||
type ReserveRequest = {
|
||||
hostname: string;
|
||||
usableRam: number;
|
||||
}
|
||||
|
||||
function getKeyOfHWGWBatchManager(hostname: string): string{
|
||||
return `HWGW-${hostname}`;
|
||||
}
|
||||
interface HWGW {
|
||||
HThreadCount: number;
|
||||
HWThreadCount: number;
|
||||
GThreadCount: number;
|
||||
GWThreadCount: number;
|
||||
}
|
||||
|
||||
function getTotalRamOf(hwgw: HWGW): number{
|
||||
return hwgw.HThreadCount * 1.7 + hwgw.HWThreadCount * 1.75 + hwgw.GThreadCount * 1.75 + hwgw.GWThreadCount * 1.75;
|
||||
}
|
||||
|
||||
type Resource = {
|
||||
hostname: string;
|
||||
usableRam: number;
|
||||
};
|
||||
|
||||
interface HackCalculation extends HWGW {
|
||||
earnMoneyPerCycle: number;
|
||||
earnExpPerCycle: number;
|
||||
/**
|
||||
* cycle time in milliseconds.
|
||||
*/
|
||||
cycleTime: number;
|
||||
totalThreadCount: number;
|
||||
|
||||
weakenTime: number;
|
||||
growTime: number;
|
||||
hackTime: number;
|
||||
}
|
||||
|
||||
export function calculateBatchResource(ns: NS, hostname: string, hackThread: number, startMoney?: number, cores = 1): HackCalculation| null {
|
||||
const helper = new FormulaHelper(ns);
|
||||
const server = ns.getServer(hostname);
|
||||
startMoney ??= server.moneyMax;
|
||||
server.hackDifficulty = server.minDifficulty;
|
||||
|
||||
const percentMoneyHacked = helper.calculatePercentMoneyHacked(server);
|
||||
const hackingChance = helper.calculateHackingChance(server);
|
||||
const earnMoneyPerCycle = hackThread * percentMoneyHacked * startMoney * hackingChance;
|
||||
const earnExpPerCycle = helper.calculateHackingExpGain(server) * hackThread * hackingChance;
|
||||
const cycleTime = 1000 + helper.calculateWeakenTime(server);
|
||||
const increasedSecurityAfterHack = helper.calculateHackSecurity(hackThread) + server.minDifficulty;
|
||||
const nWeakenHackThreads = Math.ceil(helper.calculateWeakenThreadCountToTargetSecurity(
|
||||
server.minDifficulty,
|
||||
increasedSecurityAfterHack, cores));
|
||||
if(startMoney <= earnMoneyPerCycle){
|
||||
return null;
|
||||
}
|
||||
const nGrowThreads = Math.ceil(helper.calculateGrowThreadCount(hostname, startMoney - earnMoneyPerCycle, startMoney, cores));
|
||||
const increasedSecurityAfterGrow = helper.calculuateGrowthSecurity(nGrowThreads, cores) + server.minDifficulty;
|
||||
|
||||
const nWeakenGrowThreads = Math.ceil(helper.calculateWeakenThreadCountToTargetSecurity(
|
||||
server.minDifficulty,
|
||||
increasedSecurityAfterGrow, cores));
|
||||
|
||||
return {
|
||||
weakenTime: helper.calculateWeakenTime(server),
|
||||
growTime: helper.calculateGrowTime(server),
|
||||
hackTime: helper.calculateHackingTime(server),
|
||||
earnMoneyPerCycle,
|
||||
earnExpPerCycle,
|
||||
cycleTime,
|
||||
totalThreadCount: hackThread + nWeakenHackThreads + nGrowThreads + nWeakenGrowThreads,
|
||||
HThreadCount: hackThread,
|
||||
HWThreadCount: nWeakenHackThreads,
|
||||
GThreadCount: nGrowThreads,
|
||||
GWThreadCount: nWeakenGrowThreads
|
||||
}
|
||||
}
|
||||
class BatchManager {
|
||||
server: Server;
|
||||
ns: NS;
|
||||
totalRam: number
|
||||
scheluder: Scheduler;
|
||||
resources: Resource[] = [];
|
||||
|
||||
constructor(ns: NS, hostname: string) {
|
||||
this.ns = ns;
|
||||
this.server = ns.getServer(hostname);
|
||||
this.totalRam = 0;
|
||||
this.scheluder = new Scheduler(ns, 250);
|
||||
}
|
||||
|
||||
reserve(hostname: string, usableRam: number): void {
|
||||
this.resources.push({
|
||||
hostname,
|
||||
usableRam,
|
||||
});
|
||||
this.totalRam += usableRam;
|
||||
}
|
||||
|
||||
getUsableTotalRam(): number {
|
||||
return this.resources.reduce((acc, cur) => acc + cur.usableRam, 0);
|
||||
}
|
||||
|
||||
execute(type: BatchType, opt: Omit<ExecOption, "hostname">): number[] {
|
||||
const ret = [];
|
||||
let numThread = opt.numThread ?? 1;
|
||||
const ramUnit = getRamOfScript(this.ns, type);
|
||||
for (let i = 0; i < this.resources.length; i++) {
|
||||
const resource = this.resources[i]
|
||||
let threadToRun = Math.floor(resource.usableRam / ramUnit);
|
||||
if (threadToRun > numThread) {
|
||||
threadToRun = numThread;
|
||||
}
|
||||
if (threadToRun <= 0) continue;
|
||||
|
||||
const pid = execScriptOf(this.ns, type, {
|
||||
hostname: resource.hostname,
|
||||
numThread: threadToRun,
|
||||
sleepDuration: opt.sleepDuration,
|
||||
target: opt.target,
|
||||
stock: opt.stock,
|
||||
});
|
||||
resource.usableRam -= threadToRun * ramUnit;
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
const doWork = ()=>{
|
||||
resource.usableRam += threadToRun * ramUnit;
|
||||
const msg = [
|
||||
`${pid} has finished.`,
|
||||
`${resource.hostname} recover ${threadToRun * ramUnit} ram`]
|
||||
this.ns.print(msg.join("\n"));
|
||||
}
|
||||
this.scheluder.onPidTerminate(pid, {work() {doWork();}});
|
||||
numThread -= threadToRun;
|
||||
ret.push(pid);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
class HWGWBatchManager extends BatchManager {
|
||||
constructor(ns: NS, hostname: string) {
|
||||
super(ns, hostname);
|
||||
}
|
||||
|
||||
calcBatchResource(hackThread: number): HackCalculation| null {
|
||||
return calculateBatchResource(this.ns, this.server.hostname, hackThread, this.server.moneyMax)
|
||||
}
|
||||
/**
|
||||
* execute batch
|
||||
* do not await this function.
|
||||
* @param hwgw HackCalculation of HWGW
|
||||
* @param stock if true, it influnces stock market.
|
||||
* @returns set of pids of scripts runned.
|
||||
*/
|
||||
async execBatch(hwgw: HackCalculation, stock?: boolean): Promise<number[]> {
|
||||
stock ??= false;
|
||||
//this.ns.print(`executing batch of ${hwgw.totalThreadCount} threads.`);
|
||||
//this.ns.print(`total ram: ${this.totalRam}`);
|
||||
|
||||
this.ns.print(`stage 1: executed ${hwgw.HThreadCount} threads.`);
|
||||
const hpid = this.execute("hack",{
|
||||
target:this.server.hostname,
|
||||
numThread:hwgw.HThreadCount,
|
||||
sleepDuration: hwgw.cycleTime - hwgw.hackTime - 1000,
|
||||
stock
|
||||
});
|
||||
await this.scheluder.sleepInWork(250);
|
||||
this.ns.print(`stage 2: executed ${hwgw.HWThreadCount} threads.`);
|
||||
const wpid = this.execute("weaken",{
|
||||
target:this.server.hostname,
|
||||
numThread:hwgw.HWThreadCount,
|
||||
sleepDuration: hwgw.cycleTime - hwgw.weakenTime - 1000,
|
||||
stock
|
||||
});
|
||||
await this.scheluder.sleepInWork(250);
|
||||
this.ns.print(`stage 3: executed ${hwgw.GThreadCount} threads.`);
|
||||
const gpid = this.execute("grow",{
|
||||
target:this.server.hostname,
|
||||
numThread:hwgw.GThreadCount,
|
||||
sleepDuration: hwgw.cycleTime - hwgw.growTime - 1000,
|
||||
stock
|
||||
});
|
||||
await this.scheluder.sleepInWork(250);
|
||||
this.ns.print(`stage 4: executed ${hwgw.GWThreadCount} threads.`);
|
||||
const gwpid = this.execute("weaken",{
|
||||
target:this.server.hostname,
|
||||
numThread:hwgw.GWThreadCount,
|
||||
sleepDuration: hwgw.cycleTime - hwgw.weakenTime - 1000,
|
||||
stock
|
||||
});
|
||||
await this.scheluder.sleepInWork(250);
|
||||
return [...hpid, ...wpid, ...gpid, ...gwpid];
|
||||
}
|
||||
|
||||
calcHWGW(): HackCalculation {
|
||||
const helper = new FormulaHelper(this.ns);
|
||||
const cycleTime = 1 + helper.calculateWeakenTime(this.server);
|
||||
const ramPerSec = Math.floor(this.totalRam / (cycleTime / 1000));
|
||||
const [min,minInfo] = this.getMinThreadCount();
|
||||
const minRam = getTotalRamOf(minInfo);
|
||||
if(minRam > ramPerSec){
|
||||
return minInfo;
|
||||
}
|
||||
// linear search
|
||||
let prev = minInfo;
|
||||
for (let i = min+1; i < min + 100; i++) {
|
||||
const info = this.calcBatchResource(i);
|
||||
if(info === null) break;
|
||||
if(getTotalRamOf(info) > ramPerSec){
|
||||
return prev;
|
||||
}
|
||||
prev = info;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
getMinThreadCount():[number,HackCalculation]{
|
||||
let minCE = 0;
|
||||
let minHC = 0;
|
||||
let minInfo: HackCalculation;
|
||||
for (let i = 1; i < 100; i++) {
|
||||
const info = this.calcBatchResource(i);
|
||||
if(info === null) break;
|
||||
const ce = info.earnMoneyPerCycle / info.totalThreadCount;
|
||||
if (ce >= minCE) {
|
||||
minCE = ce;
|
||||
minHC = i;
|
||||
minInfo = info;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return [minHC,minInfo!];
|
||||
}
|
||||
}
|
||||
|
||||
export function requestHWGW(req: ReserveRequest, target: string): void {
|
||||
const key = getKeyOfHWGWBatchManager(target);
|
||||
const data = localStorage.getItem(key);
|
||||
let requests: ReserveRequest[];
|
||||
if (data) {
|
||||
requests = JSON.parse(data);
|
||||
}
|
||||
else {
|
||||
requests = [];
|
||||
}
|
||||
requests.push(req);
|
||||
localStorage.setItem(key, JSON.stringify(requests));
|
||||
}
|
||||
|
||||
export function popHWGWRequest(hostname: string): ReserveRequest[]{
|
||||
const key = getKeyOfHWGWBatchManager(hostname);
|
||||
const text = localStorage.getItem(key)
|
||||
if(text === null){
|
||||
return [];
|
||||
}
|
||||
localStorage.setItem(key,"[]");
|
||||
return JSON.parse(text) as ReserveRequest[];
|
||||
}
|
||||
|
||||
export async function batchDaemon(ns: NS, hostname: string): Promise<void> {
|
||||
ns.disableLog("getBitNodeMultipliers");
|
||||
ns.disableLog("sleep");
|
||||
const mgr = new HWGWBatchManager(ns, hostname);
|
||||
for (;;) {
|
||||
const requests = popHWGWRequest(hostname);
|
||||
for (const req of requests) {
|
||||
mgr.reserve(req.hostname, req.usableRam);
|
||||
}
|
||||
const hwgw = mgr.calcHWGW();
|
||||
if (hwgw !== null && mgr.getUsableTotalRam() >= getTotalRamOf(hwgw)) {
|
||||
ns.print(`hwgw: total ${hwgw.totalThreadCount} threads.`);
|
||||
mgr.execBatch(hwgw);
|
||||
}
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await mgr.scheluder.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyOfGrowBatchManager(hostname: string): string{
|
||||
return `Grow-${hostname}`;
|
||||
}
|
||||
export function popGrowRequest(hostname: string): ReserveRequest[]{
|
||||
const key = getKeyOfGrowBatchManager(hostname);
|
||||
const text = localStorage.getItem(key)
|
||||
if(text === null){
|
||||
return [];
|
||||
}
|
||||
localStorage.setItem(key,"[]");
|
||||
return JSON.parse(text) as ReserveRequest[];
|
||||
}
|
||||
export function requestGrow(req: ReserveRequest, target: string): void {
|
||||
const key = getKeyOfGrowBatchManager(target);
|
||||
const data = localStorage.getItem(key);
|
||||
let requests: ReserveRequest[];
|
||||
if (data) {
|
||||
requests = JSON.parse(data);
|
||||
}
|
||||
else {
|
||||
requests = [];
|
||||
}
|
||||
requests.push(req);
|
||||
localStorage.setItem(key, JSON.stringify(requests));
|
||||
}
|
||||
|
||||
|
||||
class GrowManager extends BatchManager {
|
||||
constructor(ns: NS, hostname: string) {
|
||||
super(ns, hostname);
|
||||
}
|
||||
|
||||
async execStraigy(stock: boolean): Promise<void> {
|
||||
const helper = new FormulaHelper(this.ns);
|
||||
const difficultyMargin = 1;
|
||||
if(this.server.hackDifficulty > this.server.minDifficulty + difficultyMargin){
|
||||
const weakenThread = helper.calculateWeakenThreadCountToTargetSecurity(
|
||||
this.server.hackDifficulty, this.server.minDifficulty);
|
||||
const g = this.execute("weaken", {
|
||||
target: this.server.hostname,
|
||||
numThread: weakenThread,
|
||||
sleepDuration: 1,
|
||||
stock
|
||||
});
|
||||
this.ns.print("[PROCESS] weakened");
|
||||
const work = async (): Promise<void> => {
|
||||
await this.execStraigy(stock);
|
||||
}
|
||||
this.scheluder.onPidTerminate(g[0], {
|
||||
work
|
||||
});
|
||||
}
|
||||
|
||||
const moneyFactor = 0.8;
|
||||
if(this.server.moneyAvailable > this.server.moneyMax * moneyFactor){
|
||||
return;
|
||||
}
|
||||
const totalRam = this.getUsableTotalRam();
|
||||
const growThread = Math.floor(totalRam / (getRamOfScript(this.ns, "grow") * 3));
|
||||
const weakenThread = growThread * 2;
|
||||
const growTime = helper.calculateGrowTime(this.server);
|
||||
const weakenTime = helper.calculateWeakenTime(this.server);
|
||||
|
||||
this.execute("weaken", {
|
||||
target: this.server.hostname,
|
||||
numThread: weakenThread,
|
||||
sleepDuration: 0,
|
||||
});
|
||||
await this.scheluder.sleepInWork(250);
|
||||
const g = this.execute("grow", {
|
||||
target: this.server.hostname,
|
||||
numThread: growThread,
|
||||
sleepDuration: weakenTime - growTime,
|
||||
stock
|
||||
});
|
||||
|
||||
const work = async (): Promise<void> => {
|
||||
await this.execStraigy(stock);
|
||||
}
|
||||
this.scheluder.onPidTerminate(g[0], {
|
||||
work
|
||||
});
|
||||
}
|
||||
|
||||
empty(): boolean {
|
||||
return this.scheluder.empty();
|
||||
}
|
||||
}
|
||||
|
||||
export async function growDaemon(ns: NS, hostname: string): Promise<void> {
|
||||
ns.disableLog("getBitNodeMultipliers");
|
||||
ns.disableLog("sleep");
|
||||
const mgr = new GrowManager(ns, hostname);
|
||||
processRequest();
|
||||
mgr.execStraigy(false);
|
||||
while (!mgr.empty()) {
|
||||
processRequest()
|
||||
await mgr.scheluder.tick();
|
||||
}
|
||||
|
||||
function processRequest(): void{
|
||||
const requests = popGrowRequest(hostname);
|
||||
for (const req of requests) {
|
||||
mgr.reserve(req.hostname, req.usableRam);
|
||||
}
|
||||
}
|
||||
}
|
536
src/lib/colors.ts
Normal file
536
src/lib/colors.ts
Normal file
@ -0,0 +1,536 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
// A module to print ANSI terminal colors. Inspired by chalk, kleur, and colors
|
||||
// on npm.
|
||||
|
||||
/**
|
||||
* String formatters and utilities for dealing with ANSI color codes.
|
||||
*
|
||||
* This module is browser compatible.
|
||||
*
|
||||
* ```ts
|
||||
* import { bgBlue, red, bold } from "https://deno.land/std@$STD_VERSION/fmt/colors.ts";
|
||||
* console.log(bgBlue(red(bold("Hello world!"))));
|
||||
* ```
|
||||
*
|
||||
* This module supports `NO_COLOR` environmental variable disabling any coloring
|
||||
* if `NO_COLOR` is set.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
const noColor = false;
|
||||
|
||||
interface Code {
|
||||
open: string;
|
||||
close: string;
|
||||
regexp: RegExp;
|
||||
}
|
||||
|
||||
/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */
|
||||
interface Rgb {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
let enabled = !noColor;
|
||||
|
||||
/**
|
||||
* Set changing text color to enabled or disabled
|
||||
* @param value
|
||||
*/
|
||||
export function setColorEnabled(value: boolean) {
|
||||
if (noColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
enabled = value;
|
||||
}
|
||||
|
||||
/** Get whether text color change is enabled or disabled. */
|
||||
export function getColorEnabled(): boolean {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds color code
|
||||
* @param open
|
||||
* @param close
|
||||
*/
|
||||
function code(open: number[], close: number): Code {
|
||||
return {
|
||||
open: `\x1b[${open.join(";")}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies color and background based on color code and its associated text
|
||||
* @param str text to apply color settings to
|
||||
* @param code color code to apply
|
||||
*/
|
||||
function run(str: string, code: Code): string {
|
||||
return enabled
|
||||
? `${code.open}${str.replace(code.regexp, code.open)}${code.close}`
|
||||
: str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the text modified
|
||||
* @param str text to reset
|
||||
*/
|
||||
export function reset(str: string): string {
|
||||
return run(str, code([0], 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text bold.
|
||||
* @param str text to make bold
|
||||
*/
|
||||
export function bold(str: string): string {
|
||||
return run(str, code([1], 22));
|
||||
}
|
||||
|
||||
/**
|
||||
* The text emits only a small amount of light.
|
||||
* @param str text to dim
|
||||
*/
|
||||
export function dim(str: string): string {
|
||||
return run(str, code([2], 22));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text italic.
|
||||
* @param str text to make italic
|
||||
*/
|
||||
export function italic(str: string): string {
|
||||
return run(str, code([3], 23));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text underline.
|
||||
* @param str text to underline
|
||||
*/
|
||||
export function underline(str: string): string {
|
||||
return run(str, code([4], 24));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert background color and text color.
|
||||
* @param str text to invert its color
|
||||
*/
|
||||
export function inverse(str: string): string {
|
||||
return run(str, code([7], 27));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text hidden.
|
||||
* @param str text to hide
|
||||
*/
|
||||
export function hidden(str: string): string {
|
||||
return run(str, code([8], 28));
|
||||
}
|
||||
|
||||
/**
|
||||
* Put horizontal line through the center of the text.
|
||||
* @param str text to strike through
|
||||
*/
|
||||
export function strikethrough(str: string): string {
|
||||
return run(str, code([9], 29));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to black.
|
||||
* @param str text to make black
|
||||
*/
|
||||
export function black(str: string): string {
|
||||
return run(str, code([30], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to red.
|
||||
* @param str text to make red
|
||||
*/
|
||||
export function red(str: string): string {
|
||||
return run(str, code([31], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to green.
|
||||
* @param str text to make green
|
||||
*/
|
||||
export function green(str: string): string {
|
||||
return run(str, code([32], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to yellow.
|
||||
* @param str text to make yellow
|
||||
*/
|
||||
export function yellow(str: string): string {
|
||||
return run(str, code([33], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to blue.
|
||||
* @param str text to make blue
|
||||
*/
|
||||
export function blue(str: string): string {
|
||||
return run(str, code([34], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to magenta.
|
||||
* @param str text to make magenta
|
||||
*/
|
||||
export function magenta(str: string): string {
|
||||
return run(str, code([35], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to cyan.
|
||||
* @param str text to make cyan
|
||||
*/
|
||||
export function cyan(str: string): string {
|
||||
return run(str, code([36], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to white.
|
||||
* @param str text to make white
|
||||
*/
|
||||
export function white(str: string): string {
|
||||
return run(str, code([37], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to gray.
|
||||
* @param str text to make gray
|
||||
*/
|
||||
export function gray(str: string): string {
|
||||
return brightBlack(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright black.
|
||||
* @param str text to make bright-black
|
||||
*/
|
||||
export function brightBlack(str: string): string {
|
||||
return run(str, code([90], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright red.
|
||||
* @param str text to make bright-red
|
||||
*/
|
||||
export function brightRed(str: string): string {
|
||||
return run(str, code([91], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright green.
|
||||
* @param str text to make bright-green
|
||||
*/
|
||||
export function brightGreen(str: string): string {
|
||||
return run(str, code([92], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright yellow.
|
||||
* @param str text to make bright-yellow
|
||||
*/
|
||||
export function brightYellow(str: string): string {
|
||||
return run(str, code([93], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright blue.
|
||||
* @param str text to make bright-blue
|
||||
*/
|
||||
export function brightBlue(str: string): string {
|
||||
return run(str, code([94], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright magenta.
|
||||
* @param str text to make bright-magenta
|
||||
*/
|
||||
export function brightMagenta(str: string): string {
|
||||
return run(str, code([95], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright cyan.
|
||||
* @param str text to make bright-cyan
|
||||
*/
|
||||
export function brightCyan(str: string): string {
|
||||
return run(str, code([96], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright white.
|
||||
* @param str text to make bright-white
|
||||
*/
|
||||
export function brightWhite(str: string): string {
|
||||
return run(str, code([97], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to black.
|
||||
* @param str text to make its background black
|
||||
*/
|
||||
export function bgBlack(str: string): string {
|
||||
return run(str, code([40], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to red.
|
||||
* @param str text to make its background red
|
||||
*/
|
||||
export function bgRed(str: string): string {
|
||||
return run(str, code([41], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to green.
|
||||
* @param str text to make its background green
|
||||
*/
|
||||
export function bgGreen(str: string): string {
|
||||
return run(str, code([42], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to yellow.
|
||||
* @param str text to make its background yellow
|
||||
*/
|
||||
export function bgYellow(str: string): string {
|
||||
return run(str, code([43], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to blue.
|
||||
* @param str text to make its background blue
|
||||
*/
|
||||
export function bgBlue(str: string): string {
|
||||
return run(str, code([44], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to magenta.
|
||||
* @param str text to make its background magenta
|
||||
*/
|
||||
export function bgMagenta(str: string): string {
|
||||
return run(str, code([45], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to cyan.
|
||||
* @param str text to make its background cyan
|
||||
*/
|
||||
export function bgCyan(str: string): string {
|
||||
return run(str, code([46], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to white.
|
||||
* @param str text to make its background white
|
||||
*/
|
||||
export function bgWhite(str: string): string {
|
||||
return run(str, code([47], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright black.
|
||||
* @param str text to make its background bright-black
|
||||
*/
|
||||
export function bgBrightBlack(str: string): string {
|
||||
return run(str, code([100], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright red.
|
||||
* @param str text to make its background bright-red
|
||||
*/
|
||||
export function bgBrightRed(str: string): string {
|
||||
return run(str, code([101], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright green.
|
||||
* @param str text to make its background bright-green
|
||||
*/
|
||||
export function bgBrightGreen(str: string): string {
|
||||
return run(str, code([102], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright yellow.
|
||||
* @param str text to make its background bright-yellow
|
||||
*/
|
||||
export function bgBrightYellow(str: string): string {
|
||||
return run(str, code([103], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright blue.
|
||||
* @param str text to make its background bright-blue
|
||||
*/
|
||||
export function bgBrightBlue(str: string): string {
|
||||
return run(str, code([104], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright magenta.
|
||||
* @param str text to make its background bright-magenta
|
||||
*/
|
||||
export function bgBrightMagenta(str: string): string {
|
||||
return run(str, code([105], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright cyan.
|
||||
* @param str text to make its background bright-cyan
|
||||
*/
|
||||
export function bgBrightCyan(str: string): string {
|
||||
return run(str, code([106], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright white.
|
||||
* @param str text to make its background bright-white
|
||||
*/
|
||||
export function bgBrightWhite(str: string): string {
|
||||
return run(str, code([107], 49));
|
||||
}
|
||||
|
||||
/* Special Color Sequences */
|
||||
|
||||
/**
|
||||
* Clam and truncate color codes
|
||||
* @param n
|
||||
* @param max number to truncate to
|
||||
* @param min number to truncate from
|
||||
*/
|
||||
function clampAndTruncate(n: number, max = 255, min = 0): number {
|
||||
return Math.trunc(Math.max(Math.min(n, max), min));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color using paletted 8bit colors.
|
||||
* https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
|
||||
* @param str text color to apply paletted 8bit colors to
|
||||
* @param color code
|
||||
*/
|
||||
export function rgb8(str: string, color: number): string {
|
||||
return run(str, code([38, 5, clampAndTruncate(color)], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color using paletted 8bit colors.
|
||||
* https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
|
||||
* @param str text color to apply paletted 8bit background colors to
|
||||
* @param color code
|
||||
*/
|
||||
export function bgRgb8(str: string, color: number): string {
|
||||
return run(str, code([48, 5, clampAndTruncate(color)], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color using 24bit rgb.
|
||||
* `color` can be a number in range `0x000000` to `0xffffff` or
|
||||
* an `Rgb`.
|
||||
*
|
||||
* To produce the color magenta:
|
||||
*
|
||||
* ```ts
|
||||
* import { rgb24 } from "./colors.ts";
|
||||
* rgb24("foo", 0xff00ff);
|
||||
* rgb24("foo", {r: 255, g: 0, b: 255});
|
||||
* ```
|
||||
* @param str text color to apply 24bit rgb to
|
||||
* @param color code
|
||||
*/
|
||||
export function rgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === "number") {
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff],
|
||||
39,
|
||||
),
|
||||
);
|
||||
}
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[
|
||||
38,
|
||||
2,
|
||||
clampAndTruncate(color.r),
|
||||
clampAndTruncate(color.g),
|
||||
clampAndTruncate(color.b),
|
||||
],
|
||||
39,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color using 24bit rgb.
|
||||
* `color` can be a number in range `0x000000` to `0xffffff` or
|
||||
* an `Rgb`.
|
||||
*
|
||||
* To produce the color magenta:
|
||||
*
|
||||
* ```ts
|
||||
* import { bgRgb24 } from "./colors.ts";
|
||||
* bgRgb24("foo", 0xff00ff);
|
||||
* bgRgb24("foo", {r: 255, g: 0, b: 255});
|
||||
* ```
|
||||
* @param str text color to apply 24bit rgb to
|
||||
* @param color code
|
||||
*/
|
||||
export function bgRgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === "number") {
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff],
|
||||
49,
|
||||
),
|
||||
);
|
||||
}
|
||||
return run(
|
||||
str,
|
||||
code(
|
||||
[
|
||||
48,
|
||||
2,
|
||||
clampAndTruncate(color.r),
|
||||
clampAndTruncate(color.g),
|
||||
clampAndTruncate(color.b),
|
||||
],
|
||||
49,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js
|
||||
const ANSI_PATTERN = new RegExp(
|
||||
[
|
||||
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
||||
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))",
|
||||
].join("|"),
|
||||
"g",
|
||||
);
|
||||
|
||||
/**
|
||||
* Remove ANSI escape codes from the string.
|
||||
* @param string to remove ANSI escape codes from
|
||||
*/
|
||||
export function stripColor(string: string): string {
|
||||
return string.replace(ANSI_PATTERN, "");
|
||||
}
|
228
src/lib/contract.ts
Normal file
228
src/lib/contract.ts
Normal file
@ -0,0 +1,228 @@
|
||||
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";
|
||||
import {caesarCipherSolve} from "./solve/caesarCipher";
|
||||
|
||||
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: caesarCipherSolve,
|
||||
},/*
|
||||
"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;
|
||||
}
|
456
src/lib/flag.ts
Normal file
456
src/lib/flag.ts
Normal file
@ -0,0 +1,456 @@
|
||||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
/**
|
||||
* CLI flag parser.
|
||||
*
|
||||
* This module is browser compatible.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
import { assert } from "../util/assert";
|
||||
|
||||
/** The value returned from `parse`. */
|
||||
export interface Args {
|
||||
/** Contains all the arguments that didn't have an option associated with
|
||||
* them. */
|
||||
_: Array<string | number>;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/** The options for the `parse` call. */
|
||||
export interface ParseOptions {
|
||||
/** When `true`, populate the result `_` with everything before the `--` and
|
||||
* the result `['--']` with everything after the `--`. Here's an example:
|
||||
*
|
||||
* ```ts
|
||||
* // $ deno run example.ts -- a arg1
|
||||
* import { parse } from "./mod.ts";
|
||||
* console.dir(parse(Deno.args, { "--": false }));
|
||||
* // output: { _: [ "a", "arg1" ] }
|
||||
* console.dir(parse(Deno.args, { "--": true }));
|
||||
* // output: { _: [], --: [ "a", "arg1" ] }
|
||||
* ```
|
||||
*
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
"--"?: boolean;
|
||||
|
||||
/** An object mapping string names to strings or arrays of string argument
|
||||
* names to use as aliases. */
|
||||
alias?: Record<string, string | string[]>;
|
||||
|
||||
/** A boolean, string or array of strings to always treat as booleans. If
|
||||
* `true` will treat all double hyphenated arguments without equal signs as
|
||||
* `boolean` (e.g. affects `--foo`, not `-f` or `--foo=bar`) */
|
||||
boolean?: boolean | string | string[];
|
||||
|
||||
/** An object mapping string argument names to default values. */
|
||||
default?: Record<string, unknown>;
|
||||
|
||||
/** When `true`, populate the result `_` with everything after the first
|
||||
* non-option. */
|
||||
stopEarly?: boolean;
|
||||
|
||||
/** A string or array of strings argument names to always treat as strings. */
|
||||
string?: string | string[];
|
||||
|
||||
/** A string or array of strings argument names to always treat as arrays.
|
||||
* Collectable options can be used multiple times. All values will be
|
||||
* colelcted into one array. If a non-collectable option is used multiple
|
||||
* times, the last value is used. */
|
||||
collect?: string | string[];
|
||||
|
||||
/** A string or array of strings argument names which can be negated
|
||||
* by prefixing them with `--no-`, like `--no-config`. */
|
||||
negatable?: string | string[];
|
||||
|
||||
/** A function which is invoked with a command line parameter not defined in
|
||||
* the `options` configuration object. If the function returns `false`, the
|
||||
* unknown option is not added to `parsedArgs`. */
|
||||
unknown?: (arg: string, key?: string, value?: unknown) => unknown;
|
||||
}
|
||||
|
||||
interface Flags {
|
||||
bools: Record<string, boolean>;
|
||||
strings: Record<string, boolean>;
|
||||
collect: Record<string, boolean>;
|
||||
negatable: Record<string, boolean>;
|
||||
unknownFn: (arg: string, key?: string, value?: unknown) => unknown;
|
||||
allBools: boolean;
|
||||
}
|
||||
|
||||
interface NestedMapping {
|
||||
[key: string]: NestedMapping | unknown;
|
||||
}
|
||||
|
||||
const { hasOwn } = Object;
|
||||
|
||||
function get<T>(obj: Record<string, T>, key: string): T | undefined {
|
||||
if (hasOwn(obj, key)) {
|
||||
return obj[key];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getForce<T>(obj: Record<string, T>, key: string): T {
|
||||
const v = get(obj, key);
|
||||
assert(v != null);
|
||||
return v;
|
||||
}
|
||||
|
||||
function isNumber(x: unknown): boolean {
|
||||
if (typeof x === "number") return true;
|
||||
if (/^0x[0-9a-f]+$/i.test(String(x))) return true;
|
||||
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(x));
|
||||
}
|
||||
|
||||
function hasKey(obj: NestedMapping, keys: string[]): boolean {
|
||||
let o = obj;
|
||||
keys.slice(0, -1).forEach((key) => {
|
||||
o = (get(o, key) ?? {}) as NestedMapping;
|
||||
});
|
||||
|
||||
const key = keys[keys.length - 1];
|
||||
return key in o;
|
||||
}
|
||||
|
||||
/** Take a set of command line arguments, optionally with a set of options, and
|
||||
* return an object representing the flags found in the passed arguments.
|
||||
*
|
||||
* By default any arguments starting with `-` or `--` are considered boolean
|
||||
* flags. If the argument name is followed by an equal sign (`=`) it is
|
||||
* considered a key-value pair. Any arguments which could not be parsed are
|
||||
* available in the `_` property of the returned object.
|
||||
*
|
||||
* ```ts
|
||||
* import { parse } from "./mod.ts";
|
||||
* const parsedArgs = parse(Deno.args);
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* import { parse } from "./mod.ts";
|
||||
* const parsedArgs = parse(["--foo", "--bar=baz", "--no-qux", "./quux.txt"]);
|
||||
* // parsedArgs: { foo: true, bar: "baz", qux: false, _: ["./quux.txt"] }
|
||||
* ```
|
||||
*/
|
||||
export function parse(
|
||||
args: string[],
|
||||
{
|
||||
"--": doubleDash = false,
|
||||
alias = {},
|
||||
boolean = false,
|
||||
default: defaults = {},
|
||||
stopEarly = false,
|
||||
string = [],
|
||||
collect = [],
|
||||
negatable = [],
|
||||
unknown = (i: string): unknown => i,
|
||||
}: ParseOptions = {},
|
||||
): Args {
|
||||
const flags: Flags = {
|
||||
bools: {},
|
||||
strings: {},
|
||||
unknownFn: unknown,
|
||||
allBools: false,
|
||||
collect: {},
|
||||
negatable: {},
|
||||
};
|
||||
|
||||
if (boolean !== undefined) {
|
||||
if (typeof boolean === "boolean") {
|
||||
flags.allBools = !!boolean;
|
||||
} else {
|
||||
const booleanArgs = typeof boolean === "string" ? [boolean] : boolean;
|
||||
|
||||
for (const key of booleanArgs.filter(Boolean)) {
|
||||
flags.bools[key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const aliases: Record<string, string[]> = {};
|
||||
if (alias !== undefined) {
|
||||
for (const key in alias) {
|
||||
const val = getForce(alias, key);
|
||||
if (typeof val === "string") {
|
||||
aliases[key] = [val];
|
||||
} else {
|
||||
aliases[key] = val;
|
||||
}
|
||||
for (const alias of getForce(aliases, key)) {
|
||||
aliases[alias] = [key].concat(aliases[key].filter((y) => alias !== y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string !== undefined) {
|
||||
const stringArgs = typeof string === "string" ? [string] : string;
|
||||
|
||||
for (const key of stringArgs.filter(Boolean)) {
|
||||
flags.strings[key] = true;
|
||||
const alias = get(aliases, key);
|
||||
if (alias) {
|
||||
for (const al of alias) {
|
||||
flags.strings[al] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (collect !== undefined) {
|
||||
const collectArgs = typeof collect === "string" ? [collect] : collect;
|
||||
|
||||
for (const key of collectArgs.filter(Boolean)) {
|
||||
flags.collect[key] = true;
|
||||
const alias = get(aliases, key);
|
||||
if (alias) {
|
||||
for (const al of alias) {
|
||||
flags.collect[al] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (negatable !== undefined) {
|
||||
const negatableArgs = typeof negatable === "string"
|
||||
? [negatable]
|
||||
: negatable;
|
||||
|
||||
for (const key of negatableArgs.filter(Boolean)) {
|
||||
flags.negatable[key] = true;
|
||||
const alias = get(aliases, key);
|
||||
if (alias) {
|
||||
for (const al of alias) {
|
||||
flags.negatable[al] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const argv: Args = { _: [] };
|
||||
|
||||
function argDefined(key: string, arg: string): boolean {
|
||||
return (
|
||||
(flags.allBools && /^--[^=]+$/.test(arg)) ||
|
||||
get(flags.bools, key) ||
|
||||
!!get(flags.strings, key) ||
|
||||
!!get(aliases, key)
|
||||
);
|
||||
}
|
||||
|
||||
function setKey(
|
||||
obj: NestedMapping,
|
||||
name: string,
|
||||
value: unknown,
|
||||
collect = true,
|
||||
): void {
|
||||
let o = obj;
|
||||
const keys = name.split(".");
|
||||
keys.slice(0, -1).forEach(function (key): void {
|
||||
if (get(o, key) === undefined) {
|
||||
o[key] = {};
|
||||
}
|
||||
o = get(o, key) as NestedMapping;
|
||||
});
|
||||
|
||||
const key = keys[keys.length - 1];
|
||||
const collectable = collect && !!get(flags.collect, name);
|
||||
|
||||
if (!collectable) {
|
||||
o[key] = value;
|
||||
} else if (get(o, key) === undefined) {
|
||||
o[key] = [value];
|
||||
} else if (Array.isArray(get(o, key))) {
|
||||
(o[key] as unknown[]).push(value);
|
||||
} else {
|
||||
o[key] = [get(o, key), value];
|
||||
}
|
||||
}
|
||||
|
||||
function setArg(
|
||||
key: string,
|
||||
val: unknown,
|
||||
arg: string | undefined = undefined,
|
||||
collect?: boolean,
|
||||
): void {
|
||||
if (arg && flags.unknownFn && !argDefined(key, arg)) {
|
||||
if (flags.unknownFn(arg, key, val) === false) return;
|
||||
}
|
||||
|
||||
const value = !get(flags.strings, key) && isNumber(val) ? Number(val) : val;
|
||||
setKey(argv, key, value, collect);
|
||||
|
||||
const alias = get(aliases, key);
|
||||
if (alias) {
|
||||
for (const x of alias) {
|
||||
setKey(argv, x, value, collect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function aliasIsBoolean(key: string): boolean {
|
||||
return getForce(aliases, key).some(
|
||||
(x) => typeof get(flags.bools, x) === "boolean",
|
||||
);
|
||||
}
|
||||
|
||||
let notFlags: string[] = [];
|
||||
|
||||
// all args after "--" are not parsed
|
||||
if (args.includes("--")) {
|
||||
notFlags = args.slice(args.indexOf("--") + 1);
|
||||
args = args.slice(0, args.indexOf("--"));
|
||||
}
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
|
||||
if (/^--.+=/.test(arg)) {
|
||||
const m = arg.match(/^--([^=]+)=(.*)$/s);
|
||||
assert(m != null);
|
||||
const [, key, value] = m;
|
||||
|
||||
if (flags.bools[key]) {
|
||||
const booleanValue = value !== "false";
|
||||
setArg(key, booleanValue, arg);
|
||||
} else {
|
||||
setArg(key, value, arg);
|
||||
}
|
||||
} else if (
|
||||
/^--no-.+/.test(arg) && get(flags.negatable, arg.replace(/^--no-/, ""))
|
||||
) {
|
||||
const m = arg.match(/^--no-(.+)/);
|
||||
assert(m != null);
|
||||
setArg(m[1], false, arg, false);
|
||||
} else if (/^--.+/.test(arg)) {
|
||||
const m = arg.match(/^--(.+)/);
|
||||
assert(m != null);
|
||||
const [, key] = m;
|
||||
const next = args[i + 1];
|
||||
if (
|
||||
next !== undefined &&
|
||||
!/^-/.test(next) &&
|
||||
!get(flags.bools, key) &&
|
||||
!flags.allBools &&
|
||||
(get(aliases, key) ? !aliasIsBoolean(key) : true)
|
||||
) {
|
||||
setArg(key, next, arg);
|
||||
i++;
|
||||
} else if (/^(true|false)$/.test(next)) {
|
||||
setArg(key, next === "true", arg);
|
||||
i++;
|
||||
} else {
|
||||
setArg(key, get(flags.strings, key) ? "" : true, arg);
|
||||
}
|
||||
} else if (/^-[^-]+/.test(arg)) {
|
||||
const letters = arg.slice(1, -1).split("");
|
||||
|
||||
let broken = false;
|
||||
for (let j = 0; j < letters.length; j++) {
|
||||
const next = arg.slice(j + 2);
|
||||
|
||||
if (next === "-") {
|
||||
setArg(letters[j], next, arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
|
||||
setArg(letters[j], next.split(/=(.+)/)[1], arg);
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
/[A-Za-z]/.test(letters[j]) &&
|
||||
/-?\d+(\.\d*)?(e-?\d+)?$/.test(next)
|
||||
) {
|
||||
setArg(letters[j], next, arg);
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
|
||||
setArg(letters[j], arg.slice(j + 2), arg);
|
||||
broken = true;
|
||||
break;
|
||||
} else {
|
||||
setArg(letters[j], get(flags.strings, letters[j]) ? "" : true, arg);
|
||||
}
|
||||
}
|
||||
|
||||
const [key] = arg.slice(-1);
|
||||
if (!broken && key !== "-") {
|
||||
if (
|
||||
args[i + 1] &&
|
||||
!/^(-|--)[^-]/.test(args[i + 1]) &&
|
||||
!get(flags.bools, key) &&
|
||||
(get(aliases, key) ? !aliasIsBoolean(key) : true)
|
||||
) {
|
||||
setArg(key, args[i + 1], arg);
|
||||
i++;
|
||||
} else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) {
|
||||
setArg(key, args[i + 1] === "true", arg);
|
||||
i++;
|
||||
} else {
|
||||
setArg(key, get(flags.strings, key) ? "" : true, arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
|
||||
argv._.push(flags.strings["_"] ?? !isNumber(arg) ? arg : Number(arg));
|
||||
}
|
||||
if (stopEarly) {
|
||||
argv._.push(...args.slice(i + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(defaults)) {
|
||||
if (!hasKey(argv, key.split("."))) {
|
||||
setKey(argv, key, value);
|
||||
|
||||
if (aliases[key]) {
|
||||
for (const x of aliases[key]) {
|
||||
setKey(argv, x, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(flags.bools)) {
|
||||
if (!hasKey(argv, key.split("."))) {
|
||||
const value = get(flags.collect, key) ? [] : false;
|
||||
setKey(
|
||||
argv,
|
||||
key,
|
||||
value,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(flags.strings)) {
|
||||
if (!hasKey(argv, key.split(".")) && get(flags.collect, key)) {
|
||||
setKey(
|
||||
argv,
|
||||
key,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (doubleDash) {
|
||||
argv["--"] = [];
|
||||
for (const key of notFlags) {
|
||||
argv["--"].push(key);
|
||||
}
|
||||
} else {
|
||||
for (const key of notFlags) {
|
||||
argv._.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
310
src/lib/formula.ts
Normal file
310
src/lib/formula.ts
Normal file
@ -0,0 +1,310 @@
|
||||
import { NS, Server, Player, BitNodeMultipliers } 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 DefaultBitNodeMultipliers: BitNodeMultipliers = {
|
||||
HackingLevelMultiplier: 1,
|
||||
StrengthLevelMultiplier: 1,
|
||||
DefenseLevelMultiplier: 1,
|
||||
DexterityLevelMultiplier: 1,
|
||||
AgilityLevelMultiplier: 1,
|
||||
CharismaLevelMultiplier: 1,
|
||||
|
||||
ServerGrowthRate: 1,
|
||||
ServerMaxMoney: 1,
|
||||
ServerStartingMoney: 1,
|
||||
ServerStartingSecurity: 1,
|
||||
ServerWeakenRate: 1,
|
||||
|
||||
HomeComputerRamCost: 1,
|
||||
|
||||
PurchasedServerCost: 1,
|
||||
PurchasedServerSoftcap: 1,
|
||||
PurchasedServerLimit: 1,
|
||||
PurchasedServerMaxRam: 1,
|
||||
|
||||
CompanyWorkMoney: 1,
|
||||
CrimeMoney: 1,
|
||||
HacknetNodeMoney: 1,
|
||||
ManualHackMoney: 1,
|
||||
ScriptHackMoney: 1,
|
||||
ScriptHackMoneyGain: 1,
|
||||
CodingContractMoney: 1,
|
||||
|
||||
ClassGymExpGain: 1,
|
||||
CompanyWorkExpGain: 1,
|
||||
CrimeExpGain: 1,
|
||||
FactionWorkExpGain: 1,
|
||||
HackExpGain: 1,
|
||||
|
||||
FactionPassiveRepGain: 1,
|
||||
FactionWorkRepGain: 1,
|
||||
RepToDonateToFaction: 1,
|
||||
|
||||
AugmentationMoneyCost: 1,
|
||||
AugmentationRepCost: 1,
|
||||
|
||||
InfiltrationMoney: 1,
|
||||
InfiltrationRep: 1,
|
||||
|
||||
FourSigmaMarketDataCost: 1,
|
||||
FourSigmaMarketDataApiCost: 1,
|
||||
|
||||
CorporationValuation: 1,
|
||||
CorporationSoftcap: 1,
|
||||
|
||||
BladeburnerRank: 1,
|
||||
BladeburnerSkillCost: 1,
|
||||
|
||||
GangSoftcap: 1,
|
||||
//GangUniqueAugs: 1,
|
||||
|
||||
DaedalusAugsRequirement: 30,
|
||||
|
||||
StaneksGiftPowerMultiplier: 1,
|
||||
StaneksGiftExtraSize: 0,
|
||||
|
||||
WorldDaemonDifficulty: 1,
|
||||
};
|
||||
|
||||
export function calculateIntelligenceBonus(intelligence: number, weight = 1): number {
|
||||
return 1 + (weight * Math.pow(intelligence, 0.8)) / 600;
|
||||
}
|
||||
|
||||
export function calculateServerGrowth(server: Server, threads: number, p: Player, cores = 1, bit: BitNodeMultipliers = DefaultBitNodeMultipliers): 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 * bit.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, bit: BitNodeMultipliers = DefaultBitNodeMultipliers): 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 * bit.HackExpGain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of money that will be stolen from a server if
|
||||
* it is successfully hacked (returns the decimal form, not the actual percent value)
|
||||
*/
|
||||
export function calculatePercentMoneyHacked(server: Server, player: Player, bit: BitNodeMultipliers = DefaultBitNodeMultipliers): number {
|
||||
// Adjust if needed for balancing. This is the divisor for the final calculation
|
||||
const balanceFactor = 240;
|
||||
|
||||
const difficultyMult = (100 - server.hackDifficulty) / 100;
|
||||
const skillMult = (player.skills.hacking - (server.requiredHackingSkill - 1)) / player.skills.hacking;
|
||||
const percentMoneyHacked =
|
||||
(difficultyMult * skillMult * player.mults.hacking_money * bit.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);
|
||||
}
|
||||
|
||||
export function canBeAccessBitNode(ns: NS): boolean {
|
||||
try {
|
||||
ns.getBitNodeMultipliers();
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getBitNodeMultipliersSafe(ns: NS): BitNodeMultipliers {
|
||||
const fnName = "getBitNodeMultipliers";
|
||||
const isEnabled = ns.isLogEnabled(fnName);
|
||||
ns.disableLog("disableLog");
|
||||
ns.disableLog("enableLog");
|
||||
ns.disableLog("getBitNodeMultipliers");
|
||||
try {
|
||||
return ns.getBitNodeMultipliers();
|
||||
} catch (error) {
|
||||
if (typeof error === "string") {
|
||||
return DefaultBitNodeMultipliers;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
if (isEnabled) {
|
||||
ns.enableLog("getBitNodeMultipliers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FormulaHelper {
|
||||
ns: NS;
|
||||
constructor(ns: NS) {
|
||||
this.ns = ns;
|
||||
}
|
||||
calculateWeakenThreadCountToTargetSecurity(targetSecurity: number, curSecurity: number, cores = 1): number {
|
||||
if(curSecurity < targetSecurity) {
|
||||
return 0;
|
||||
}
|
||||
const k = curSecurity - targetSecurity;
|
||||
const c = this.ns.weakenAnalyze(1, cores);
|
||||
return k / c;
|
||||
}
|
||||
calculateGrowThreadCount(hostname: string, currentMoney: number, targetMoney: number, cores = 1): number {
|
||||
const g = this.ns.growthAnalyze(hostname, targetMoney / currentMoney, cores);
|
||||
return g;
|
||||
}
|
||||
calculateHackSecurity(nThread: number): number {
|
||||
const ServerFortifyAmount = 0.002;
|
||||
return ServerFortifyAmount * nThread;
|
||||
}
|
||||
calculuateGrowthSecurity(nThread: number, cores = 1): number {
|
||||
const ServerWeakenAmount = 0.004;
|
||||
const mul = getBitNodeMultipliersSafe(this.ns);
|
||||
const coreBonus = 1 + (cores - 1) / 16;
|
||||
return ServerWeakenAmount * nThread * coreBonus * mul.ServerWeakenRate;
|
||||
}
|
||||
/**
|
||||
* Returns time it takes to complete a weaken operation on a server, in milliseconds
|
||||
* @param server
|
||||
* @returns milliseconds to complete a weaken operation
|
||||
*/
|
||||
calculateWeakenTime(server: Server): number {
|
||||
const player = this.ns.getPlayer();
|
||||
return calculateWeakenTime(server, player) * 1000;
|
||||
}
|
||||
/**
|
||||
* Returns time it takes to complete a grow operation on a server, in milliseconds
|
||||
* @param server
|
||||
* @returns milliseconds to complete a grow operation
|
||||
*/
|
||||
calculateGrowTime(server: Server): number {
|
||||
const player = this.ns.getPlayer();
|
||||
return calculateGrowTime(server, player) * 1000;
|
||||
}
|
||||
/**
|
||||
* Returns time it takes to complete a hacking operation on a server, in milliseconds
|
||||
* @param server
|
||||
* @returns milliseconds to complete a hacking operation
|
||||
*/
|
||||
calculateHackingTime(server: Server): number {
|
||||
const player = this.ns.getPlayer();
|
||||
return calculateHackingTime(server, player) * 1000;
|
||||
}
|
||||
/**
|
||||
* Hack
|
||||
* @param server server to get
|
||||
* @returns percent of earned money (0~1 value)
|
||||
* @example
|
||||
* ```ts
|
||||
* calculatePercentMoneyHacked(server) //0.2 mean 20%
|
||||
* ```
|
||||
*/
|
||||
calculatePercentMoneyHacked(server: Server): number {
|
||||
const player = this.ns.getPlayer();
|
||||
const bit = getBitNodeMultipliersSafe(this.ns);
|
||||
return calculatePercentMoneyHacked(server, player, bit);
|
||||
}
|
||||
calculateServerGrowth(server: Server, thread: number, cores = 1): number {
|
||||
const player = this.ns.getPlayer();
|
||||
const bit = getBitNodeMultipliersSafe(this.ns);
|
||||
return calculateServerGrowth(server, thread, player, cores, bit);
|
||||
}
|
||||
calculateHackingChance(server: Server): number {
|
||||
const player = this.ns.getPlayer();
|
||||
return calculateHackingChance(server, player);
|
||||
}
|
||||
calculateHackingExpGain(server: Server): number{
|
||||
const player = this.ns.getPlayer();
|
||||
const bit = getBitNodeMultipliersSafe(this.ns);
|
||||
return calculateHackingExpGain(server,player,bit);
|
||||
}
|
||||
}
|
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
97
src/lib/hack.ts
Normal file
97
src/lib/hack.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { NS, Server, AutocompleteData} from '@ns'
|
||||
import {selectAllServerList, ServerInfo} from "./servers"
|
||||
|
||||
export type Hackability = "hackable" | "rooted" | "impossible"
|
||||
|
||||
export function getHackability(ns: NS,hostname:string): Hackability{
|
||||
if (ns.hasRootAccess(hostname)) {
|
||||
return "rooted";
|
||||
}
|
||||
if (ns.getServerRequiredHackingLevel(hostname) > ns.getHackingLevel() ||
|
||||
ns.getServerNumPortsRequired(hostname) > hackablePorts(ns)) {
|
||||
return "impossible";
|
||||
}
|
||||
return "hackable";
|
||||
}
|
||||
export function getHackabilityServer(ns: NS,server:Server): Hackability{
|
||||
if (server.hasAdminRights) {
|
||||
return "rooted";
|
||||
}
|
||||
if (server.requiredHackingSkill > ns.getHackingLevel() ||
|
||||
server.numOpenPortsRequired > hackablePorts(ns)) {
|
||||
return "impossible";
|
||||
}
|
||||
return "hackable";
|
||||
}
|
||||
|
||||
export function getLockSymbol(k:Hackability):string{
|
||||
switch (k) {
|
||||
case "rooted":
|
||||
return "🔓"
|
||||
case "hackable":
|
||||
return "🔐"
|
||||
case "impossible":
|
||||
return "🔒"
|
||||
}
|
||||
}
|
||||
|
||||
type Hack = {
|
||||
file: string;
|
||||
exec: (ns: NS, target:string)=> void;
|
||||
};
|
||||
|
||||
export const hacks: Hack[] = [
|
||||
{ file: 'BruteSSH.exe', exec: (ns: NS, target: string) => ns.brutessh(target) },
|
||||
{ file: 'FTPCrack.exe', exec: (ns: NS, target: string) => ns.ftpcrack(target) },
|
||||
{ file: 'relaySMTP.exe', exec: (ns: NS, target: string) => ns.relaysmtp(target) },
|
||||
{ file: 'HTTPWorm.exe', exec: (ns: NS, target: string) => ns.httpworm(target) },
|
||||
{ file: 'SQLInject.exe', exec: (ns: NS, target: string) => ns.sqlinject(target) },
|
||||
]
|
||||
|
||||
/**
|
||||
* @param {NS} ns
|
||||
*/
|
||||
export function hackablePorts(ns: NS): number {
|
||||
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{
|
||||
return target.numOpenPortsRequired <= hackablePorts(ns) &&
|
||||
target.hackDifficulty <= ns.getHackingLevel()
|
||||
}
|
||||
export function selectHackableServerList(ns: NS): ServerInfo[]{
|
||||
return selectAllServerList(ns).filter(x=>{
|
||||
return isHackableServer(ns,x)
|
||||
})
|
||||
}
|
||||
|
||||
export function hackServer(ns:NS,target:Server):void;
|
||||
export function hackServer(ns:NS,target:string):void;
|
||||
export function hackServer(ns:NS,target:string|Server):void{
|
||||
const hostname = typeof target === "string" ? target : target.hostname
|
||||
hacks.forEach(x=>{
|
||||
if(ns.fileExists(x.file,"home")){
|
||||
x.exec(ns, hostname)
|
||||
}
|
||||
})
|
||||
ns.nuke(hostname)
|
||||
}
|
||||
|
||||
/** @param {NS} ns */
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS):Promise<void> {
|
||||
if(ns.args.length == 0){
|
||||
ns.tprint("argument required")
|
||||
ns.tprint("run cmd [target]")
|
||||
return
|
||||
}
|
||||
const target = String(ns.args[0]);
|
||||
hackServer(ns,target)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
|
||||
return [...data.servers]
|
||||
}
|
14
src/lib/log.ts
Normal file
14
src/lib/log.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {NS} from "@ns"
|
||||
|
||||
export async function logFile(ns:NS,logFilename:string, data: unknown):Promise<void>{
|
||||
logFilename = logFilename.replaceAll(" ","-");
|
||||
logFilename = logFilename.replaceAll(":","-");
|
||||
logFilename = `/log/${logFilename}.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);
|
||||
}
|
185
src/lib/scheduler.ts
Normal file
185
src/lib/scheduler.ts
Normal file
@ -0,0 +1,185 @@
|
||||
import {NS} from "@ns";
|
||||
|
||||
/**
|
||||
* tick in scheduler
|
||||
*/
|
||||
export interface Tickable {
|
||||
/**
|
||||
* tick in scheduler
|
||||
*/
|
||||
tick(): void;
|
||||
/**
|
||||
* no work to run in this tick.
|
||||
*/
|
||||
empty(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* worker in scheduler
|
||||
*/
|
||||
export interface Work {
|
||||
work: (() => Promise<void>) | (()=>void);
|
||||
}
|
||||
|
||||
/**
|
||||
* set of work to be executed at the same time
|
||||
*/
|
||||
export class WorkSet implements Work{
|
||||
works: Work[];
|
||||
constructor(){
|
||||
this.works = [];
|
||||
}
|
||||
add(work: Work): void{
|
||||
this.works.push(work);
|
||||
}
|
||||
remove(work: Work): void{
|
||||
this.works = this.works.filter(x=>x !== work);
|
||||
}
|
||||
concat(other: WorkSet): void{
|
||||
this.works = this.works.concat(other.works);
|
||||
}
|
||||
async work(): Promise<void>{
|
||||
for(const work of this.works){
|
||||
await work.work();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeWork(...args: Work[]): WorkSet{
|
||||
const workSet = new WorkSet();
|
||||
for(const work of args){
|
||||
if(work instanceof WorkSet){
|
||||
workSet.concat(work);
|
||||
}
|
||||
else {
|
||||
workSet.add(work);
|
||||
}
|
||||
}
|
||||
return workSet;
|
||||
}
|
||||
|
||||
class PidTerminateExecutor implements Tickable{
|
||||
ns: NS;
|
||||
lastCheck: number;
|
||||
pidSet: Map<number, Work>;
|
||||
|
||||
constructor(ns: NS){
|
||||
this.ns = ns;
|
||||
this.lastCheck = Date.now();
|
||||
this.pidSet = new Map();
|
||||
}
|
||||
watchPid(pid: number, work: Work): void{
|
||||
if(this.pidSet.has(pid)){
|
||||
throw new Error(`pid ${pid} is already watched`);
|
||||
}
|
||||
this.pidSet.set(pid, work);
|
||||
}
|
||||
|
||||
empty(): boolean{
|
||||
return this.pidSet.size === 0;
|
||||
}
|
||||
|
||||
async tick(): Promise<void>{
|
||||
const current = Date.now();
|
||||
const deadScripts = this.ns.getRecentScripts().filter(x=>x.timeOfDeath.getTime() >= this.lastCheck);
|
||||
for(const script of deadScripts){
|
||||
const target = this.pidSet.get(script.pid);
|
||||
if(target){
|
||||
await target.work();
|
||||
}
|
||||
this.pidSet.delete(script.pid);
|
||||
}
|
||||
this.lastCheck = current;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeTime(time: number, timeResolution: number): number{
|
||||
return Math.floor(time / timeResolution) * timeResolution;
|
||||
}
|
||||
|
||||
class TimerExecutor implements Tickable{
|
||||
ns: NS;
|
||||
timerResolution: number;
|
||||
timerStart: number;
|
||||
workBucket: Map<number, WorkSet>;
|
||||
constructor(ns: NS, timerResolution = 250){
|
||||
this.ns = ns;
|
||||
this.workBucket = new Map;
|
||||
this.timerResolution = timerResolution;
|
||||
this.timerStart = performance.now();
|
||||
}
|
||||
|
||||
empty(): boolean{
|
||||
return this.workBucket.size === 0;
|
||||
}
|
||||
|
||||
setTimeout(work: Work, delay: number): void {
|
||||
const time = performance.now() + delay - this.timerStart;
|
||||
const normalizedTime = normalizeTime(time, this.timerResolution);
|
||||
const workSet = this.workBucket.get(normalizedTime);
|
||||
if(workSet){
|
||||
workSet.add(work);
|
||||
}
|
||||
else{
|
||||
this.workBucket.set(normalizedTime, mergeWork(work));
|
||||
}
|
||||
}
|
||||
async tick(): Promise<void> {
|
||||
const current = performance.now();
|
||||
const normalizedCurrent = normalizeTime(current-this.timerStart, this.timerResolution);
|
||||
const workSet = this.workBucket.get(normalizedCurrent);
|
||||
if(workSet){
|
||||
await workSet.work();
|
||||
this.workBucket.delete(normalizedCurrent);
|
||||
}
|
||||
}
|
||||
async sleepForTick(): Promise<void>{
|
||||
const current = performance.now();
|
||||
|
||||
const normalizedCurrent = normalizeTime(current-this.timerStart, this.timerResolution);
|
||||
const next = normalizedCurrent + this.timerResolution;
|
||||
|
||||
const nextTime = this.timerStart + next;
|
||||
const sleepTime = nextTime - current;
|
||||
//this.ns.print(`Sleeping for ${sleepTime}ms`);
|
||||
await this.ns.sleep(sleepTime);
|
||||
}
|
||||
}
|
||||
|
||||
export class Scheduler{
|
||||
ns: NS;
|
||||
timeResolution: number;
|
||||
pidWatcher: PidTerminateExecutor;
|
||||
timerWatcher: TimerExecutor;
|
||||
constructor(ns: NS, timeResolution = 250){
|
||||
this.ns = ns;
|
||||
this.timeResolution = timeResolution;
|
||||
this.pidWatcher = new PidTerminateExecutor(ns);
|
||||
this.timerWatcher = new TimerExecutor(ns, timeResolution);
|
||||
}
|
||||
|
||||
empty(): boolean{
|
||||
return this.pidWatcher.empty() && this.timerWatcher.empty();
|
||||
}
|
||||
|
||||
setTimeout(work: Work, delay: number): void {
|
||||
this.timerWatcher.setTimeout(work, delay);
|
||||
}
|
||||
|
||||
sleepInWork(delay: number): Promise<void>{
|
||||
return new Promise<void>(resolve=>{
|
||||
this.setTimeout({work(){
|
||||
resolve();
|
||||
} }, delay);
|
||||
});
|
||||
}
|
||||
onPidTerminate(pid: number, work: Work): void{
|
||||
this.pidWatcher.watchPid(pid, work);
|
||||
}
|
||||
|
||||
async tick(): Promise<void>{
|
||||
await this.pidWatcher.tick();
|
||||
await this.timerWatcher.tick();
|
||||
await this.timerWatcher.sleepForTick();
|
||||
}
|
||||
}
|
72
src/lib/servers.ts
Normal file
72
src/lib/servers.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { NS, Server } from '@ns'
|
||||
|
||||
/** @param {NS} ns */
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
if (ns.args.length == 0) {
|
||||
ns.tprint("argument required")
|
||||
ns.tprint("search all server and print to json")
|
||||
ns.tprint("run cmd [filePath]")
|
||||
ns.exit()
|
||||
}
|
||||
const filePath = String(ns.args[0])
|
||||
const collectedData = selectAllServerList(ns)
|
||||
const buf = JSON.stringify(collectedData, undefined, 2)
|
||||
await ns.write(filePath, buf, "w")
|
||||
}
|
||||
|
||||
export interface ServerInfo extends Server{
|
||||
relHost:string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NS} ns
|
||||
* @param func
|
||||
*/
|
||||
export function visitAllServerList(ns: NS, func:(data: ServerInfo)=>void):void {
|
||||
/** @type {string[]} */
|
||||
const queue: string[] = ["home"]
|
||||
/** @type {Set<string>} */
|
||||
const visited = new Set()
|
||||
|
||||
//breadth first search
|
||||
while (queue.length > 0) {
|
||||
const vHost = queue.pop()
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const data = getServersInfo(ns, vHost!)
|
||||
|
||||
func(data)
|
||||
|
||||
visited.add(vHost)
|
||||
for (const h of data.relHost) {
|
||||
if (!visited.has(h)) {
|
||||
queue.push(h)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**@param {NS} ns */
|
||||
export function selectAllServerList(ns: NS): ServerInfo[] {
|
||||
const collectedData: ServerInfo[] = []
|
||||
visitAllServerList(ns, (data) => {
|
||||
collectedData.push(data)
|
||||
})
|
||||
return collectedData
|
||||
}
|
||||
|
||||
export function isRootedServer(server:Server):boolean{
|
||||
return server.hasAdminRights
|
||||
}
|
||||
|
||||
export function selectRootedServerList(ns:NS): ServerInfo[]{
|
||||
return selectAllServerList(ns).filter(isRootedServer)
|
||||
}
|
||||
|
||||
/** @param {NS} ns
|
||||
* @param {string} host
|
||||
* @return {Server & {relHost: string[]}}
|
||||
*/
|
||||
export function getServersInfo(ns: NS, host: string): ServerInfo {
|
||||
const neighbor = ns.scan(host)
|
||||
const info = ns.getServer(host)
|
||||
return { ...info, relHost: neighbor }
|
||||
}
|
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);
|
||||
//}
|
31
src/lib/solve/arrayJump2.ts
Normal file
31
src/lib/solve/arrayJump2.ts
Normal file
@ -0,0 +1,31 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
return d[arr.length - 1];
|
||||
}
|
||||
|
||||
export function arrayJump2Solve(arr:number[]): number{
|
||||
const v = arrayJump2(arr);
|
||||
if (v === Infinity) {
|
||||
return 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
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)
|
||||
//}
|
30
src/lib/solve/caesarCipher.ts
Normal file
30
src/lib/solve/caesarCipher.ts
Normal file
@ -0,0 +1,30 @@
|
||||
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.toUpperCase();
|
||||
|
||||
export function caesarCipher(str: string, key: number): string {
|
||||
return str.split('').map((ch) => {
|
||||
if (ch === ' ') return ch;
|
||||
const idx = alphabet.indexOf(ch);
|
||||
return alphabet[(idx + key) % alphabet.length];
|
||||
}).join('');
|
||||
}
|
||||
|
||||
type CaesarCipherInput = [string, number];
|
||||
|
||||
export function caesarCipherSolve([plain, key]: CaesarCipherInput): string[] {
|
||||
return [caesarCipher(plain, 26-key)];
|
||||
}
|
||||
|
||||
//type TestCase = [CaesarCipherInput, string];
|
||||
//
|
||||
//const TestCases: TestCase[] = [
|
||||
// [["ABC", -1], "BCD"],
|
||||
// [["ABC", -2], "CDE"],
|
||||
// [["ABC", -3], "DEF"],
|
||||
// [["ABC", -4], "EFG"],
|
||||
// [["ABC", -5], "FGH"],
|
||||
// [["ABC", -6], "GHI"],
|
||||
// [["HELLO WORLD", -1], "IFMMP XPSME"],
|
||||
// [["Z", 1], "A"],
|
||||
// [["MODEM FRAME VIRUS TRASH POPUP", -13], "ZBQRZ SENZR IVEHF GENFU CBCHC"]
|
||||
// [["LOGIN MOUSE TABLE PRINT FLASH", -18], "TWOQV UWCAM BIJTM XZQVB NTIAP"]
|
||||
//]
|
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
86
src/lib/solve/graph2color.ts
Normal file
86
src/lib/solve/graph2color.ts
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// 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[]{
|
||||
const ret = graph2coloring(input).map(x=>x.toString());
|
||||
if (ret.length === 0) {
|
||||
return ["[]"];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
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{}
|
54
src/list-all-server.ts
Normal file
54
src/list-all-server.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import {NS} from "@ns"
|
||||
import { parse } from "./lib/flag";
|
||||
import {getHackability,getLockSymbol} from "./lib/hack";
|
||||
import { sprintf } from "./lib/printf";
|
||||
|
||||
|
||||
let detail = false;
|
||||
|
||||
/** @param {NS} ns */
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
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 {string} startServer
|
||||
* @param {string} targetServer
|
||||
* @param {number} depth
|
||||
*/
|
||||
function findServer(ns: NS, startServer: string, targetServer: string, depth: number): string[] {
|
||||
const servers = ns.scan(targetServer)
|
||||
.filter((server) => server !== startServer);
|
||||
return servers.map((server, i, arr) => {
|
||||
const lock = getHackability(ns, server);
|
||||
const lock_symbol = getLockSymbol(lock);
|
||||
const info = ns.getServer(server);
|
||||
const backdoorSymbol = info.backdoorInstalled ? '✅': '❌';
|
||||
let ret: string[] = [];
|
||||
|
||||
const startSymbol = (i + 1 == arr.length) ? "└" : "├";
|
||||
const extendSymbol = (i + 1 == arr.length) ? " " : "│";
|
||||
//printf(ns, `😹${'>'.repeat(depth)} %s %s %s`,lock_symbol,server,info.backdoorInstalled ? '✅': '❌')
|
||||
const fmsg = (`${startSymbol}${lock_symbol} ${backdoorSymbol} ${server}`);
|
||||
ret.push(fmsg);
|
||||
if(detail){
|
||||
const moneyAvailable = ns.nFormat(info.moneyAvailable,"$0.000a");
|
||||
const moneyMax = ns.nFormat(info.moneyMax,"$0.000a");
|
||||
ret.push(sprintf("%s └%s🛡️ %6.2f/%6.2f(%3d),💸 %10s/%10s 💾%d/%d",extendSymbol,
|
||||
"-".repeat(20-depth),
|
||||
info.hackDifficulty,info.minDifficulty,info.requiredHackingSkill,
|
||||
moneyAvailable,moneyMax,
|
||||
info.maxRam, info.ramUsed));
|
||||
}
|
||||
if (lock !== "impossible") {
|
||||
const s = findServer(ns, targetServer, server, depth + 1).map(x=>`${extendSymbol}${x}`);
|
||||
ret = ret.concat(s)
|
||||
}
|
||||
return ret;
|
||||
}).flat();
|
||||
}
|
13
src/list-purchase-server.ts
Normal file
13
src/list-purchase-server.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {NS} from "@ns"
|
||||
import {parse} from "lib/flag"
|
||||
|
||||
/** @param {NS} ns */
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS):Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>String(x)));
|
||||
if(flag.help || flag.h){
|
||||
ns.tprint(`* list all purchased server`);
|
||||
ns.exit();
|
||||
}
|
||||
ns.tprint(ns.getPurchasedServers().join("\n"));
|
||||
}
|
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 [];
|
||||
}
|
58
src/lsnode.ts
Normal file
58
src/lsnode.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { selectRootedServerList, ServerInfo } from "lib/servers";
|
||||
import {
|
||||
calculateWeakenTime,
|
||||
calculatePercentMoneyHacked,
|
||||
calculateHackingExpGain,
|
||||
calculateServerGrowth
|
||||
} from "lib/formula";
|
||||
import { NS } from '@ns'
|
||||
import { sprintf } from "./lib/printf";
|
||||
import { parse } from "./lib/flag";
|
||||
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>x.toString()))
|
||||
|
||||
const list = selectRootedServerList(ns);
|
||||
const player = ns.getPlayer();
|
||||
const m = list.map(x => {
|
||||
const optimalState: ServerInfo = {...x,
|
||||
hackDifficulty: x.minDifficulty
|
||||
} as ServerInfo;
|
||||
ns.print(optimalState.minDifficulty," ", optimalState.hackDifficulty ,"");
|
||||
const weakenTime = calculateWeakenTime(optimalState, player);
|
||||
const expGain = calculateHackingExpGain(optimalState,player);
|
||||
const earnMoney = calculatePercentMoneyHacked(optimalState, player);
|
||||
//const growPercent = calculateServerGrowth()
|
||||
return {
|
||||
hostname: x.hostname,
|
||||
info: x,
|
||||
weakenTime,
|
||||
earnMoney,
|
||||
ce: earnMoney* x.moneyMax/weakenTime,
|
||||
expCe: expGain / weakenTime,
|
||||
expGain,
|
||||
}
|
||||
});
|
||||
if(flag.exp){
|
||||
m.sort((a,b)=>(b.expCe-a.expCe));
|
||||
|
||||
m.filter(x=>x.expCe > 0).forEach(x=>{
|
||||
const msg = sprintf("%20s %8s %6.1fs %8s",x.hostname,
|
||||
ns.nFormat(x.expGain,"0.00a"),x.weakenTime,
|
||||
ns.nFormat(x.expCe,"0.00a"));
|
||||
ns.tprint(msg);
|
||||
})
|
||||
}
|
||||
else {
|
||||
m.sort((a,b)=>(b.ce-a.ce));
|
||||
|
||||
m.filter(x=>x.ce > 0).forEach(x=>{
|
||||
const msg = sprintf("%20s %8s %6.1fs %10s",x.hostname,
|
||||
ns.nFormat(x.earnMoney * x.info.moneyMax,"$0.00a"),x.weakenTime,
|
||||
ns.nFormat(x.ce,"$0.000a"));
|
||||
ns.tprint(msg);
|
||||
})
|
||||
}
|
||||
}
|
73
src/purchase-server.ts
Normal file
73
src/purchase-server.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { NS } from "@ns"
|
||||
import {parse} from "./lib/flag"
|
||||
import {range} from "./util/range";
|
||||
|
||||
|
||||
/** @param {NS} ns */
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>String(x)));
|
||||
if(flag.h || flag.help){
|
||||
ns.tprint("script : purchase server")
|
||||
ns.tprint("");
|
||||
ns.exit()
|
||||
}
|
||||
if(Boolean(flag.i) || Boolean(flag.interactive)){
|
||||
const ramLimit = ns.getPurchasedServerMaxRam()
|
||||
const choices = [...range(3,21)].map(x=>Math.pow(2,x)).filter(x=>x <= ramLimit).map((x)=>{
|
||||
const cost = ns.getPurchasedServerCost(x);
|
||||
return `${x}GB (${ns.nFormat(cost,"$0.000a")})`
|
||||
});
|
||||
const choice = await ns.prompt("which server do you purchase?",{
|
||||
type:"select",
|
||||
choices
|
||||
}) as string;
|
||||
if(choice === ""){
|
||||
ns.tprint("canceled");
|
||||
ns.exit();
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const gb = parseInt(/^(\d+)GB/.exec(choice)![1]);
|
||||
ns.tprint("you select ",gb,"GB");
|
||||
const hostname = await ns.prompt("name your server",{type:"text"}) as string;
|
||||
if(hostname === ""){
|
||||
ns.tprint("canceled");
|
||||
ns.exit();
|
||||
return;
|
||||
}
|
||||
const p = await ns.prompt(
|
||||
`do you want to purchase server ${gb}GB(${
|
||||
ns.nFormat(ns.getPurchasedServerCost(gb),"$0.000a")})?`,
|
||||
{ type: "boolean" });
|
||||
if (p) {
|
||||
const l = ns.purchaseServer(hostname, gb);
|
||||
ns.tprint(l, " purchased");
|
||||
}
|
||||
ns.exit()
|
||||
return;
|
||||
}
|
||||
|
||||
if ((flag.n || flag.name) && (flag.s || flag.size)) {
|
||||
ns.tprint("argument needed")
|
||||
ns.tprint("run cmd -n [hostname] -s [ram(GB)]")
|
||||
ns.tprint("run cmd --name [hostname] --size [ram(GB)]")
|
||||
ns.exit()
|
||||
return;
|
||||
}
|
||||
|
||||
const hostname = flag.n ?? flag.name
|
||||
const ram = parseInt(flag.s ?? flag.size)
|
||||
if(isNaN(ram)){
|
||||
ns.tprint("size must be integer!");
|
||||
ns.exit();
|
||||
return;
|
||||
}
|
||||
const f = ns.getPurchasedServerCost(ram)
|
||||
const ff = ns.nFormat(f, "($ 0.00 a)")
|
||||
const p = await ns.prompt("required : " + ff + "\ndo you want to purchase server " + ram + "GB?",
|
||||
{ type: "boolean" })
|
||||
if (p) {
|
||||
const l = ns.purchaseServer(hostname, ram)
|
||||
ns.tprint(l, " purchased")
|
||||
}
|
||||
}
|
20
src/rainbow.ts
Normal file
20
src/rainbow.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { NS } from '@ns'
|
||||
import { parse } from './lib/flag';
|
||||
import { sprintf } from './lib/printf';
|
||||
|
||||
function guess(ns:NS,text:string):void{
|
||||
// undocumented function
|
||||
// @ts-ignore TS2339
|
||||
return ns.rainbow(text);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS) : Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>x.toString()));
|
||||
if(flag.h || flag.help){
|
||||
const msg = ['run cmd']
|
||||
msg.forEach(x=>ns.tprint(x));
|
||||
return;
|
||||
}
|
||||
guess(ns,"noodles")
|
||||
}
|
11
src/repl.ts
Normal file
11
src/repl.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { NS } from '@ns'
|
||||
|
||||
export async function main(ns: NS) : Promise<void> {
|
||||
const cmd = ns.args.join(" ");
|
||||
// @ts-ignore: disable-next-line
|
||||
const fn = new Function(["ns"],cmd);
|
||||
const v = await fn(ns);
|
||||
if(v !== undefined){
|
||||
ns.tprint(v)
|
||||
}
|
||||
}
|
30
src/runtest.ts
Normal file
30
src/runtest.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { NS } from '@ns'
|
||||
import { parse } from './lib/flag';
|
||||
import { red, rgb24, blue, bgBrightMagenta } from './lib/colors';
|
||||
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS) : Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>x.toString()));
|
||||
if(flag.h || flag.help){
|
||||
const msg = ['run cmd']
|
||||
msg.forEach(x=>ns.tprint(x));
|
||||
return;
|
||||
}
|
||||
ns.tprint(red("hello world red "));
|
||||
ns.tprint(blue("hello world blue"));
|
||||
ns.tprint(bgBrightMagenta("hello world bgBrightMagenta"));
|
||||
ns.tprint(rgb24("hello world #00ff00", 0x00ff00));
|
||||
ns.tprint(rgb24("hello world #f0f000", 0xf0f000));
|
||||
ns.tprint(rgb24("hello world #0000ff", 0x0000ff));
|
||||
ns.tprint(rgb24("hello world #0000ff", {r:0,g:0,b:255}));
|
||||
//const d = "document"
|
||||
//const doc = eval(d) as Document;
|
||||
//const div = doc.querySelector(".css-1hamw82") as HTMLDivElement;
|
||||
//if(!div){
|
||||
// ns.tprint("div not found");
|
||||
// return;
|
||||
//}
|
||||
//const text = div.innerText;
|
||||
//ns.tprint(text);
|
||||
}
|
95
src/server-status.ts
Normal file
95
src/server-status.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import {NS, AutocompleteData, Server} from "@ns";
|
||||
import {hackablePorts, getHackability, getLockSymbol} from "./lib/hack";
|
||||
import {parse} from "./lib/flag";
|
||||
|
||||
|
||||
type ReportOption = {
|
||||
detail?: boolean;
|
||||
}
|
||||
/*
|
||||
* Utility functions that report serverStatus
|
||||
* and Hackability
|
||||
* @param {NS} ns
|
||||
*/
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
const flag = parse(ns.args.map(x=>String(x)));
|
||||
if (flag._.length == 0) {
|
||||
ns.tprint("argumented required");
|
||||
return;
|
||||
}
|
||||
const hostname = String(flag._[0]);
|
||||
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,{
|
||||
detail: detail,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function makeDetailServerReport(ns:NS,server:Server): string{
|
||||
return [`hostname : ${server.hostname}(${server.ip})`,
|
||||
`🛡️${ns.nFormat(server.hackDifficulty,"0[.]000")}/${server.minDifficulty}(${server.baseDifficulty})`,
|
||||
`💸${ns.nFormat(server.moneyAvailable,"$0.000a")}/${ns.nFormat(server.moneyMax,"$0.000a")}`,
|
||||
`💾${server.ramUsed}GB/${server.maxRam}GB`,
|
||||
`backdoorInstalled \t: ${server.backdoorInstalled ? `🚪` : `❌`}`,
|
||||
`cpu \t\t\t: ${server.cpuCores}`,
|
||||
`growth \t\t\t: ${server.serverGrowth}`,
|
||||
`organization name\t: ${server.organizationName}`,
|
||||
`purchased \t\t: ${server.purchasedByPlayer}`,
|
||||
`required hacking skill\t: ${server.requiredHackingSkill}`,
|
||||
`ports \t\t\t: ${server.openPortCount}/${server.numOpenPortsRequired}`
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @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{
|
||||
if (serverLock == "rooted") {
|
||||
ns.tprint(`🛡️${Math.round(ns.getServerSecurityLevel(hostname))}/${ns.getServerMinSecurityLevel(hostname)}`);
|
||||
ns.tprint(`💸${ns.nFormat(ns.getServerMoneyAvailable(hostname), "$0.000a")}/${ns.nFormat(ns.getServerMaxMoney(hostname), "$0.000a")}`);
|
||||
} else {
|
||||
ns.tprint(`Hack Level: ${ns.getServerRequiredHackingLevel(hostname)}`);
|
||||
ns.tprint(`Ports: ${ns.getServerNumPortsRequired(hostname)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {NS} ns
|
||||
*/
|
||||
export function serverHackStatus(ns: NS, server: string): string {
|
||||
if (ns.hasRootAccess(server)) {
|
||||
return "🔓";
|
||||
}
|
||||
if (ns.getServerRequiredHackingLevel(server) > ns.getHackingLevel() ||
|
||||
ns.getServerNumPortsRequired(server) > hackablePorts(ns)) {
|
||||
return "🔒";
|
||||
}
|
||||
return "🔐";
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function autocomplete(data : AutocompleteData, args : string[]) : string[] {
|
||||
return [...data.servers]
|
||||
}
|
8
src/share-server.ts
Normal file
8
src/share-server.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import {NS} from "@ns"
|
||||
|
||||
// eslint-disable-next-line require-await
|
||||
export async function main(ns:NS):Promise<void>{
|
||||
while(true){
|
||||
await ns.share()
|
||||
}
|
||||
}
|
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);
|
||||
}
|
18
src/util/assert.ts
Normal file
18
src/util/assert.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export class AssertionError extends Error{
|
||||
constructor(msg:string){
|
||||
super(msg);
|
||||
this.name = "AssertionError";
|
||||
}
|
||||
}
|
||||
|
||||
export function assert(expr: unknown, msg=""): asserts expr{
|
||||
if(!expr){
|
||||
throw new AssertionError(msg)
|
||||
}
|
||||
}
|
||||
|
||||
export class NotImplementError extends Error{}
|
||||
|
||||
export function notImplemented(): never {
|
||||
throw new NotImplementError();
|
||||
}
|
8
src/util/range.ts
Normal file
8
src/util/range.ts
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
export function* range(arg1: number, arg2?: number): Generator<number>{
|
||||
const begin = arg2 ? arg1 : 0;
|
||||
const end = arg2 ?? arg1;
|
||||
for(let i = begin; i<end; i++){
|
||||
yield i;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { NS, ProcessInfo } from '../NetscriptDefinitions'
|
||||
import { NS, ProcessInfo } from '@ns'
|
||||
|
||||
export async function main(ns: NS): Promise<void> {
|
||||
const hashes: any = {}
|
||||
@ -6,27 +6,27 @@ export async function main(ns: NS): Promise<void> {
|
||||
const files = ns.ls('home', '.js')
|
||||
for (const file of files) {
|
||||
const contents = ns.read(file)
|
||||
hashes[file] = getHash(contents)
|
||||
hashes[file] = getHash(contents as string)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const files = ns.ls('home', '.js')
|
||||
|
||||
for (const file of files) {
|
||||
const contents = ns.read(file)
|
||||
const hash = getHash(contents)
|
||||
const hash = getHash(contents as string)
|
||||
|
||||
if (hash != hashes[file]) {
|
||||
ns.tprint(`INFO: Detected change in ${file}`)
|
||||
ns.tprintf(`INFO: Detected change in ${file}`)
|
||||
|
||||
const processes = ns.ps().filter((p: ProcessInfo) => {
|
||||
return p.filename == file
|
||||
})
|
||||
|
||||
for (const process of processes) {
|
||||
ns.tprint(`INFO: Restarting ${process.filename} ${process.args} -t ${process.threads}`)
|
||||
ns.tprintf(`INFO: Restarting ${process.filename} ${process.args} -t ${process.threads}`)
|
||||
if (process.filename != ns.getScriptName()) {
|
||||
ns.kill(process.pid, ns.getHostname())
|
||||
ns.kill(process.pid)
|
||||
ns.run(process.filename, process.threads, ...process.args)
|
||||
} else {
|
||||
ns.spawn(process.filename, process.threads, ...process.args)
|
||||
|
@ -3,7 +3,6 @@
|
||||
"NetscriptDefinitions.d.ts",
|
||||
"src/**/*",
|
||||
],
|
||||
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
@ -11,15 +10,21 @@
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": true,
|
||||
"strict": true,
|
||||
|
||||
"rootDir": "src/",
|
||||
"outDir": "dist/",
|
||||
"baseUrl": "src/",
|
||||
"paths": {
|
||||
"/*.js": [ "*" ],
|
||||
"*.js": [ "*" ]
|
||||
"/*.js": [
|
||||
"*"
|
||||
],
|
||||
"*.js": [
|
||||
"*"
|
||||
],
|
||||
"@ns": [
|
||||
"../NetscriptDefinitions.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user