1
0
Fork 0

Compare commits

...

5 Commits

Author SHA1 Message Date
Massaki Archambault d851d84c05 pull existing scripts from bitburner 2023-09-16 09:07:32 -04:00
Tanimodori ad0171f878 chore: update viteburner and other dependencies 2023-03-14 15:20:03 +08:00
Tanimodori fd2a6f6222 build: properly configure js for tsconfig 2023-03-14 15:13:51 +08:00
Tanimodori a5f4ca5db8 chore: update viteburner and deps 2023-02-25 16:13:44 +08:00
Tanimodori d0dceaab8f chore: use node.gitignore from github repo 2023-02-09 09:10:07 +08:00
27 changed files with 10971 additions and 288 deletions

151
.gitignore vendored
View File

@ -1,40 +1,149 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# IDE
.idea/
.vscode/
# Environment variables
.env
# Dependencies
/node_modules
/.pnp
.pnp.js
# Testing
/coverage
# Distributable
/build
/dist/
/playground/dist/
# Misc
# MacOS folder attributes
.DS_Store
# Env
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Netscript definitions is pulled automatically
NetScriptDefinitions.d.ts
# Node
# https://github.com/github/gitignore/blob/main/Node.gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Netscript definitions is pulled automatically
NetScriptDefinitions.d.ts
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

7896
NetscriptDefinitions.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

510
package-lock.json generated
View File

@ -9,16 +9,16 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.6.0",
"@types/node": "^18.15.3",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"eslint": "^8.36.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.3",
"typescript": "^4.9.4",
"vite": "^4.0.4",
"viteburner": "^0.5.1"
"prettier": "^2.8.4",
"typescript": "^4.9.5",
"vite": "^4.1.4",
"viteburner": "^0.5.3"
}
},
"node_modules/@antfu/utils": {
@ -382,15 +382,39 @@
"node": ">=12"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz",
"integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==",
"dev": true,
"dependencies": {
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
"node_modules/@eslint-community/regexpp": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz",
"integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==",
"dev": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/eslintrc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
"integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz",
"integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
"espree": "^9.4.0",
"espree": "^9.5.0",
"globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
@ -405,6 +429,15 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/js": {
"version": "8.36.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz",
"integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@ -486,9 +519,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
"version": "18.15.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
"dev": true
},
"node_modules/@types/semver": {
@ -498,18 +531,19 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz",
"integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/type-utils": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.55.0",
"@typescript-eslint/type-utils": "5.55.0",
"@typescript-eslint/utils": "5.55.0",
"debug": "^4.3.4",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0",
"regexpp": "^3.2.0",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
@ -531,14 +565,14 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz",
"integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.55.0",
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/typescript-estree": "5.55.0",
"debug": "^4.3.4"
},
"engines": {
@ -558,13 +592,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz",
"integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0"
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/visitor-keys": "5.55.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -575,13 +609,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz",
"integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@typescript-eslint/typescript-estree": "5.55.0",
"@typescript-eslint/utils": "5.55.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
@ -602,9 +636,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz",
"integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -615,13 +649,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz",
"integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0",
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/visitor-keys": "5.55.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -642,18 +676,18 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz",
"integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.55.0",
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/typescript-estree": "5.55.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
},
"engines": {
@ -668,12 +702,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz",
"integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/types": "5.55.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@ -1024,12 +1058,15 @@
}
},
"node_modules/eslint": {
"version": "8.33.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
"version": "8.36.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz",
"integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==",
"dev": true,
"dependencies": {
"@eslint/eslintrc": "^1.4.1",
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.1",
"@eslint/js": "8.36.0",
"@humanwhocodes/config-array": "^0.11.8",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -1040,10 +1077,9 @@
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.4.0",
"esquery": "^1.4.0",
"espree": "^9.5.0",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
@ -1064,7 +1100,6 @@
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
"regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
@ -1080,9 +1115,9 @@
}
},
"node_modules/eslint-config-prettier": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
"integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
"version": "8.7.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz",
"integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==",
"dev": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
@ -1125,33 +1160,6 @@
"node": ">=8.0.0"
}
},
"node_modules/eslint-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
"dev": true,
"dependencies": {
"eslint-visitor-keys": "^2.0.0"
},
"engines": {
"node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
},
"funding": {
"url": "https://github.com/sponsors/mysticatea"
},
"peerDependencies": {
"eslint": ">=5"
}
},
"node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true,
"engines": {
"node": ">=10"
}
},
"node_modules/eslint-visitor-keys": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
@ -1184,9 +1192,9 @@
}
},
"node_modules/espree": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
"integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz",
"integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==",
"dev": true,
"dependencies": {
"acorn": "^8.8.0",
@ -1201,9 +1209,9 @@
}
},
"node_modules/esquery": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
"dev": true,
"dependencies": {
"estraverse": "^5.1.0"
@ -1714,9 +1722,9 @@
}
},
"node_modules/magic-string": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
@ -1966,9 +1974,9 @@
}
},
"node_modules/prettier": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
@ -2046,18 +2054,6 @@
"node": ">=8.10.0"
}
},
"node_modules/regexpp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/mysticatea"
}
},
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@ -2320,9 +2316,9 @@
}
},
"node_modules/typescript": {
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@ -2356,15 +2352,15 @@
}
},
"node_modules/vite": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.4.tgz",
"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
"dev": true,
"dependencies": {
"esbuild": "^0.16.3",
"postcss": "^8.4.20",
"esbuild": "^0.16.14",
"postcss": "^8.4.21",
"resolve": "^1.22.1",
"rollup": "^3.7.0"
"rollup": "^3.10.0"
},
"bin": {
"vite": "bin/vite.js"
@ -2405,23 +2401,23 @@
}
},
"node_modules/viteburner": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.1.tgz",
"integrity": "sha512-LPXCFYU4HOF8LmBBKXiW1uL+8qrJJm3KtoGAsEgtMXhDnx8LQZMrLVl+hwQmYuIGl770SVaFJBXd4vpoH4JYbQ==",
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.3.tgz",
"integrity": "sha512-93eSxQK3XyFF9t3JixEs0a2N6nbZkaOA1YAB7I86S1xm7qyFqcwv3oCLdNI5XzsPc/+Aue/26sHP3epbP5IEzA==",
"dev": true,
"dependencies": {
"acorn": "^8.8.2",
"cac": "^6.7.14",
"chokidar": "^3.5.3",
"fast-glob": "^3.2.12",
"magic-string": "^0.27.0",
"magic-string": "^0.30.0",
"micromatch": "^4.0.5",
"pathe": "^1.1.0",
"picocolors": "^1.0.0",
"prompts": "^2.4.2",
"unconfig": "^0.3.7",
"ws": "^8.12.0",
"zod": "^3.19.1"
"ws": "^8.12.1",
"zod": "^3.20.6"
},
"bin": {
"viteburner": "bin/viteburner.js"
@ -2461,9 +2457,9 @@
"dev": true
},
"node_modules/ws": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"dev": true,
"engines": {
"node": ">=10.0.0"
@ -2500,9 +2496,9 @@
}
},
"node_modules/zod": {
"version": "3.19.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.19.1.tgz",
"integrity": "sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==",
"version": "3.20.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.20.6.tgz",
"integrity": "sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
@ -2670,15 +2666,30 @@
"dev": true,
"optional": true
},
"@eslint-community/eslint-utils": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz",
"integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^3.3.0"
}
},
"@eslint-community/regexpp": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz",
"integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==",
"dev": true
},
"@eslint/eslintrc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz",
"integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz",
"integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
"espree": "^9.4.0",
"espree": "^9.5.0",
"globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
@ -2687,6 +2698,12 @@
"strip-json-comments": "^3.1.1"
}
},
"@eslint/js": {
"version": "8.36.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz",
"integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==",
"dev": true
},
"@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@ -2749,9 +2766,9 @@
"dev": true
},
"@types/node": {
"version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
"version": "18.15.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
"dev": true
},
"@types/semver": {
@ -2761,70 +2778,71 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz",
"integrity": "sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/type-utils": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.55.0",
"@typescript-eslint/type-utils": "5.55.0",
"@typescript-eslint/utils": "5.55.0",
"debug": "^4.3.4",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0",
"regexpp": "^3.2.0",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/parser": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.55.0.tgz",
"integrity": "sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.55.0",
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/typescript-estree": "5.55.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz",
"integrity": "sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0"
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/visitor-keys": "5.55.0"
}
},
"@typescript-eslint/type-utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz",
"integrity": "sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==",
"dev": true,
"requires": {
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/utils": "5.49.0",
"@typescript-eslint/typescript-estree": "5.55.0",
"@typescript-eslint/utils": "5.55.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/types": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.55.0.tgz",
"integrity": "sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz",
"integrity": "sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/visitor-keys": "5.49.0",
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/visitor-keys": "5.55.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -2833,28 +2851,28 @@
}
},
"@typescript-eslint/utils": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.55.0.tgz",
"integrity": "sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.49.0",
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/typescript-estree": "5.49.0",
"@typescript-eslint/scope-manager": "5.55.0",
"@typescript-eslint/types": "5.55.0",
"@typescript-eslint/typescript-estree": "5.55.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0",
"semver": "^7.3.7"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
"version": "5.55.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz",
"integrity": "sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.49.0",
"@typescript-eslint/types": "5.55.0",
"eslint-visitor-keys": "^3.3.0"
}
},
@ -3108,12 +3126,15 @@
"dev": true
},
"eslint": {
"version": "8.33.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
"version": "8.36.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz",
"integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==",
"dev": true,
"requires": {
"@eslint/eslintrc": "^1.4.1",
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.1",
"@eslint/js": "8.36.0",
"@humanwhocodes/config-array": "^0.11.8",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -3124,10 +3145,9 @@
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.4.0",
"esquery": "^1.4.0",
"espree": "^9.5.0",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
@ -3148,7 +3168,6 @@
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
"regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
@ -3173,9 +3192,9 @@
}
},
"eslint-config-prettier": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
"integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
"version": "8.7.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.7.0.tgz",
"integrity": "sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==",
"dev": true,
"requires": {}
},
@ -3198,23 +3217,6 @@
"estraverse": "^4.1.1"
}
},
"eslint-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
"integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^2.0.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
"dev": true
}
}
},
"eslint-visitor-keys": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
@ -3222,9 +3224,9 @@
"dev": true
},
"espree": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
"integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==",
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz",
"integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==",
"dev": true,
"requires": {
"acorn": "^8.8.0",
@ -3233,9 +3235,9 @@
}
},
"esquery": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
"integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
"dev": true,
"requires": {
"estraverse": "^5.1.0"
@ -3628,9 +3630,9 @@
}
},
"magic-string": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz",
"integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==",
"dev": true,
"requires": {
"@jridgewell/sourcemap-codec": "^1.4.13"
@ -3807,9 +3809,9 @@
"dev": true
},
"prettier": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
"dev": true
},
"prettier-linter-helpers": {
@ -3852,12 +3854,6 @@
"picomatch": "^2.2.1"
}
},
"regexpp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true
},
"resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@ -4026,9 +4022,9 @@
"dev": true
},
"typescript": {
"version": "4.9.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true
},
"unconfig": {
@ -4052,36 +4048,36 @@
}
},
"vite": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz",
"integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==",
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.4.tgz",
"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
"dev": true,
"requires": {
"esbuild": "^0.16.3",
"esbuild": "^0.16.14",
"fsevents": "~2.3.2",
"postcss": "^8.4.20",
"postcss": "^8.4.21",
"resolve": "^1.22.1",
"rollup": "^3.7.0"
"rollup": "^3.10.0"
}
},
"viteburner": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.1.tgz",
"integrity": "sha512-LPXCFYU4HOF8LmBBKXiW1uL+8qrJJm3KtoGAsEgtMXhDnx8LQZMrLVl+hwQmYuIGl770SVaFJBXd4vpoH4JYbQ==",
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/viteburner/-/viteburner-0.5.3.tgz",
"integrity": "sha512-93eSxQK3XyFF9t3JixEs0a2N6nbZkaOA1YAB7I86S1xm7qyFqcwv3oCLdNI5XzsPc/+Aue/26sHP3epbP5IEzA==",
"dev": true,
"requires": {
"acorn": "^8.8.2",
"cac": "^6.7.14",
"chokidar": "^3.5.3",
"fast-glob": "^3.2.12",
"magic-string": "^0.27.0",
"magic-string": "^0.30.0",
"micromatch": "^4.0.5",
"pathe": "^1.1.0",
"picocolors": "^1.0.0",
"prompts": "^2.4.2",
"unconfig": "^0.3.7",
"ws": "^8.12.0",
"zod": "^3.19.1"
"ws": "^8.12.1",
"zod": "^3.20.6"
}
},
"which": {
@ -4106,9 +4102,9 @@
"dev": true
},
"ws": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"dev": true,
"requires": {}
},
@ -4125,9 +4121,9 @@
"dev": true
},
"zod": {
"version": "3.19.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.19.1.tgz",
"integrity": "sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==",
"version": "3.20.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.20.6.tgz",
"integrity": "sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==",
"dev": true
}
}

View File

@ -6,16 +6,16 @@
},
"author": "Tanimodori",
"devDependencies": {
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.6.0",
"@types/node": "^18.15.3",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"eslint": "^8.36.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.3",
"typescript": "^4.9.4",
"vite": "^4.0.4",
"viteburner": "^0.5.1"
"prettier": "^2.8.4",
"typescript": "^4.9.5",
"vite": "^4.1.4",
"viteburner": "^0.5.3"
},
"license": "MIT"
}

166
src/archives/cc.js Normal file
View File

@ -0,0 +1,166 @@
/** @param {NS} ns */
export async function main(ns) {
const moneyLowPercent = 0.75
const securityHighOffset = 5
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
const pwnMethods = []
if (ns.fileExists("BruteSSH.exe")) {
pwnMethods.push(ns.brutessh)
}
if (ns.fileExists("FTPCrack.exe")) {
pwnMethods.push(ns.ftpcrack)
}
if (ns.fileExists("HTTPWorm.exe")) {
pwnMethods.push(ns.httpworm)
}
if (ns.fileExists("relaySMTP.exe")) {
pwnMethods.push(ns.relaysmtp)
}
if (ns.fileExists("SQLInject.exe")) {
pwnMethods.push(ns.sqlinject)
}
// ns.clearLog()
// ns.tail()
// ns.disableLog("getServerMaxRam")
// ns.disableLog("scp")
// ns.disableLog("killall")
ns.print(`${pwnMethods.length} pwning method(s) available!`)
class Server {
constructor(hostname) {
this.hostname = hostname
this.maxMoney = ns.getServerMaxMoney(hostname)
this.minSecurityLevel = ns.getServerMinSecurityLevel(hostname)
this.maxRam = ns.getServerMaxRam(hostname)
this.requiredHackLevel = ns.getServerRequiredHackingLevel(hostname)
this.hasRootAccess = ns.hasRootAccess(hostname)
this.numPortRequired = ns.getServerNumPortsRequired(hostname)
}
toString() {
return this.hostname
}
get usedRam() {
return ns.getServerUsedRam(this.hostname)
}
get availRam() {
return this.maxRam - this.usedRam
}
get availMoney() {
return ns.getServerMoneyAvailable(this.hostname)
}
get currSecurityLevel() {
return ns.getServerSecurityLevel(this.hostname)
}
get weakenTime() {
return ns.getWeakenTime(this.hostname)
}
get hackTime() {
return ns.getHackTime(this.hostname)
}
get growTime() {
return ns.getGrowTime(this.hostname)
}
get score() {
if (this.requiredHackLevel > ns.getHackingLevel() || this.maxMoney == 0) {
return Infinity
}
else {
const percentMinSecurityLevel = (this.minSecurityLevel / 100)
const targetRequiredHackLevel = ns.getHackingLevel() * 0.33
const percentLevelDeviation = Math.abs((this.requiredHackLevel - targetRequiredHackLevel) / targetRequiredHackLevel)
// return Math.round(this.maxMoney * percentMinSecurityLevel * percentDeviation)
return percentMinSecurityLevel + percentLevelDeviation
}
}
computeScriptCapacity(script) {
return Math.floor(this.availRam / ns.getScriptRam(script))
}
pwn() {
if (nodeNumPortsRequired <= pwnMethods.length) {
ns.tprint(`pwning ${this.hostname}`)
for (let i = 0; i < this.numPortRequired; i++) {
pwnMethods[i](this.hostname)
}
ns.nuke(this.hostname)
return true
}
else {
ns.print(`missing requirements to pwn ${this.hostname}. (${pwnMethods.length} < ${this.numPortRequired})`)
return false
}
}
exploit(target) {
const moneyLowThresh = target.maxMoney * moneyLowPercent
const securityHighThresh = target.minSecurityLevel + securityHighOffset
ns.print(`setup ${this}`)
const capacity = this.computeScriptCapacity("exploit.js")
if (capacity > 0) {
ns.scp("exploit.js", this.hostname)
ns.killall(this.hostname)
ns.exec("exploit.js", this.hostname, capacity, target.hostname, moneyLowThresh, securityHighThresh)
}
}
}
/**
* Recursively list all available worker nodes
*/
const getAvailableNodes = (hostnameToAnalyze = "home", availableNodes = []) => {
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
const parentServer = new Server(parentHostname)
// check if we alreay have added this node to the list
if (availableNodes.map((n) => n.toString()).includes(parentServer.toString())) {
continue //skip this node
}
// if we are not root on the server yet
if (!parentServer.hasRootAccess) {
// gain access
if (!parentServer.pwn()) {
// failed, skip this node
}
}
ns.print(`adding ${parentServer} to worker list`)
availableNodes.push(parentServer)
availableNodes = getAvailableNodes(parentServer.hostname, availableNodes)
}
return availableNodes
}
const printNodeTab = (nodes) => {
const format = "%-15ss %10s %10s %10s %10s %10s %10s"
ns.tprintf(format, "host", "score", "maxMoney", "minSecurity", "root?", "hackLevel", "maxRam")
nodes.forEach((n) => {
ns.tprintf(format, n.hostname, ns.formatNumber(n.score), ns.formatNumber(n.maxMoney), ns.formatNumber(n.minSecurityLevel), n.hasRootAccess, n.requiredHackLevel, ns.formatRam(n.maxRam))
})
ns.tprint(`total nodes: ${ns.formatNumber(nodes.length)}`)
ns.tprint(`total RAM: ${ns.formatRam(nodes.reduce((x, y) => x + y.maxRam, 0))}`)
}
// find optimal target
const availableNodes = getAvailableNodes()
availableNodes.sort((a, b) => a.score - b.score)
const target = availableNodes.at(0)
// print status
printNodeTab(availableNodes)
ns.tprint(`targeting ${target}`)
//execute
availableNodes.forEach((n) => { n.exploit(target) })
}

166
src/archives/cc_v1.js Normal file
View File

@ -0,0 +1,166 @@
/** @param {NS} ns */
export async function main(ns) {
const moneyLowPercent = 0.75
const securityHighOffset = 5
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
const pwnMethods = []
if (ns.fileExists("BruteSSH.exe")) {
pwnMethods.push(ns.brutessh)
}
if (ns.fileExists("FTPCrack.exe")) {
pwnMethods.push(ns.ftpcrack)
}
if (ns.fileExists("HTTPWorm.exe")) {
pwnMethods.push(ns.httpworm)
}
if (ns.fileExists("relaySMTP.exe")) {
pwnMethods.push(ns.relaysmtp)
}
if (ns.fileExists("SQLInject.exe")) {
pwnMethods.push(ns.sqlinject)
}
// ns.clearLog()
// ns.tail()
// ns.disableLog("getServerMaxRam")
// ns.disableLog("scp")
// ns.disableLog("killall")
ns.print(`${pwnMethods.length} pwning method(s) available!`)
class Server {
constructor(hostname) {
this.hostname = hostname
this.maxMoney = ns.getServerMaxMoney(hostname)
this.minSecurityLevel = ns.getServerMinSecurityLevel(hostname)
this.maxRam = ns.getServerMaxRam(hostname)
this.requiredHackLevel = ns.getServerRequiredHackingLevel(hostname)
this.hasRootAccess = ns.hasRootAccess(hostname)
this.numPortRequired = ns.getServerNumPortsRequired(hostname)
}
toString() {
return this.hostname
}
get usedRam() {
return ns.getServerUsedRam(this.hostname)
}
get availRam() {
return this.maxRam - this.usedRam
}
get availMoney() {
return ns.getServerMoneyAvailable(this.hostname)
}
get currSecurityLevel() {
return ns.getServerSecurityLevel(this.hostname)
}
get weakenTime() {
return ns.getWeakenTime(this.hostname)
}
get hackTime() {
return ns.getHackTime(this.hostname)
}
get growTime() {
return ns.getGrowTime(this.hostname)
}
get score() {
if (this.requiredHackLevel > ns.getHackingLevel() || this.maxMoney == 0) {
return Infinity
}
else {
const percentMinSecurityLevel = (this.minSecurityLevel / 100)
const targetRequiredHackLevel = ns.getHackingLevel() * 0.33
const percentLevelDeviation = Math.abs((this.requiredHackLevel - targetRequiredHackLevel) / targetRequiredHackLevel)
// return Math.round(this.maxMoney * percentMinSecurityLevel * percentDeviation)
return percentMinSecurityLevel + percentLevelDeviation
}
}
computeScriptCapacity(script) {
return Math.floor(this.maxRam / ns.getScriptRam(script))
}
pwn() {
if (nodeNumPortsRequired <= pwnMethods.length) {
ns.tprint(`pwning ${this.hostname}`)
for (let i = 0; i < this.numPortRequired; i++) {
pwnMethods[i](this.hostname)
}
ns.nuke(this.hostname)
return true
}
else {
ns.print(`missing requirements to pwn ${this.hostname}. (${pwnMethods.length} < ${this.numPortRequired})`)
return false
}
}
exploit(target) {
const moneyLowThresh = target.maxMoney * moneyLowPercent
const securityHighThresh = target.minSecurityLevel + securityHighOffset
ns.print(`setup ${this}`)
const capacity = this.computeScriptCapacity("exploit.js")
if (capacity > 0) {
ns.scp("exploit.js", this.hostname)
ns.killall(this.hostname)
ns.exec("exploit.js", this.hostname, capacity, target.hostname, moneyLowThresh, securityHighThresh)
}
}
}
/**
* Recursively list all available worker nodes
*/
const getAvailableNodes = (hostnameToAnalyze = "home", availableNodes = []) => {
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
const parentServer = new Server(parentHostname)
// check if we alreay have added this node to the list
if (availableNodes.map((n) => n.toString()).includes(parentServer.toString())) {
continue //skip this node
}
// if we are not root on the server yet
if (!parentServer.hasRootAccess) {
// gain access
if (!parentServer.pwn()) {
// failed, skip this node
}
}
ns.print(`adding ${parentServer} to worker list`)
availableNodes.push(parentServer)
availableNodes = getAvailableNodes(parentServer.hostname, availableNodes)
}
return availableNodes
}
const printNodeTab = (nodes) => {
const format = "%-15ss %10s %10s %10s %10s %10s %10s"
ns.tprintf(format, "host", "score", "maxMoney", "minSecurity", "root?", "hackLevel", "maxRam")
nodes.forEach((n) => {
ns.tprintf(format, n.hostname, ns.formatNumber(n.score), ns.formatNumber(n.maxMoney), ns.formatNumber(n.minSecurityLevel), n.hasRootAccess, n.requiredHackLevel, ns.formatRam(n.maxRam))
})
ns.tprint(`total nodes: ${ns.formatNumber(nodes.length)}`)
ns.tprint(`total RAM: ${ns.formatRam(nodes.reduce((x, y) => x + y.maxRam, 0))}`)
}
// find optimal target
const availableNodes = getAvailableNodes()
availableNodes.sort((a, b) => a.score - b.score)
const target = availableNodes.at(0)
// print status
printNodeTab(availableNodes)
ns.tprint(`targeting ${target}`)
//execute
availableNodes.forEach((n) => { n.exploit(target) })
}

18
src/archives/exploit.js Normal file
View File

@ -0,0 +1,18 @@
/** @param {NS} ns */
export async function main(ns) {
const target = ns.args[0]
const moneyLowThresh = ns.args[1]
const securityHighThresh = ns.args[2]
while (true) {
if (ns.getServerSecurityLevel(target) > securityHighThresh) {
await ns.weaken(target)
}
else if (ns.getServerMoneyAvailable(target) < moneyLowThresh) {
await ns.grow(target)
}
else {
await ns.hack(target)
}
}
}

59
src/auto/backdoor.js Normal file
View File

@ -0,0 +1,59 @@
import { getServerTree } from "/lib/ServerTree.js"
/** @param {NS} ns */
export async function main(ns) {
const serverTree = getServerTree(ns)
// 1st pass: recursively find servers to backdoor
const getTreeOfServerToBackdoor = (subtree) => {
const childrenToBackdoor = []
for (const child of subtree["children"]) {
const backdoorSubtreeOfChild = getTreeOfServerToBackdoor(child)
if (backdoorSubtreeOfChild != null) {
childrenToBackdoor.push(backdoorSubtreeOfChild)
}
}
if (childrenToBackdoor.length != 0 || (!subtree.info.backdoorInstalled && subtree.info.hasAdminRights && subtree.info.requiredHackingSkill <= ns.getHackingLevel())) {
return {
"info": subtree.info,
"children": childrenToBackdoor,
}
}
else {
return null
}
}
// 2nd pass: walk the tree and backdoor servers
const backdoor = async (subtree) => {
if (!subtree.info.backdoorInstalled) {
ns.tprintf("backdoor %s, do not change server", subtree.info.hostname)
await ns.singularity.installBackdoor()
ns.tprint("backdoor complete")
}
for (const child of subtree.children) {
// connect to child
if (!ns.singularity.connect(child.info.hostname)) {
ns.tprintf("failed to connect: %s", child.info.hostname)
ns.exit()
}
// backdoor the child
await backdoor(child)
// connect back to current host
if (!ns.singularity.connect(subtree.info.hostname)) {
ns.tprintf("failed to connect: %s", subtree.info.hostname)
ns.exit()
}
}
}
// execute
const serverTreeToBackdoor = getTreeOfServerToBackdoor(serverTree)
if (serverTreeToBackdoor == null) {
ns.tprint("nothing to do")
}
else {
await backdoor(serverTreeToBackdoor)
}
ns.tprint("script complete")
}

31
src/auto/rack-workers.js Normal file
View File

@ -0,0 +1,31 @@
/** @param {NS} ns */
export async function main(ns) {
const maxServerCount = ns.getPurchasedServerLimit()
let serverCount = ns.getPurchasedServers().length
ns.tprint(`we have ${serverCount} servers`)
ns.tprint(`we can have ${maxServerCount} servers`)
if (serverCount < maxServerCount) {
// create missing servers
const requestedRAM = 4
const serverCost = ns.getPurchasedServerCost(requestedRAM)
ns.tprint(`server cost is ${ns.formatNumber(serverCost)}`)
while (serverCount < maxServerCount) {
const serverName = `worker-${serverCount}`
if (ns.getServerMoneyAvailable("home") >= serverCost) {
ns.tprint(`racking server ${serverName}`)
ns.purchaseServer(serverName, requestedRAM)
serverCount++
}
else {
await ns.sleep(60000)
}
}
}
ns.tprint("done! spawning auto-upgrade")
ns.spawn("auto/upgrade-workers.js")
}

49
src/auto/tor.js Normal file
View File

@ -0,0 +1,49 @@
/** @param {NS} ns */
export async function main(ns) {
const programs = [
"BruteSSH.exe",
"FTPCrack.exe",
"relaySMTP.exe",
"HTTPWorm.exe",
"SQLInject.exe",
"ServerProfiler.exe",
"DeepscanV1.exe",
"DeepscanV2.exe",
"AutoLink.exe",
"Formulas.exe",
]
// attempt to buy tor every second
while (!ns.singularity.purchaseTor()) {
await ns.sleep(1000)
}
// build a list to track which program we have in our possession
let programsPurchased = new Array(programs.length)
for (let i = 0; i < programs.length; i++) {
programsPurchased[i] = ns.ls("home").includes(programs[i])
}
// attempt to buy the programs every seconds
do {
await ns.sleep(1000)
for (let i = 0; i < programs.length; i++) {
if (!programsPurchased[i]) {
const purchaseResult = ns.singularity.purchaseProgram(programs[i])
if (purchaseResult) {
ns.tprintf("auto-tor: purchased %s", programs[i])
}
else {
// purchase the programs in the order listed
// if we are unable to buy a program, break and retry later
break
}
programsPurchased[i] = purchaseResult
}
}
} while (!programsPurchased.reduce((x, y) => x && y, true))
ns.tprint("auto-tor: complete")
}

View File

@ -0,0 +1,33 @@
/** @param {NS} ns */
export async function main(ns) {
const servers = ns.getPurchasedServers()
ns.clearLog()
ns.tail()
ns.disableLog('ALL')
const getLowestServerRAM = () => {
return Math.min.apply(Math, servers.map(ns.getServerMaxRam))
}
while (getLowestServerRAM() < ns.getPurchasedServerMaxRam()) {
const requestedRAM = getLowestServerRAM()*2
for (const serverName of servers) {
const serverRAM = ns.getServerMaxRam(serverName)
if (serverRAM < requestedRAM) {
const serverCost = ns.getPurchasedServerUpgradeCost(serverName, requestedRAM)
ns.print(`selected server ${serverName} to be upgraded (current RAM: ${ns.formatRam(serverRAM)}, upgraded RAM: ${ns.formatRam(requestedRAM)}, upgrade cost: ${ns.formatNumber(serverCost)})`)
while (true) {
if (ns.getServerMoneyAvailable("home") >= serverCost) {
ns.killall(serverName) //TODO: make it more graceful
ns.upgradePurchasedServer(serverName, requestedRAM)
break
}
else {
await ns.sleep(60000)
}
}
}
}
}
}

668
src/cc_v2.js Normal file
View File

