Skip to content

Instantly share code, notes, and snippets.

@jaguilar
Last active May 9, 2018 04:36
Show Gist options
  • Save jaguilar/99808272740f64cdd50cb5bc11e1d383 to your computer and use it in GitHub Desktop.
Save jaguilar/99808272740f64cdd50cb5bc11e1d383 to your computer and use it in GitHub Desktop.
import {getHosts, getAllHosts, schedule} from "hack-dist.js";
const target = args[0];
const numBatchWeaken = 9;
const numBatchGrow = 9;
const numBatchHack = 3;
export const allowedRam = 1000;
function killAllMatching(...args) {
getAllHosts().forEach(h => {
kill(args[0], h.address, ...args.slice(1, args.length));
});
}
async function reset() {
killAllMatching("weaken.js", target, "initial");
killAllMatching("weaken.js", target, "secondary");
killAllMatching("grow.js", target, "initial");
for (let i = 0; i < 9; ++i) {
killAllMatching("weaken.js", target, i);
killAllMatching("grow.js", target, i);
killAllMatching("hack.js", target, i);
}
await sleep(12000);
}
function maxThreads(script, ram) {
return Math.floor(ram / getScriptRam(script, "home"));
}
function numWeaks(numHacks, numGrows) {
return (numHacks * 0.002 * 4 + numGrows * 0.004 * 2) / 0.05;
}
function scaleToAllowed(counts) {
// Figure out how much RAM we're going to use on each thing.
let hackR = counts.hackT * getScriptRam("hack.js", "home");
let growR = counts.growT * getScriptRam("grow.js", "home");
let weakR = counts.weakT * getScriptRam("weaken.js", "home");
// Scale to the amount of ram available.
const scale = allowedRam / (hackR + growR + weakR);
hackR *= scale;
growR *= scale;
weakR *= scale;
counts.hackT = Math.floor(hackR / getScriptRam("hack.js", "home"));
counts.growT = Math.ceil(growR / getScriptRam("grow.js", "home"));
counts.weakT = Math.ceil(weakR / getScriptRam("weaken.js", "home"));
}
// Lowers security to the minimum.
async function lowerSecurity() {
let counts = {
hackT: 0,
growT: 0,
weakT: 1
};
scaleToAllowed(counts);
tprint(JSON.stringify(counts));
// Reduce security using the maximum number of threads.
await schedule(getHosts(), {
script: "weaken.js",
threads: counts.weakT,
args: [target, "initial"]
});
while (getServerSecurityLevel(target) > 1 + getServerMinSecurityLevel(target)) {
await sleep(2000);
}
}
// Grows money to the maximum, while maintaining a low security level.
async function growMoney() {
let counts = {
hackT: 0,
growT: 1,
weakT: numWeaks(0, 1)
};
scaleToAllowed(counts);
tprint(JSON.stringify(counts));
await schedule(getHosts(), {
script: "weaken.js",
threads: Math.max(1, counts.weakT),
args: [target, "secondary"]
});
await schedule(getHosts(), {
script: "grow.js",
threads: counts.growT,
args: [target, "initial"]
});
while (getServerMoneyAvailable(target) < 0.99 * getServerMaxMoney(target)) {
await sleep(2000);
}
}
async function hackForever(hackPerGrow) {
let counts = {
hackT: 1,
growT: 1 / hackPerGrow,
// hack cost 0.002 sec, runs ~4 times per weak.
// grow costs 0.004 sec, runs ~1.3 times per weak (round up).
// weaken weakens the security level by 0.05.
weakT: numWeaks(1, 1 / hackPerGrow)
};
scaleToAllowed(counts);
tprint(JSON.stringify(counts));
// Stagger start -- try to spread the grow, weaken, and hack operations across the timeframe.
const staggerWindow = getHackTime(target);
for (let i = 0; i < 9; ++i) {
const remainingPasses = 9 - i;
if (true) {
const t = Math.round(counts.weakT / remainingPasses);
await schedule(getHosts(), {script: "weaken.js", threads: t, args: [target, i]});
counts.weakT -= t;
}
if (true) {
const t = Math.round(counts.growT / remainingPasses);
await schedule(getHosts(), {script: "grow.js", threads: t, args: [target, i]});
counts.growT -= t;
}
// Since hack occurs with roughly triple the frequency, we schedule a third as many jobs.
if ((i % 3) === 0) {
const t = Math.round(counts.hackT / remainingPasses)
await schedule(getHosts(), {script: "hack.js", threads: t, args: [target, i]});
counts.hackT -= t;
}
await sleep(staggerWindow * 1000 / 9);
}
}
class MoneyStats {
constructor() {
this.data = [];
}
observe() {
this.data.push(getServerMoneyAvailable(target) / getServerMaxMoney(target));
while (this.data.length > 40) {
this.data.shift();
}
}
average() {
const sum = this.data.reduce((acc, v) => acc + v);
return sum / this.data.length;
}
empty() {
return this.data.length === 0;
}
num_samples() {
return this.data.length;
}
}
export async function main() {
disableLog("getScriptRam");
// Strategy:
// - Compute the number of threads of each type that are allowed,
// given a desired ratio of hacks to grows.
// - Scale the number of threads to the allowable amount of RAM.
// - Stagger-schedule the jobs.
// - Wait one cycle.
// - Measure the average server money available for one cycle.
// - If it's above the target, scale up the hack-to-grow ratio slightly.
// - Restart thse process.
//
// TODO: shut down if not present in file.
let targetRatio = 1/10;
while (true) {
reset();
await lowerSecurity();
reset();
await growMoney();
reset();
await hackForever(targetRatio);
// Automatically adjust the ratio of hacks to grows.
const r = new MoneyStats();
let samplingRate = 4; // Number of times we sample per loop.
for (let i = 0; i < 10 && (r.num_samples() < samplingRate || r.average() > 0.9); ++i) {
for (let s = 0; s < samplingRate; ++s) {
await sleep(getGrowTime(target) * 1000/samplingRate);
r.observe();
}
}
// We're adjusting slowly, since we have a lot of time.
if (r.average() > 0.99) targetRatio += 0.01;
else if (r.average() < 0.9) targetRatio -= 0.01;
else continue;
tprint(target + ": new ratio=" + targetRatio);
targetRatio = Math.max(0.01, Math.min(0.99, targetRatio));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment