1
0
Fork 0

pull existing scripts from bitburner

This commit is contained in:
Massaki Archambault 2023-09-16 09:07:32 -04:00
parent ad0171f878
commit d851d84c05
22 changed files with 10576 additions and 0 deletions

7896
NetscriptDefinitions.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

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);
}