@ -0,0 +1,668 @@
/** @param {NS} ns */
export async function main(ns) {
const hackMinMoneyLimitPercent = 0.75
const hackMaxSecurityIncreaseLimit = 5
const hackMinLevelThreshPercent = 1
const maxTargets = Infinity
const loopSettings = {
"poolBlacklist": [],
"poolBlacklist": ["home"],
"moneyTargetsWhitelist": [],
// "moneyTargetsWhitelist": ["n00dles", "harakiri-sushi", "phantasy"], // early game list
// "moneyTargetsBlacklist": [],
"moneyTargetsBlacklist": ["joesguns"], // blacklist joesguns to avoid conflict with exp farm
"expTargetHostname": "joesguns",
"expMaxAvailRamUsePercent": 0.99,
}
const hackRamUsage = ns.getScriptRam("/payloads/hack.js")
const growRamUsage = ns.getScriptRam("/payloads/grow.js")
const weakenRamUsage = ns.getScriptRam("/payloads/weaken.js")
const autoRackRamUsage = ns.getScriptRam("/payloads/autorack.js")
const autoRackPwnUsage = ns.getScriptRam("/payloads/autopwn.js")
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
ns.clearLog()
ns.tail()
ns.disableLog('ALL')
// ns.disableLog('getServerUsedRam')
// ns.disableLog('getServerMaxRam')
// ns.print(`${pwnMethods.length} pwning method(s) available!`)
class PoolException {
constructor(message) {
this.message = message
}
toString() {
return `WARNING: ${this.message}`
}
}
class Server {
constructor(hostname) {
this.hostname = hostname
ns.scp(["/payloads/grow.js", "/payloads/hack.js", "/payloads/weaken.js", "/payloads/autorack.js", "/payloads/autopwn.js"], this.hostname)
}
toString() {
return this.hostname
}
get hasRootAccess() {
return ns.hasRootAccess(this.hostname)
}
get hackable() {
return this.hasRootAccess && this.maxMoney > 0 && (this.requiredHackLevel <= ns.getHackingLevel() * hackMinLevelThreshPercent || this.requiredHackLevel == 1)
}
get requiredHackLevel() {
return ns.getServerRequiredHackingLevel(this.hostname)
}
get maxRam() {
return ns.getServerMaxRam(this.hostname)
}
get usedRam() {
return ns.getServerUsedRam(this.hostname)
}
get availRam() {
return this.maxRam - this.usedRam
}
get maxMoney() {
return ns.getServerMaxMoney(this.hostname)
}
get availMoney() {
return ns.getServerMoneyAvailable(this.hostname)
}
get minSecurityLevel() {
return ns.getServerMinSecurityLevel(this.hostname)
}
get currSecurityLevel() {
return ns.getServerSecurityLevel(this.hostname)
}
get hackTime() {
return Math.ceil(ns.getHackTime(this.hostname))
}
get growTime() {
return Math.ceil(ns.getGrowTime(this.hostname))
}
get weakenTime() {
return Math.ceil(ns.getWeakenTime(this.hostname))
}
get hackChance() {
return ns.hackAnalyzeChance(this.hostname)
}
get pids() {
return ns.ps(this.hostname).map(process => process.pid)
}
getScriptCapacity(script) {
return Math.floor(this.availRam / ns.getScriptRam(script))
}
getHackCapacity() {
return Math.floor(this.availRam / hackRamUsage)
}
getGrowCapacity() {
return Math.floor(this.availRam / growRamUsage)
}
getWeakenCapacity() {
return Math.floor(this.availRam / weakenRamUsage)
}
computeOptimalHackThreadCount() {
const requiredThreads = Math.ceil(((this.availMoney - (this.maxMoney * hackMinMoneyLimitPercent)) / this.availMoney) / ns.hackAnalyze(this.hostname))
let securityLimitedThreads = 0
let predictedSecurityIncrease = 0
while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
predictedSecurityIncrease = ns.hackAnalyzeSecurity(++securityLimitedThreads)
}
return Math.min(requiredThreads, securityLimitedThreads)
}
computeOptimalGrowThreadCount() {
const requiredThreads = Math.ceil(ns.growthAnalyze(this.hostname, this.maxMoney / this.availMoney))
// let securityLimitedThreads = 0
// let predictedSecurityIncrease = 0
// while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
// predictedSecurityIncrease = ns.growthAnalyzeSecurity(++securityLimitedThreads)
// }
// return Math.min(requiredThreads, securityLimitedThreads)
return requiredThreads
}
computeOptimalWeakenThreadCount() {
let requiredThreads = 0
let prediction = this.currSecurityLevel
while (prediction > this.minSecurityLevel) {
prediction = this.currSecurityLevel - ns.weakenAnalyze(++requiredThreads)
}
return requiredThreads
}
execHack(target, numThreads = 1) {
const pid = ns.exec("/payloads/hack.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
if (pid == 0) {
throw `ERROR: failed to run hack() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
}
return new Task(this, pid, "hack", Date.now() + target.hackTime, target)
}
execGrow(target, numThreads = 1) {
const pid = ns.exec("/payloads/grow.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
if (pid == 0) {
throw `ERROR: failed to run grow() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getGrowCapacity()}`
}
return new Task(this, pid, "grow", Date.now() + target.growTime, target)
}
execWeaken(target, numThreads = 1) {
const pid = ns.exec("/payloads/weaken.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
if (pid == 0) {
throw `ERROR: failed to run weaken() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
}
return new Task(this, pid, "weaken", Date.now() + target.weakenTime, target)
}
execAutoRack() {
const pid = ns.exec("/payloads/autorack.js", this.hostname, 1, crypto.randomUUID())
if (pid == 0) {
throw `ERROR: failed to run autorack on ${this.hostname}, availRAM = ${this.availRam}`
}
return new Task(this, pid, "autorack", Date.now())
}
execAutoPwn() {
const pid = ns.exec("/payloads/autopwn.js", this.hostname, 1, crypto.randomUUID())
if (pid == 0) {
throw `ERROR: failed to run autopwn on ${this.hostname}, availRAM = ${this.availRam}`
}
return new Task(this, pid, "autorack", Date.now())
}
}
class ServerPool {
constructor() {
this.pool = []
}
get size() {
return this.pool.length
}
get maxRam() {
return this.pool.reduce((x, y) => x + y.maxRam, 0)
}
get usedRam() {
return this.pool.reduce((x, y) => x + y.usedRam, 0)
}
get availRam() {
return this.pool.reduce((x, y) => x + y.availRam, 0)
}
getHackCapacity() {
return this.pool.reduce((x, y) => x + y.getHackCapacity(), 0)
}
getGrowCapacity() {
return this.pool.reduce((x, y) => x + y.getGrowCapacity(), 0)
}
getWeakenCapacity() {
return this.pool.reduce((x, y) => x + y.getWeakenCapacity(), 0)
}
killAll() {
this.pool.forEach(server => ns.killall(server.hostname))
}
execHack(target, numThreads) {
let tasks = []
if (numThreads == 0) {
throw new PoolException(`attempted to schedule task with threads = 0 (operation = hack, target = ${target})`)
}
for (const server of this.pool) {
const capacity = server.getHackCapacity()
if (capacity == 0) {
// this server is full, so we skip it
continue
}
const newThreads = numThreads > capacity ? capacity : numThreads
const task = server.execHack(target, newThreads)
if (task.pid != 0) {
tasks.push(task)
// reduce the number of remaining threads by the number of thread started by the new task
numThreads -= newThreads
if (numThreads <= 0) {
// all threads scheduled, we can stop assigning them now
break
}
}
}
if (tasks.length == 0) {
throw new PoolException(`attempted to schedule task, put pool has no capacity (operation = hack, target = ${target}, threads = ${numThreads})`)
}
return tasks
}
execGrow(target, numThreads) {
let tasks = []
if (numThreads == 0) {
throw new PoolException(`attempted to schedule task with threads = 0 (operation = grow, target = ${target})`)
}
for (const server of this.pool) {
const capacity = server.getGrowCapacity()
if (capacity == 0) {
// this server is full, so we skip it
continue
}
const newThreads = numThreads > capacity ? capacity : numThreads
const task = server.execGrow(target, newThreads)
if (task.pid != 0) {
tasks.push(task)
// reduce the number of remaining threads by the number of thread started by the new task
numThreads -= newThreads
if (numThreads <= 0) {
// all threads scheduled, we can stop assigning them now
break
}
}
}
if (tasks.length == 0) {
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = grow, target = ${target}, threads = ${numThreads})`)
}
return tasks
}
execWeaken(target, numThreads) {
let tasks = []
if (numThreads == 0) {
throw new PoolException(`attempted to schedule task with threads = 0 (tasks = weaken, target = ${target} `)
}
for (const server of this.pool) {
const capacity = server.getWeakenCapacity()
if (capacity == 0) {
// this server is full, so we skip it
continue
}
const newThreads = numThreads > capacity ? capacity : numThreads
const task = server.execWeaken(target, newThreads)
if (task.pid != 0) {
tasks.push(task)
// reduce the number of remaining threads by the number of thread started by the new task
numThreads -= newThreads
if (numThreads <= 0) {
// all threads scheduled, we can stop assigning them now
break
}
}
}
if (tasks.length == 0) {
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = weaken, target = ${target}, threads = ${numThreads})`)
}
return tasks
}
execAutoRack() {
// TODO: skip if fully upgraded
let tasks = []
for (const server of this.pool) {
if (server.availRam >= autoRackRamUsage) {
const task = server.execAutoRack()
if (task.pid != 0) {
tasks.push(task)
break
}
}
}
return tasks
}
execAutoPwn() {
// TODO: skip if fully upgraded
let tasks = []
for (const server of this.pool) {
if (server.availRam >= autoRackPwnUsage) {
const task = server.execAutoPwn()
if (task.pid != 0) {
tasks.push(task)
break
}
}
}
return tasks
}
execOptimalHack(target, factor = 1) {
const numThreads = Math.min(this.getHackCapacity(), target.computeOptimalHackThreadCount() * factor)
ns.print(`INFO: hacking ${target} with ${numThreads} threads`)
return this.execHack(target, numThreads)
}
execOptimalGrow(target, factor = 1) {
const numThreads = Math.min(this.getGrowCapacity(), target.computeOptimalGrowThreadCount() * factor)
ns.print(`INFO: growing ${target} with ${numThreads} threads`)
return this.execGrow(target, numThreads)
}
execOptimalWeaken(target, factor = 1) {
const numThreads = Math.min(this.getWeakenCapacity(), target.computeOptimalWeakenThreadCount() * factor)
ns.print(`INFO: weakening ${target} with ${numThreads} threads`)
return this.execWeaken(target, numThreads)
}
refresh() {
// recursively list all available servers on network
const getAvailableServers = (hostnameToAnalyze = null, availableServers = []) => {
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
const parentServer = new Server(parentHostname)
// check if we alreay have added this server to the list
if (!availableServers.map((n) => n.toString()).includes(parentServer.toString())) {
// add to the list
availableServers.push(parentServer)
// add parent servers to the list
availableServers = getAvailableServers(parentServer.hostname, availableServers)
}
}
return availableServers
}
const availableServers = getAvailableServers()
ns.print(`INFO: found ${availableServers.length} servers in network`)
let serversToAdd = []
availableServers
.filter(server => server.hasRootAccess && server.maxRam > 0) // is able to run tasks
.filter(server => !this.pool.find(poolServer => poolServer.hostname == server.hostname)) // we don't already have this server in the pool
.filter(server => !loopSettings.poolBlacklist.find(blacklistedHostname => blacklistedHostname == server.hostname)) // hostname is blacklisted
.forEach(server => serversToAdd.push(server))
this.pool = this.pool.concat(serversToAdd)
ns.print(`INFO: added ${serversToAdd.length} servers to pool`)
return serversToAdd
}
prepare(target) {
let tasks = []
if (target.availMoney != target.maxMoney) {
// schedule as many grow() as able or necessary
const growThreads = Math.min(this.getGrowCapacity(), Math.ceil(ns.growthAnalyze(target.hostname, target.maxMoney / target.availMoney)))
tasks = tasks.concat(this.execGrow(target, growThreads))
// try to offset the security growth with some parallel weaken() as able
const weakenThreads = Math.min(this.getWeakenCapacity(), Math.ceil(((target.currSecurityLevel - target.minSecurityLevel) + (growThreads * 0.004)) / 0.04)) // 0.05 is more optimal, but t doesn't leave a lot of margin
if (weakenThreads > 0) {
// it's okay to skip weaken() if we have no capacity, we will do it next cycle
tasks = tasks.concat(this.execWeaken(target, weakenThreads))
}
}
return new Batch(tasks, target)
}
attack(target) {
if (target.availMoney != target.maxMoney) {
// if money is not at maximum, grow()
return this.execOptimalGrow(target)
}
else if (target.currSecurityLevel != target.minSecurityLevel) {
// if security is not at minimum, weaken()
return this.execOptimalWeaken(target)
}
else {
// if money is at maximum and security is at minimum, hack()
return this.execOptimalHack(target)
}
}
}
class Task {
constructor(server, pid, operation, completionTime, target = null) {
this.server = server
this.pid = pid
this.operation = operation
this.completionTime = completionTime
this.target = target
}
isRunning() {
return this.server.pids.includes(this.pid)
}
getTimeUntilComplete() {
return Math.max(0, this.completionTime - Date.now())
}
async sleepUntilCompleted() {
while (this.isRunning()) {
const sleepTime = this.getTimeUntilComplete() + 200
ns.print(`INFO: sleeping ${sleepTime} milliseconds for pid ${this.pid} on server ${this.server} to complete (op = ${this.operation}, target = ${this.target})`)
await ns.sleep(sleepTime)
}
}
}
class Batch {
constructor(tasks, target = null) {
this.tasks = tasks
this.target = target
}
get completionTime() {
return Math.max(...this.tasks.map(t => t.completionTime))
}
isRunning() {
return this.tasks.reduce((r, t) => r || t.isRunning(), false)
}
getTimeUntilComplete() {
return Math.max(...this.tasks.map(t => t.getTimeUntilComplete()))
}
async sleepUntilCompleted() {
while (this.isRunning()) {
const sleepTime = this.getTimeUntilComplete() + 200
ns.print(`INFO: sleeping ${sleepTime} milliseconds for batch to complete (tasks = [${this.tasks.map(t => t.operation).join(",")}], target = ${this.target})`)
await ns.sleep(sleepTime)
}
}
}
// ns.exec("map.js", "home")
const loop = async () => {
const pool = new ServerPool()
let tasks = []
let preparingTargets = []
let targets = []
let lastRefreshLevel = ns.getHackingLevel()
let refresh = true
let reScore = false
const expFarmTarget = new Server(loopSettings.expTargetHostname)
expFarmTarget.targetOf = 0
ns.atExit(() => pool.killAll())
// run autopwn once on startup to enable new servers
ns.exec("/payloads/autopwn.js", "home")
await ns.sleep(500)
while (true) {
// every 10 level-up refresh the list of targets and recalculate the target priorities
if (ns.getHackingLevel() - lastRefreshLevel >= 10) {
ns.print(`INFO: triggering auto-refresh of targets`)
refresh = true
reScore = true
lastRefreshLevel = ns.getHackingLevel()
}
// refresh targets
if (refresh) {
pool.refresh()
tasks.concat(pool.execAutoPwn())
pool.pool
.filter(target => !preparingTargets.includes(target))
.filter(target => !targets.includes(target))
.filter(target => target.hackable) // server is hackable
.filter(target => loopSettings.moneyTargetsWhitelist.length != 0 ? loopSettings.moneyTargetsWhitelist.includes(target.hostname) : true)
.filter(target => !loopSettings.moneyTargetsBlacklist.includes(target.hostname))
.forEach(target => {
target.targetOf = 0
ns.print(`INFO: added ${target} as potential target`)
preparingTargets.push(target)
})
refresh = false
await ns.sleep(500)
}
// prepare targets
// TODO: this is currently very expensive because we need to place all target in ideal conditions before computing the score
// in the future, calculate score and prepare only relevant targets
let tmpPreparingTargets = [] // to avoid concurrent modification
for (const target of preparingTargets) {
try {
if (target.targetOf == 0) {
const prepareBatch = pool.prepare(target)
if (prepareBatch.tasks.length == 0) {
// target is ready to go
// target.targetData.operation = "ready"
targets.push(target)
reScore = true // trigger a reScore
}
else {
// track the prepare batch
tasks.push(prepareBatch)
target.targetOf += 1
tmpPreparingTargets.push(target)
}
}
else {
// we can't schedule more tasks, but keep track of the target
tmpPreparingTargets.push(target)
}
}
catch (e) {
if (e instanceof PoolException) {
ns.print(e.toString())
tmpPreparingTargets.push(target) // TODO: this is very jank
if (e.fatal) {
throw e
}
}
else {
throw e
}
}
}
preparingTargets = tmpPreparingTargets
// score targets
if (reScore) {
ns.print(`INFO: sorting ${targets.length} targets`)
const score = (target) => target.maxMoney * target.hackChance / target.growTime
targets.sort((t1, t2) => score(t2) - score(t1))
reScore = false
}
// extract money
if (targets.length != 0) {
for (let i = 0; i < Math.min(maxTargets, targets.length); i++) {
try {
const target = targets[i]
if (target.targetOf == 0) {
// ns.print(`hacking ${target}`)
// ns.print(`maxMoney: ${target.maxMoney}`)
// ns.print(`availMoney: ${target.availMoney}`)
// ns.print(`minSecurity: ${target.minSecurityLevel}`)
// ns.print(`currSecurity: ${target.currSecurityLevel}`)
const newTasks = pool.attack(target)
tasks = tasks.concat(newTasks)
target.targetOf += newTasks.length
}
}
catch (e) {
if (e instanceof PoolException) {
ns.print(e.toString())
}
else {
throw e
}
}
}
}
// farm exp
if (expFarmTarget.targetOf == 0 && loopSettings.expMaxAvailRamUsePercent != 0) {
// we want to avoid filling up the ram to not starve other tasks
const usableRam = pool.availRam * loopSettings.expMaxAvailRamUsePercent
let newTasks = []
try {
if (expFarmTarget.currSecurityLevel > expFarmTarget.minSecurityLevel) {
// minimize security to speed up hack()
newTasks = pool.execWeaken(expFarmTarget, Math.floor(usableRam / weakenRamUsage))
}
else {
// hack() nonstop
newTasks = pool.execHack(expFarmTarget, Math.floor(usableRam / hackRamUsage))
}
expFarmTarget.targetOf += newTasks.length
tasks = tasks.concat(newTasks)
}
catch (e) {
// ns.print(e.toString())
if (!e instanceof PoolException) {
throw e
}
}
}
// sleep until new tick
// ns.print(tasks)
// ns.print(targets)
// ns.print(preparingTargets)
ns.print(`pool = ${pool.size}, RAM = ${ns.formatRam(pool.usedRam)}/${ns.formatRam(pool.maxRam)} (${ns.formatPercent(pool.usedRam / pool.maxRam)}), targets = ${targets.length}/${targets.length + preparingTargets.length}, tasks = ${tasks.length}, refreshCountDown = ${10 - (ns.getHackingLevel() - lastRefreshLevel)}`)
if (tasks.length > 0) {
tasks.sort((t1, t2) => t1.completionTime - t2.completionTime)
await tasks[0].sleepUntilCompleted()
while (tasks.length != 0 && !tasks[0].isRunning()) {
const task = tasks.shift()
task.target.targetOf--
}
}
else {
throw "idle"
}
}
}
await loop()
}

680
src/cc_v3.js Normal file
View File

@ -0,0 +1,680 @@
/** @param {NS} ns */
export async function main(ns) {
const hackMinMoneyLimitPercent = 0.75
const hackMaxSecurityIncreaseLimit = 5
const hackMinLevelThreshPercent = 1
const maxTargets = Infinity
const loopSettings = {
"poolBlacklist": [],
// "poolBlacklist": ["home"],
"moneyTargetsWhitelist": [],
"moneyTargetsWhitelist": ["n00dles", "harakiri-sushi", "phantasy"],
// "moneyTargetsBlacklist": [],
"moneyTargetsBlacklist": ["joesguns"], // blacklist joesguns to avoid conflict with exp farm
"expTargetHostname": "joesguns",
"expMaxAvailRamUsePercent": 0.95,
}
const hackRamUsage = ns.getScriptRam("/payloads/hack.js")
const growRamUsage = ns.getScriptRam("/payloads/grow.js")
const weakenRamUsage = ns.getScriptRam("/payloads/weaken.js")
const autoRackRamUsage = ns.getScriptRam("/payloads/autorack.js")
const autoRackPwnUsage = ns.getScriptRam("/payloads/autopwn.js")
const moneyFormat = new Intl.NumberFormat({ style: "currency" })
ns.clearLog()
ns.tail()
ns.disableLog('ALL')
// ns.disableLog('getServerUsedRam')
// ns.disableLog('getServerMaxRam')
// ns.print(`${pwnMethods.length} pwning method(s) available!`)
class PoolException {
constructor(message) {
this.message = message
}
toString() {
return `WARNING: ${this.message}`
}
}
class Server {
constructor(hostname) {
this.hostname = hostname
ns.scp(["/payloads/grow.js", "/payloads/hack.js", "/payloads/weaken.js", "/payloads/autorack.js", "/payloads/autopwn.js"], this.hostname)
}
toString() {
return this.hostname
}
get hasRootAccess() {
return ns.hasRootAccess(this.hostname)
}
get hackable() {
return this.hasRootAccess && this.maxMoney > 0 && (this.requiredHackLevel <= ns.getHackingLevel() * hackMinLevelThreshPercent || this.requiredHackLevel == 1)
}
get requiredHackLevel() {
return ns.getServerRequiredHackingLevel(this.hostname)
}
get maxRam() {
return ns.getServerMaxRam(this.hostname)
}
get usedRam() {
return ns.getServerUsedRam(this.hostname)
}
get availRam() {
return this.maxRam - this.usedRam
}
get maxMoney() {
return ns.getServerMaxMoney(this.hostname)
}
get availMoney() {
return ns.getServerMoneyAvailable(this.hostname)
}
get minSecurityLevel() {
return ns.getServerMinSecurityLevel(this.hostname)
}
get currSecurityLevel() {
return ns.getServerSecurityLevel(this.hostname)
}
get hackTime() {
return Math.ceil(ns.getHackTime(this.hostname))
}
get growTime() {
return Math.ceil(ns.getGrowTime(this.hostname))
}
get weakenTime() {
return Math.ceil(ns.getWeakenTime(this.hostname))
}
get hackChance() {
return ns.hackAnalyzeChance(this.hostname)
}
get pids() {
return ns.ps(this.hostname).map(process => process.pid)
}
getScriptCapacity(script) {
return Math.floor(this.availRam / ns.getScriptRam(script))
}
getHackCapacity() {
return Math.floor(this.availRam / hackRamUsage)
}
getGrowCapacity() {
return Math.floor(this.availRam / growRamUsage)
}
getWeakenCapacity() {
return Math.floor(this.availRam / weakenRamUsage)
}
computeOptimalHackThreadCount() {
const requiredThreads = Math.ceil(((this.availMoney - (this.maxMoney * hackMinMoneyLimitPercent)) / this.availMoney) / ns.hackAnalyze(this.hostname))
let securityLimitedThreads = 0
let predictedSecurityIncrease = 0
while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
predictedSecurityIncrease = ns.hackAnalyzeSecurity(++securityLimitedThreads)
}
return Math.min(requiredThreads, securityLimitedThreads)
}
computeOptimalGrowThreadCount() {
const requiredThreads = Math.ceil(ns.growthAnalyze(this.hostname, this.maxMoney / this.availMoney))
// let securityLimitedThreads = 0
// let predictedSecurityIncrease = 0
// while ((predictedSecurityIncrease + this.currSecurityLevel) < (this.minSecurityLevel + hackMaxSecurityIncreaseLimit)) {
// predictedSecurityIncrease = ns.growthAnalyzeSecurity(++securityLimitedThreads)
// }
// return Math.min(requiredThreads, securityLimitedThreads)
return requiredThreads
}
computeOptimalWeakenThreadCount() {
let requiredThreads = 0
let prediction = this.currSecurityLevel
while (prediction > this.minSecurityLevel) {
prediction = this.currSecurityLevel - ns.weakenAnalyze(++requiredThreads)
}
return requiredThreads
}
execHack(target, numThreads = 1) {
const pid = ns.exec("/payloads/hack.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
if (pid == 0) {
throw `ERROR: failed to run hack() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
}
return new Task(this, pid, "hack", Date.now() + target.hackTime, target)
}
execGrow(target, numThreads = 1) {
const pid = ns.exec("/payloads/grow.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
if (pid == 0) {
throw `ERROR: failed to run grow() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getGrowCapacity()}`
}
return new Task(this, pid, "grow", Date.now() + target.growTime, target)
}
execWeaken(target, numThreads = 1) {
const pid = ns.exec("/payloads/weaken.js", this.hostname, numThreads, crypto.randomUUID(), numThreads, target.hostname)
if (pid == 0) {
throw `ERROR: failed to run weaken() from ${this.hostname}, target = ${target}, numThreads = ${numThreads}, capacity = ${this.getHackCapacity()}`
}
return new Task(this, pid, "weaken", Date.now() + target.weakenTime, target)
}
execAutoRack() {
const pid = ns.exec("/payloads/autorack.js", this.hostname, 1, crypto.randomUUID())
if (pid == 0) {
throw `ERROR: failed to run autorack on ${this.hostname}, availRAM = ${this.availRam}`
}
return new Task(this, pid, "autorack", Date.now())
}
execAutoPwn() {
const pid = ns.exec("/payloads/autopwn.js", this.hostname, 1, crypto.randomUUID())
if (pid == 0) {
throw `ERROR: failed to run autopwn on ${this.hostname}, availRAM = ${this.availRam}`
}
return new Task(this, pid, "autorack", Date.now())
}
}
class ServerPool {
constructor() {
this.pool = []
}
get size() {
return this.pool.length
}
get maxRam() {
return this.pool.reduce((x, y) => x + y.maxRam, 0)
}
get usedRam() {
return this.pool.reduce((x, y) => x + y.usedRam, 0)
}
get availRam() {
return this.pool.reduce((x, y) => x + y.availRam, 0)
}
getHackCapacity() {
return this.pool.reduce((x, y) => x + y.getHackCapacity(), 0)
}
getGrowCapacity() {
return this.pool.reduce((x, y) => x + y.getGrowCapacity(), 0)
}
getWeakenCapacity() {
return this.pool.reduce((x, y) => x + y.getWeakenCapacity(), 0)
}
killAll() {
this.pool.forEach(server => ns.killall(server.hostname))
}
execHack(target, numThreads, split=true) {
let tasks = []
if (numThreads == 0) {
throw new PoolException(`attempted to schedule task with threads = 0 (operation = hack, target = ${target})`)
}
for (const server of this.pool) {
const capacity = server.getHackCapacity()
if (capacity == 0) {
// this server is full, so we skip it
continue
}
else if(!split && capacity < numThreads) {
// we don't want to split into multiple threads and this server doesn't have enough capacity, so we skip it
continue
}
const newThreads = numThreads > capacity ? capacity : numThreads
const task = server.execHack(target, newThreads)
if (task.pid != 0) {
tasks.push(task)
// reduce the number of remaining threads by the number of thread started by the new task
numThreads -= newThreads
if (numThreads <= 0) {
// all threads scheduled, we can stop assigning them now
break
}
}
}
if (tasks.length == 0) {
throw new PoolException(`attempted to schedule task, put pool has no capacity (operation = hack, target = ${target}, threads = ${numThreads})`)
}
return tasks
}
execGrow(target, numThreads, split=true) {
let tasks = []
if (numThreads == 0) {
throw new PoolException(`attempted to schedule task with threads = 0 (operation = grow, target = ${target})`)
}
for (const server of this.pool) {
const capacity = server.getGrowCapacity()
if (capacity == 0) {
// this server is full, so we skip it
continue
}
else if(!split && capacity < numThreads) {
// we don't want to split into multiple threads and this server doesn't have enough capacity, so we skip it
continue
}
const newThreads = numThreads > capacity ? capacity : numThreads
const task = server.execGrow(target, newThreads)
if (task.pid != 0) {
tasks.push(task)
// reduce the number of remaining threads by the number of thread started by the new task
numThreads -= newThreads
if (numThreads <= 0) {
// all threads scheduled, we can stop assigning them now
break
}
}
}
if (tasks.length == 0) {
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = grow, target = ${target}, threads = ${numThreads})`)
}
return tasks
}
execWeaken(target, numThreads, split=true) {
let tasks = []
if (numThreads == 0) {
throw new PoolException(`attempted to schedule task with threads = 0 (tasks = weaken, target = ${target} `)
}
for (const server of this.pool) {
const capacity = server.getWeakenCapacity()
if (capacity == 0) {
// this server is full, so we skip it
continue
}
else if(!split && capacity < numThreads) {
// we don't want to split into multiple threads and this server doesn't have enough capacity, so we skip it
continue
}
const newThreads = numThreads > capacity ? capacity : numThreads
const task = server.execWeaken(target, newThreads)
if (task.pid != 0) {
tasks.push(task)
// reduce the number of remaining threads by the number of thread started by the new task
numThreads -= newThreads
if (numThreads <= 0) {
// all threads scheduled, we can stop assigning them now
break
}
}
}
if (tasks.length == 0) {
throw new PoolException(`attempted to schedule task, put pool has no capacity (task = weaken, target = ${target}, threads = ${numThreads})`)
}
return tasks
}
execAutoRack() {
// TODO: skip if fully upgraded
let tasks = []
for (const server of this.pool) {
if (server.availRam >= autoRackRamUsage) {
const task = server.execAutoRack()
if (task.pid != 0) {
tasks.push(task)
break
}
}
}
return tasks
}
execAutoPwn() {
// TODO: skip if fully upgraded
let tasks = []
for (const server of this.pool) {
if (server.availRam >= autoRackPwnUsage) {
const task = server.execAutoPwn()
if (task.pid != 0) {
tasks.push(task)
break
}
}
}
return tasks
}
execOptimalHack(target, factor = 1) {
const numThreads = Math.min(this.getHackCapacity(), target.computeOptimalHackThreadCount() * factor)
ns.print(`INFO: hacking ${target} with ${numThreads} threads`)
return this.execHack(target, numThreads)
}
execOptimalGrow(target, factor = 1) {
const numThreads = Math.min(this.getGrowCapacity(), target.computeOptimalGrowThreadCount() * factor)
ns.print(`INFO: growing ${target} with ${numThreads} threads`)
return this.execGrow(target, numThreads)
}
execOptimalWeaken(target, factor = 1) {
const numThreads = Math.min(this.getWeakenCapacity(), target.computeOptimalWeakenThreadCount() * factor)
ns.print(`INFO: weakening ${target} with ${numThreads} threads`)
return this.execWeaken(target, numThreads)
}
refresh() {
// recursively list all available servers on network
const getAvailableServers = (hostnameToAnalyze = null, availableServers = []) => {
for (const parentHostname of ns.scan(hostnameToAnalyze)) {
const parentServer = new Server(parentHostname)
// check if we alreay have added this server to the list
if (!availableServers.map((n) => n.toString()).includes(parentServer.toString())) {
// add to the list
availableServers.push(parentServer)
// add parent servers to the list
availableServers = getAvailableServers(parentServer.hostname, availableServers)
}
}
return availableServers
}
const availableServers = getAvailableServers()
ns.print(`INFO: found ${availableServers.length} servers in network`)
let serversToAdd = []
availableServers
.filter(server => server.hasRootAccess && server.maxRam > 0) // is able to run tasks
.filter(server => !this.pool.find(poolServer => poolServer.hostname == server.hostname)) // we don't already have this server in the pool
.filter(server => !loopSettings.poolBlacklist.find(blacklistedHostname => blacklistedHostname == server.hostname)) // hostname is blacklisted
.forEach(server => serversToAdd.push(server))
this.pool = this.pool.concat(serversToAdd)
ns.print(`INFO: added ${serversToAdd.length} servers to pool`)
return serversToAdd
}
prepare(target) {
let tasks = []
if (target.availMoney != target.maxMoney) {
// schedule as many grow() as able or necessary
const growThreads = Math.min(this.getGrowCapacity(), Math.ceil(ns.growthAnalyze(target.hostname, target.maxMoney / target.availMoney)))
tasks = tasks.concat(this.execGrow(target, growThreads, true))
// try to offset the security growth with some parallel weaken() as able
const weakenThreads = Math.min(this.getWeakenCapacity(), Math.ceil(((target.currSecurityLevel - target.minSecurityLevel) + (growThreads * 0.004)) / 0.04)) // 0.05 is more optimal, but t doesn't leave a lot of margin
if (weakenThreads > 0) {
// it's okay to skip weaken() if we have no capacity, we will do it next cycle
tasks = tasks.concat(this.execWeaken(target, weakenThreads, true))
}
}
return new Batch(tasks, target)
}
attack(target) {
if (target.availMoney != target.maxMoney) {
// if money is not at maximum, grow()
return this.execOptimalGrow(target)
}
else if (target.currSecurityLevel != target.minSecurityLevel) {
// if security is not at minimum, weaken()
return this.execOptimalWeaken(target)
}
else {
// if money is at maximum and security is at minimum, hack()
return this.execOptimalHack(target)
}
}
}
class Task {
constructor(server, pid, operation, completionTime, target = null) {
this.server = server
this.pid = pid
this.operation = operation
this.completionTime = completionTime
this.target = target
}
isRunning() {
return this.server.pids.includes(this.pid)
}
getTimeUntilComplete() {
return Math.max(0, this.completionTime - Date.now())
}
async sleepUntilCompleted() {
while (this.isRunning()) {
const sleepTime = this.getTimeUntilComplete() + 200
ns.print(`INFO: sleeping ${sleepTime} milliseconds for pid ${this.pid} on server ${this.server} to complete (op = ${this.operation}, target = ${this.target})`)
await ns.sleep(sleepTime)
}
}
}
class Batch {
constructor(tasks, target = null) {
this.tasks = tasks
this.target = target
}
get completionTime() {
return Math.max(...this.tasks.map(t => t.completionTime))
}
isRunning() {
return this.tasks.reduce((r, t) => r || t.isRunning(), false)
}
getTimeUntilComplete() {
return Math.max(...this.tasks.map(t => t.getTimeUntilComplete()))
}
async sleepUntilCompleted() {
while (this.isRunning()) {
const sleepTime = this.getTimeUntilComplete() + 200
ns.print(`INFO: sleeping ${sleepTime} milliseconds for batch to complete (tasks = [${this.tasks.map(t => t.operation).join(",")}], target = ${this.target})`)
await ns.sleep(sleepTime)
}
}
}
// ns.exec("map.js", "home")
const loop = async () => {
const pool = new ServerPool()
let tasks = []
let preparingTargets = []
let targets = []
let lastRefreshLevel = ns.getHackingLevel()
let refresh = true
let reScore = false
const expFarmTarget = new Server(loopSettings.expTargetHostname)
expFarmTarget.targetOf = 0
ns.atExit(() => pool.killAll())
// run autopwn once on startup to enable new servers
ns.exec("/payloads/autopwn.js", "home")
await ns.sleep(500)
while (true) {
// every 10 level-up refresh the list of targets and recalculate the target priorities
if (ns.getHackingLevel() - lastRefreshLevel >= 10) {
ns.print(`INFO: triggering auto-refresh of targets`)
refresh = true
reScore = true
lastRefreshLevel = ns.getHackingLevel()
}
// refresh targets
if (refresh) {
pool.refresh()
tasks.concat(pool.execAutoPwn())
pool.pool
.filter(target => !preparingTargets.includes(target))
.filter(target => !targets.includes(target))
.filter(target => target.hackable) // server is hackable
.filter(target => loopSettings.moneyTargetsWhitelist.length != 0 ? loopSettings.moneyTargetsWhitelist.includes(target.hostname) : true)
.filter(target => !loopSettings.moneyTargetsBlacklist.includes(target.hostname))
.forEach(target => {
target.targetOf = 0
ns.print(`INFO: added ${target} as potential target`)
preparingTargets.push(target)
})
refresh = false
await ns.sleep(500)
}
// prepare targets
// TODO: this is currently very expensive because we need to place all target in ideal conditions before computing the score
// in the future, calculate score and prepare only relevant targets
let tmpPreparingTargets = [] // to avoid concurrent modification
for (const target of preparingTargets) {
try {
if (target.targetOf == 0) {
const prepareBatch = pool.prepare(target)
if (prepareBatch.tasks.length == 0) {
// target is ready to go
// target.targetData.operation = "ready"
targets.push(target)
reScore = true // trigger a reScore
}
else {
// track the prepare batch
tasks.push(prepareBatch)
target.targetOf += 1
tmpPreparingTargets.push(target)
}
}
else {
// we can't schedule more tasks, but keep track of the target
tmpPreparingTargets.push(target)
}
}
catch (e) {
if (e instanceof PoolException) {
ns.print(e.toString())
tmpPreparingTargets.push(target) // TODO: this is very jank
if (e.fatal) {
throw e
}
}
else {
throw e
}
}
}
preparingTargets = tmpPreparingTargets
// score targets
if (reScore) {
ns.print(`INFO: sorting ${targets.length} targets`)
const score = (target) => target.maxMoney * target.hackChance / target.growTime
targets.sort((t1, t2) => score(t2) - score(t1))
reScore = false
}
// extract money
if (targets.length != 0) {
for (let i = 0; i < Math.min(maxTargets, targets.length); i++) {
try {
const target = targets[i]
if (target.targetOf == 0) {
// ns.print(`hacking ${target}`)
// ns.print(`maxMoney: ${target.maxMoney}`)
// ns.print(`availMoney: ${target.availMoney}`)
// ns.print(`minSecurity: ${target.minSecurityLevel}`)
// ns.print(`currSecurity: ${target.currSecurityLevel}`)
const newTasks = pool.attack(target)
tasks = tasks.concat(newTasks)
target.targetOf += newTasks.length
}
}
catch (e) {
if (e instanceof PoolException) {
ns.print(e.toString())
}
else {
throw e
}
}
}
}
// farm exp
if (expFarmTarget.targetOf == 0 && loopSettings.expMaxAvailRamUsePercent != 0) {
// we want to avoid filling up the ram to not starve other tasks
const usableRam = pool.availRam * loopSettings.expMaxAvailRamUsePercent
let newTasks = []
try {
if (expFarmTarget.currSecurityLevel > expFarmTarget.minSecurityLevel) {
// minimize security to speed up hack()
newTasks = pool.execWeaken(expFarmTarget, Math.floor(usableRam / weakenRamUsage))
}
else {
// hack() nonstop
newTasks = pool.execHack(expFarmTarget, Math.floor(usableRam / hackRamUsage))
}
expFarmTarget.targetOf += newTasks.length
tasks = tasks.concat(newTasks)
}
catch (e) {
// ns.print(e.toString())
if (!e instanceof PoolException) {
throw e
}
}
}
// sleep until new tick
// ns.print(tasks)
// ns.print(targets)
// ns.print(preparingTargets)
ns.print(`pool = ${pool.size}, RAM = ${ns.formatRam(pool.usedRam)}/${ns.formatRam(pool.maxRam)} (${ns.formatPercent(pool.usedRam / pool.maxRam)}), targets = ${targets.length}/${targets.length + preparingTargets.length}, tasks = ${tasks.length}, refreshCountDown = ${10 - (ns.getHackingLevel() - lastRefreshLevel)}`)
if (tasks.length > 0) {
tasks.sort((t1, t2) => t1.completionTime - t2.completionTime)
await tasks[0].sleepUntilCompleted()
while (tasks.length != 0 && !tasks[0].isRunning()) {
const task = tasks.shift()
task.target.targetOf--
}
}
else {
throw "idle"
}
}
}
await loop()
}

411
src/contractor.js Normal file
View File

@ -0,0 +1,411 @@
/** @param {NS} ns */
export async function main(ns) {
ns.clearLog()
ns.tail()
ns.disableLog('ALL')
const getServers = (hostnameToAnalyze = "home", servers = ["home"]) => {
for (const hostname of ns.scan(hostnameToAnalyze)) {
// check if we alreay have added this server to the list
if (!servers.includes(hostname)) {
// add to the list
servers.push(hostname)
// map the host
servers = getServers(hostname, servers)
}
}
return servers
}
const solvers = new Map([
["Find Largest Prime Factor", (data) => {
const factorize = (n) => {
let results = [];
for (let i = 2; i <= n; i++) {
// check if divisible
if (n % i == 0) {
// divide the number to test
n = n / i;
// store the divisor
results.push(i);
}
}
return results;
}
return factorize(data).pop()
}],
["Subarray with Maximum Sum", (data) => {
let result = 0
// loop 1: walk starting index
for (let i = 0; i < data.length; i++) {
let sum = 0
// loop 2: sum everything from the stating index
for (let j = i; j < data.length; j++) {
sum += data[j]
// if at any point during computing the sum we find a greater sum
if (sum > result) {
result = sum
}
}
}
return result
}],
["Total Ways to Sum", (data) => {
// This can be solved with using the Partition function P
// solution = P(n) - 1
// https://en.wikipedia.org/wiki/Partition_function_(number_theory)
// https://rosettacode.org/wiki/Partition_function_P
const p = n => {
var a = new Array(n + 1)
a[0] = 1n
for (let i = 1; i <= n; i++) {
a[i] = 0n
for (let k = 1, s = 1; s <= i;) {
a[i] += (k & 1 ? a[i - s] : -a[i - s])
k > 0 ? (s += k, k = -k) : (k = -k + 1, s = k * (3 * k - 1) / 2)
}
}
return a[n]
}
return Number(p(data) - 1n)
}],
["Total Ways to Sum II", (data) => {
// https://www.geeksforgeeks.org/coin-change-dp-7/
// https://www.geeksforgeeks.org/understanding-the-coin-change-problem-with-dynamic-programming/
const target = data[0]
const numberPool = data[1]
let memoizationTable = new Array(target + 1).fill(0)
// there is always 1 way of returning 0 sum; with 0 number
memoizationTable[0] = 1
// loop through available number in the pool
for (let i = 0; i < numberPool.length; i++) {
// compute each entry in the memoization table
for (let j = 0; j < memoizationTable.length; j++) {
// ensure we do not lookup negative indices
if (numberPool[i] <= j) {
// compute the value at this index and store in the table to be referenced next iteration
memoizationTable[j] += memoizationTable[j - numberPool[i]]
}
}
}
// last entry in the table is our awnser
return memoizationTable.pop()
}],
["Spiralize Matrix", (data) => {
const matrix = data
let resultSize = matrix.length * matrix[0].length
let result = []
let topStop = 1
let bottomStop = matrix.length - 1
let leftStop = 0
let rightStop = matrix[0].length - 1
let pos = [0, 0]
let vector = [1, 0]
while (result.length < resultSize) {
if (vector[0] == 1 && vector[1] == 0) {
// going right
while (pos[1] <= rightStop) {
result.push(matrix[pos[0]][pos[1]])
pos[1]++
}
rightStop--
pos[0]++
pos[1]--
// go down next
vector = [0, 1]
}
else if (vector[0] == -1 && vector[1] == 0) {
// going left
while (pos[1] >= leftStop) {
result.push(matrix[pos[0]][pos[1]])
pos[1]--
}
leftStop++;
// go up next
pos[0]--
pos[1]++
vector = [0, -1]
}
else if (vector[0] == 0 && vector[1] == 1) {
// going down
while (pos[0] <= bottomStop) {
result.push(matrix[pos[0]][pos[1]])
pos[0]++
}
bottomStop--
// go right next
pos[0]--
pos[1]--
vector = [-1, 0]
}
else if (vector[0] == 0 && vector[1] == -1) {
// going up
while (pos[0] >= topStop) {
result.push(matrix[pos[0]][pos[1]])
pos[0]--
}
topStop++
// go left next
pos[0]++
pos[1]++
vector = [1, 0]
}
else {
ns.print("Invalid state")
break
}
}
return result
}],
["Array Jumping Game", (data) => {
return solvers.get("Array Jumping Game II")(data) == 0 ? 0 : 1
}],
["Array Jumping Game II", (data) => {
let indexJumpSums = [] // contain an array of to index this index can jump
let position = 0
let moves = [position]
let goal = data.length - 1
for (let i = 0; i < data.length; i++) {
indexJumpSums[i] = data[i] + i
}
do {
position = moves[moves.length - 1]
const jumpValue = data[position]
if (goal - position <= jumpValue) { // check if the goal is within reach
moves.push(goal) // reached the goal
break
}
else {
// find the largest jump possible at our current position
let maxJump = position
for (let i = position; i <= position + jumpValue; i++) {
if (indexJumpSums[i] > indexJumpSums[maxJump]) {
maxJump = i // this index allows us to jump the furthest
}
}
if (maxJump == position) { // we haven't moved, we are stuck at this index
moves = [0] // impossible!
break
}
moves.push(maxJump)
}
} while (position != goal)
return moves.length - 1
}],
["Merge Overlapping Intervals", (data) => {
let result = []
let newInterval = []
// for this algo to work, we need to sort the input by the min of the intervals
data.sort((a, b) => a[0] - b[0])
for (let i = 0; i < data.length; i++) {
// first interval
if (newInterval.length == 0) {
newInterval = data[i]
}
// if the min of the current inverval is less than the max of the newInterval
if (data[i][0] <= newInterval[1]) {
newInterval[1] = Math.max(data[i][1], newInterval[1]) // set the largest of the max to be the max of the newInterval
}
else {
result.push(newInterval) // we are out of the new interval so push it to the result
newInterval = data[i] // start a new newInterval
}
// last interval
if (i == data.length - 1) {
newInterval[1] = Math.max(data[i][1], newInterval[1])
result.push(newInterval)
}
}
return result
}],
// ["Generate IP Addresses", (data) => {
// }],
["HammingCodes: Integer to Encoded Binary", (data) => {
const dataBits = data.toString(2).split("").map(x => parseInt(x))
// compute the number of parity bits
let parityBitsCount = 0
while ((1 << parityBitsCount) < (dataBits.length + parityBitsCount + 1)) {
parityBitsCount++
}
// initialize encodedData and setup the data at the correct indices
let encodedData = new Array(dataBits.length + parityBitsCount)
let dataIndex = 0
for (let i = 0; i < encodedData.length; i++) {
// TODO: why this works?
if (Math.log2(i + 1) % 1 == 0) { // check if i is power of 2
// initialize parity bit
encodedData[i] = 0
}
else {
// set data bit
encodedData[i] = dataBits[dataIndex++]
}
}
// set the parity bits
for (let i = 0; i < parityBitsCount; i++) { // loop every parity bits
const parityIndex = (1 << i) - 1;
let parity = 0;
// xor together every bits this parity bit covers
for (let j = parityIndex; j < encodedData.length; j += (parityIndex + 1) * 2) {
for (let k = j; k < j + parityIndex + 1; k++) {
parity ^= encodedData[k];
}
}
// set parity bit
encodedData[parityIndex] = parity;
}
// insert the overall parity bit at index 0
encodedData.unshift(encodedData.reduce((x, y) => x ^ y))
// reduce encodedData to a string of 1s and 0s
return encodedData.reduce((x, y) => x + y, "")
}],
["HammingCodes: Encoded Binary to Integer", (data) => {
// https://www.youtube.com/watch?v=b3NxrZOu_CE
let encodedData = data.split("").map(x => parseInt(x))
// NOTE: bit at index 0 is the overall parity bit
// we will not get a string with 2 errors, so can ignore it
// check for flipped bit and correct it
let errorBitIndex = 0
for (let i = 0; i < encodedData.length; i++) {
if (encodedData[i] == 1) {
errorBitIndex ^= i
}
}
if (errorBitIndex != 0) {
encodedData[errorBitIndex] ^= 1
}
// extract data
let dataBits = []
for (let i = 0; i < encodedData.length; i++) {
if ((i & (i - 1)) != 0 && i != 0) { // check if i is not power of 2
// extract data bit
dataBits.push(encodedData[i])
}
}
return parseInt(dataBits.reduce((x, y) => x + y, ""), 2).toString(10)
}],
["Encryption I: Caesar Cipher", (data) => {
const charList = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
const plaintext = data[0].split("")
const key = data[1]
return plaintext.map(c => {
const i = charList.indexOf(c)
if (i >= 0) {
return charList[(i + (26 - key)) % 26]
}
else {
return ' '
}
}).reduce((x, y) => x + y, "")
}],
["Encryption II: Vigenère Cipher", (data) => {
const charList = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
const cypher = (a, b) => charList[(charList.indexOf(a) + charList.indexOf(b)) % 26]
const plaintext = data[0].split("")
const key = data[1].split("")
let anwser = ""
let keyIndex = 0
for (const char of plaintext) {
anwser += cypher(char, key[keyIndex++ % key.length])
}
return anwser
}]
])
// ns.codingcontract.createDummyContract("Find Largest Prime Factor") // ok
// ns.codingcontract.createDummyContract("Subarray with Maximum Sum") // ok
// ns.codingcontract.createDummyContract("Total Ways to Sum") // ok
// ns.codingcontract.createDummyContract("Total Ways to Sum II") // ok
// ns.codingcontract.createDummyContract("Spiralize Matrix") // ok
// ns.codingcontract.createDummyContract("Array Jumping Game") // ok
// ns.codingcontract.createDummyContract("Array Jumping Game II") //ok
// ns.codingcontract.createDummyContract("Merge Overlapping Intervals") // ok
// ns.codingcontract.createDummyContract("Generate IP Addresses")
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader I")
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader II")
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader III")
// ns.codingcontract.createDummyContract("Algorithmic Stock Trader IV")
// ns.codingcontract.createDummyContract("Minimum Path Sum in a Triangle")
// ns.codingcontract.createDummyContract("Unique Paths in a Grid I")
// ns.codingcontract.createDummyContract("Unique Paths in a Grid II")
// ns.codingcontract.createDummyContract("Shortest Path in a Grid")
// ns.codingcontract.createDummyContract("Sanitize Parentheses in Expression")
// ns.codingcontract.createDummyContract("Find All Valid Math Expressions")
// ns.codingcontract.createDummyContract("HammingCodes: Integer to Encoded Binary") // ok
// ns.codingcontract.createDummyContract("HammingCodes: Encoded Binary to Integer") // ok
// ns.codingcontract.createDummyContract("Proper 2-Coloring of a Graph")
// ns.codingcontract.createDummyContract("Compression I: RLE Compression")
// ns.codingcontract.createDummyContract("Compression II: LZ Decompression")
// ns.codingcontract.createDummyContract("Compression III: LZ Compression")
// ns.codingcontract.createDummyContract("Encryption I: Caesar Cipher") // ok
// ns.codingcontract.createDummyContract("Encryption II: Vigenère Cipher") // ok
// const server = "home"
// for (const contract of ns.ls(server, '.cct')) {
// const contractType = ns.codingcontract.getContractType(contract, server)
// ns.print(`found contract ${contract} of type "${contractType}" on server ${server}`)
// if (solvers.has(contractType)) {
// ns.print("attempting to solve")
// const anwser = solvers.get(contractType)(ns.codingcontract.getData(contract, server))
// const reward = ns.codingcontract.attempt(anwser, contract, server)
// if (reward == "") {
// ns.print(`ERROR: failed to solve contract! anwser = ${anwser}`)
// break
// }
// else {
// ns.print(reward)
// }
// }
// else {
// ns.print(`WARNING: unknown contract type: ${contractType}`)
// }
// }
for (const server of getServers()) {
for (const contract of ns.ls(server, '.cct')) {
const contractType = ns.codingcontract.getContractType(contract, server)
ns.print(`found contract ${contract} of type "${contractType}" on server ${server}`)
if (solvers.has(contractType)) {
const anwser = solvers.get(contractType)(ns.codingcontract.getData(contract, server))
const reward = ns.codingcontract.attempt(anwser, contract, server)
if (anwser == "") {
ns.print(`ERROR: failed to solve contract! anwser = ${anwser}`)
break
}
else {
ns.print(reward)
}
}
else {
ns.print(`WARNING: unknown contract type: ${contractType}`)
}
}
}
}

1
src/dump/map.txt Normal file

File diff suppressed because one or more lines are too long

186
src/gang.js Normal file
View File

@ -0,0 +1,186 @@
/** @param {NS} ns */
export async function main(ns) {
ns.clearLog()
ns.tail()
ns.disableLog('ALL')
const MAKE_MONEY = "Human Trafficking"
const VIGILANTE_JUSTICE = "Vigilante Justice"
const TERRORISM = "Terrorism"
const TRAIN_COMBAT = "Train Combat"
const TERRITORY_WARFARE = "Territory Warfare"
const maxMemberCount = 12
const memberNamePool = [
"CJ",
"Big Smoke",
"Sweet",
"Ryder",
"OG Loc",
"Big Bear",
"Emmet",
"Big Devil",
"Little Devil",
"Tony",
"LB",
"Madd Dogg",
"Little Weasel",
]
function estimateMemberPower(memberName) {
const memberInfo = ns.gang.getMemberInformation(memberName)
return Math.floor(Math.min(
memberInfo.str_asc_mult,
memberInfo.def_asc_mult,
memberInfo.dex_asc_mult,
memberInfo.agi_asc_mult,
) / 1.5)
}
// Credit: Mysteyes. https://discord.com/channels/415207508303544321/415207923506216971/940379724214075442
function getAscendTreshold(memberName) {
const memberInfo = ns.gang.getMemberInformation(memberName)
const mult = Math.min(
memberInfo.str_asc_mult,
memberInfo.def_asc_mult,
memberInfo.dex_asc_mult,
memberInfo.agi_asc_mult,
)
if (mult < 1.632) return 1.6326;
if (mult < 2.336) return 1.4315;
if (mult < 2.999) return 1.284;
if (mult < 3.363) return 1.2125;
if (mult < 4.253) return 1.1698;
if (mult < 4.860) return 1.1428;
if (mult < 5.455) return 1.1225;
if (mult < 5.977) return 1.0957;
if (mult < 6.496) return 1.0869;
if (mult < 7.008) return 1.0789;
if (mult < 7.519) return 1.073;
if (mult < 8.025) return 1.0673;
if (mult < 8.513) return 1.0631;
return 1.0591;
}
function getMinAscensionResult(memberName) {
const memberAscensionResult = ns.gang.getAscensionResult(memberName)
return memberAscensionResult === undefined ? undefined : Math.min(
memberAscensionResult.str,
memberAscensionResult.def,
memberAscensionResult.dex,
memberAscensionResult.agi
)
}
function isAscensionReady(memberName) {
return getMinAscensionResult(memberName) >= getAscendTreshold(memberName)
}
function isRecruitementReady(relativePower, memberCount) {
const res = relativePower / 3 > memberCount - 3 // TODO
// ns.print(ascentionCount / 3)
return res
}
function getGangWarWinChances() {
const gangInfo = ns.gang.getGangInformation()
const otherGangNames = Object.keys(ns.gang.getOtherGangInformation()).filter(x => gangInfo.faction != x && x.territory > 0)
const chancesToWin = otherGangNames.map(x => ns.gang.getChanceToWinClash(x))
return Math.min(...chancesToWin)
}
while (true) {
const gangInfo = ns.gang.getGangInformation()
ns.print("running loop")
// check for and recruit new members if available
while (ns.gang.canRecruitMember()) {
const availableNames = memberNamePool.filter(x => memberNamePool.includes(x))
const memberName = availableNames[Math.floor(Math.random() * availableNames.length)]
ns.gang.recruitMember(memberName)
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
}
const gangMembers = ns.gang.getMemberNames()
// check for and ascend members who are ready
for (const memberName of gangMembers) {
if (isAscensionReady(memberName)) {
// ns.print("ascending " + memberName)
ns.gang.ascendMember(memberName)
}
}
for (const memberName of gangMembers) {
if (isAscensionReady(memberName)) {
// ns.print("ascending " + memberName)
ns.gang.ascendMember(memberName)
}
}
// TODO: equipement
// gang war
if (getGangWarWinChances() >= 0.6) {
// go to war
ns.gang.setTerritoryWarfare(true)
}
else if (getGangWarWinChances() <= 0.55) {
// disengage
ns.gang.setTerritoryWarfare(false)
}
// score each member by their estimated power level
let memberDatas = gangMembers.map(x => [x, estimateMemberPower(x)])
memberDatas.sort((a, b) => b[1] - a[1])
const targetRelativePower = memberDatas[0][1] - 1
for (const memberData of memberDatas) {
const memberName = memberData[0]
const relativePower = memberData[1]
ns.print(memberName)
// ns.print(getMinAscensionResult(memberName) / getAscendTreshold(memberName))
// ns.print(isAscensionReady(memberName))
if (relativePower < targetRelativePower && relativePower < 40) {
ns.print("assignment: catchup")
// bring up newcomers to the same level than established members
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
}
else if (getMinAscensionResult(memberName) / getAscendTreshold(memberName) < 0.9) { // TODO: arbitrary constant to be ajusted
ns.print("assignment: level up after ascention")
// train a member back up after ascention
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
}
else if (gangInfo.wantedPenalty < 0) {
ns.print("assignment: reduce wanted level")
// reduce wanted level
ns.print(gangInfo.wantedPenalty)
ns.gang.setMemberTask(memberName, VIGILANTE_JUSTICE)
}
else if (gangMembers.length < maxMemberCount && isRecruitementReady(relativePower, gangMembers.length)) {
ns.print("assignment: gain respect")
// gain respect to recruit new member
ns.gang.setMemberTask(memberName, TERRORISM)
}
else if (relativePower < 20) { // TODO
ns.print("assignment: train to next ascension")
// train up to next ascention
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
}
else if (gangInfo.territory < 1 && getGangWarWinChances() < 0.95) {
ns.print("assignment: gang war")
// prepare for gang war
ns.gang.setMemberTask(memberName, TERRITORY_WARFARE)
}
else {
ns.print("assignment: make money")
// go for the money
ns.gang.setMemberTask(memberName, MAKE_MONEY) // TODO
}
}
await ns.sleep(5000)
}
}

4
src/karma.js Normal file
View File

@ -0,0 +1,4 @@
/** @param {NS} ns */
export async function main(ns) {
ns.tprint(ns.heart.break())
}

73
src/lib/ServerTree.js Normal file
View File

@ -0,0 +1,73 @@
/** @param {NS} ns */
const importantHosts = ["CSEC", "avmnite-02h", "I.I.I.I", "run4theh111z", "w0r1d_d43m0n"]
class ServerTreeNode {
constructor(ns, serverName) {
this.ns = ns
this.serverName = serverName
this.children = []
this.info = ns.getServer(serverName)
}
toObject() {
return {
[this.serverName]: {
"info": this.info,
"children": this.children.map(x => x.toObject()),
},
}
}
toJson() {
return JSON.stringify(this.toObject())
}
printTable(padding = "", pointer = "", lastNodeOfParent = true) {
if (this.serverName == "home") {
this.ns.tprintf("--R-P--LVL---")
}
this.ns.tprintf(
"%s %s %s %4s %s",
importantHosts.includes(this.serverName) ? "!" : " ",
this.info.hasAdminRights ? "Y" : "N",
this.info.openPortCount,
this.info.requiredHackingSkill,
padding + pointer + this.serverName
)
// prepare padding for children
let newPadding = padding
if (this.serverName != "home") {
if (lastNodeOfParent) {
newPadding += " "
}
else {
newPadding += "| "
}
}
// print children
for (let i = 0; i < this.children.length; i++) {
if (i == this.children.length - 1) {
this.children[i].printTable(newPadding, "└─", true)
}
else {
this.children[i].printTable(newPadding, "├─", false)
}
}
}
}
export function getServerTree(ns, serverNameToScan = "home", scannedServerList = ["home"]) {
const currentNode = new ServerTreeNode(ns, serverNameToScan)
for (const serverName of ns.scan(serverNameToScan)) {
// check if we have already scanned this server
if (!scannedServerList.includes(serverName)) {
// add to the list to avoid duplicates
scannedServerList.push(serverName)
// scan the node and add as a child to the current node
currentNode.children.push(getServerTree(ns, serverName, scannedServerList))
}
}
return currentNode
}

11
src/map.js Normal file
View File

@ -0,0 +1,11 @@
import {getServerTree} from '/lib/ServerTree.js'
/** @param {NS} ns */
export async function main(ns) {
const serverTree = getServerTree(ns)
ns.write("dump/map.txt", serverTree.toJson(), 'w')
if (ns.args[0] != "noout") {
ns.tprintf("")
serverTree.printTable()
}
}

51
src/payloads/autopwn.js Normal file
View File

@ -0,0 +1,51 @@
/** @param {NS} ns */
export async function main(ns) {
// ns.clearLog()
// ns.tail()
ns.disableLog('ALL')
const pwnMethods = []
if (ns.fileExists("BruteSSH.exe")) {
pwnMethods.push(ns.brutessh)
}
if (ns.fileExists("FTPCrack.exe")) {
pwnMethods.push(ns.ftpcrack)
}
if (ns.fileExists("HTTPWorm.exe")) {
pwnMethods.push(ns.httpworm)
}
if (ns.fileExists("relaySMTP.exe")) {
pwnMethods.push(ns.relaysmtp)
}
if (ns.fileExists("SQLInject.exe")) {
pwnMethods.push(ns.sqlinject)
}
const autopwn = (hostnameToAnalyze = "home", serverList = ["home"]) => {
for (const hostname of ns.scan(hostnameToAnalyze)) {
// check if we alreay have added this server to the list
if (!serverList.includes(hostname)) {
// nuke if able
if (ns.hasRootAccess(hostname)) {
ns.print(`already pwned ${hostname}`)
}
else if (ns.getServerNumPortsRequired(hostname) <= pwnMethods.length) {
ns.print(`pwning ${hostname}`)
for (let i = 0; i < ns.getServerNumPortsRequired(hostname); i++) {
pwnMethods[i](hostname)
}
ns.nuke(hostname)
}
else {
ns.print(`missing requirements to pwn ${hostname}. (${pwnMethods.length} < ${ns.getServerNumPortsRequired(hostname)})`)
}
// add to the list
serverList.push(hostname)
// pwn childen
autopwn(hostname, serverList)
}
}
}
autopwn()
}

55
src/payloads/autorack.js Normal file
View File

@ -0,0 +1,55 @@
/** @param {NS} ns */
export async function main(ns) {
const maxServerCount = ns.getPurchasedServerLimit()
let serverCount = ns.getPurchasedServers().length
ns.print(`we have ${serverCount} servers`)
ns.print(`we can have ${maxServerCount} servers`)
const getLowestServerRAM = (servers) => {
return Math.min.apply(Math, servers.map(ns.getServerMaxRam))
}
if (serverCount < maxServerCount) {
// create missing servers
const requestedRAM = 2
const serverCost = ns.getPurchasedServerCost(requestedRAM)
ns.print(`server cost is ${ns.formatNumber(serverCost)}`)
while (serverCount < maxServerCount) {
const serverName = `worker-${serverCount}`
if (ns.getServerMoneyAvailable("home") >= serverCost) {
ns.tprint(`racking server ${serverName}`)
ns.purchaseServer(serverName, requestedRAM)
serverCount++
}
else {
break
}
}
}
if (serverCount == maxServerCount) {
const servers = ns.getPurchasedServers()
let success = true
do {
// upgrade servers
const requestedRAM = getLowestServerRAM(servers) * 2
for (const serverName of servers) {
const serverRAM = ns.getServerMaxRam(serverName)
if (serverRAM < requestedRAM) {
const serverCost = ns.getPurchasedServerUpgradeCost(serverName, requestedRAM)
ns.print(`selected server ${serverName} to be upgraded (current RAM: ${ns.formatRam(serverRAM)}, upgraded RAM: ${ns.formatRam(requestedRAM)}, upgrade cost: ${ns.formatNumber(serverCost)})`)
if (ns.getServerMoneyAvailable("home") >= serverCost) {
ns.tprint(`upgrading server ${serverName}`)
ns.killall(serverName) //TODO: make it more graceful
ns.upgradePurchasedServer(serverName, requestedRAM)
}
else {
success = false
}
}
}
} while (success)
}
}

4
src/payloads/grow.js Normal file
View File

@ -0,0 +1,4 @@
/** @param {NS} ns */
export async function main(ns) {
await ns.grow(ns.args[2], { "threads": ns.args[1] })
}

4
src/payloads/hack.js Normal file
View File

@ -0,0 +1,4 @@
/** @param {NS} ns */
export async function main(ns) {
await ns.hack(ns.args[2], { "threads": ns.args[1] })
}

4
src/payloads/weaken.js Normal file
View File

@ -0,0 +1,4 @@
/** @param {NS} ns */
export async function main(ns) {
await ns.weaken(ns.args[2], { "threads": ns.args[1] })
}

6
src/setup.js Normal file
View File

@ -0,0 +1,6 @@
/** @param {NS} ns */
export async function main(ns) {
const styles = ns.ui.getStyles();
styles.fontFamily = 'Hack';
ns.ui.setStyles(styles);
}

View File

@ -6,6 +6,7 @@
"types": ["vite/client"],
"strict": true,
"allowJs": true,
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true,
"baseUrl": ".",
@ -19,5 +20,5 @@
"@ns": ["./NetscriptDefinitions.d.ts"]
}
},
"include": ["src/**/*.ts", "NetscriptDefinitions.d.ts", "vite.config.ts"]
"include": ["src/**/*.ts", "src/**/*.js", "NetscriptDefinitions.d.ts", "vite.config.ts", "vite.config.js"]
}

View File

@ -1,3 +1,4 @@
/* eslint-env node */
import { defineConfig } from 'viteburner';
import { resolve } from 'path';