-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcasino_installer.js
38 lines (37 loc) · 8.75 KB
/
casino_installer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
export async function main(ns) {
const buffer = [];
if (ns.args[0] === "-w") {
// Wait until change
ns.tprintf("Waiting for new installer.js version...");
const deadline = Date.now() + 10000;
while (Date.now() < deadline) {
if (ns.ls("home", ns.args[1]).length) {
buffer.push("New version of installer.js received!");
break;
}
await ns.asleep(100);
}
if (Date.now() >= deadline) {
ns.tprintf("WARNING: No new installer.js, gave up after 10 seconds.");
return;
}
ns.mv("home", ns.args[1], "installer.js");
ns.run("installer.js");
return;
}
for (const [path, content] of Object.entries(data)) {
if (content === ns.read(path)) {
buffer.push(ns.sprintf("Skipped %s", path));
} else {
buffer.push(ns.sprintf("Writing %s...", path));
ns.write(path, content, "w");
}
}
ns.tprintf("%s", buffer.join("\n"));
}
var data = {
"coinflip.js":
'// This is a basically a direct copy from the source.\nclass BadRng {\n x;\n m = 1024;\n a = 341;\n c = 1;\n\n constructor() {\n this.x = 0;\n this.reset();\n }\n\n step() {\n this.x = (this.a * this.x + this.c) % this.m;\n }\n\n random() {\n this.step();\n return this.x / this.m;\n }\n\n reset() {\n this.x = 0;\n }\n}\n\n/** @param {NS} ns */\nexport async function main(ns) {\n ns.disableLog("ALL");\n if (!ns.args[0] || typeof ns.args[0] !== "string") {\n ns.tprintf("ERROR: Need initial sequence of flips to align sequence!");\n ns.tprintf(\'Specify flips with "run coinflip.js HTHTTH" etc.\');\n return;\n }\n const chr = [];\n const rng = new BadRng();\n for (let i = 0; i < 1024; ++i) {\n chr[i] = rng.random() < 0.5 ? "H" : "T";\n }\n let haystack = chr.join("");\n haystack += haystack.slice(0, ns.args[0].length - 1);\n const re = RegExp(ns.args[0], "g");\n let goodIdx, result;\n let numResults = 0;\n while ((result = re.exec(haystack))) {\n goodIdx = result.index;\n re.lastIndex = result.index + 1;\n numResults++;\n }\n if (numResults !== 1) {\n ns.tprintf(\n "ERROR: Needed 1 match, but found %d matching sequences",\n numResults\n );\n if (numResults === 0) {\n ns.tprintf("You made a mistake, or there is a program error.");\n } else {\n ns.tprintf("Try gathering more coin flips.");\n }\n return;\n }\n const cut = goodIdx + ns.args[0].length;\n haystack = haystack.slice(cut, 1024) + haystack.slice(0, cut);\n\n ns.clearLog();\n ns.tail();\n await ns.asleep(5);\n ns.resizeTail(389, 673);\n const output = [];\n let line = "";\n for (let i = 0; i < 256; ++i) {\n line += haystack.slice(i * 4, i * 4 + 4);\n if (i % 8 == 7) {\n output.push(line);\n if (i % 32 == 31) {\n output.push("");\n }\n line = "";\n continue;\n }\n line += " ";\n if (i % 4 == 3) {\n line += " ";\n }\n }\n ns.printf("%s", output.join("\\n"));\n}\n',
"roulette.js":
'// Directly copied from the source, with a few utility functions added\nexport class WHRNG {\n s1 = 0;\n s2 = 0;\n s3 = 0;\n\n constructor(seed) {\n const v = (seed / 1000) % 30000;\n this.s1 = v;\n this.s2 = v;\n this.s3 = v;\n }\n\n step() {\n this.s1 = (171 * this.s1) % 30269;\n this.s2 = (172 * this.s2) % 30307;\n this.s3 = (170 * this.s3) % 30323;\n }\n\n random() {\n this.step();\n return Math.floor(\n ((this.s1 / 30269.0 + this.s2 / 30307.0 + this.s3 / 30323.0) % 1.0) * 37\n );\n }\n\n clone() {\n const copy = new WHRNG(0);\n copy.s1 = this.s1;\n copy.s2 = this.s2;\n copy.s3 = this.s3;\n return copy;\n }\n\n matches(num, won) {\n while (true) {\n const val = this.random();\n // If value is in low range, it may have been skipped due to house\n // cheating.\n if (!won && val > 0 && val <= 18) continue;\n return val === num;\n }\n }\n}\n\n// Scrape the DOM and get the current roulette state\nfunction getLastPlay(h4s) {\n for (let i = 0; i < h4s.length; ++i) {\n const e = h4s[i];\n if (e.innerText !== "Iker Molina Casino") continue;\n if (h4s.length <= i + 2) {\n return null;\n }\n const match1 = /^(\\d\\d?)[RB]?$/.exec(h4s[i + 1].innerText);\n if (!match1) {\n return null;\n }\n const match2 = /^won|^lost|^playing$|^waiting$/.exec(h4s[i + 2].innerText);\n if (!match2) {\n return null;\n }\n return [match2[0], match1[1] | 0];\n }\n return null;\n}\n\n/** @param {NS} ns */\nexport async function main(ns) {\n ns.disableLog("ALL");\n\n ns.clearLog();\n ns.tail();\n ns.atExit(() => ns.closeTail());\n await ns.asleep(5);\n ns.resizeTail(389, 100);\n const doc = globalThis["document"];\n // State is both used as an enum, but also is the function to call to\n // process the current state and transition to the next one.\n let state = waiting;\n let timebase = 0;\n let seeds = [];\n let bet = 0;\n let nextseq = "";\n\n while (true) {\n ns.clearLog();\n state();\n await ns.asleep(100);\n }\n return;\n\n function next_bet() {\n const seed_copy = seeds[0].clone();\n bet = seeds[0].random();\n const acc = [];\n for (let i = 0; i < 8; ++i) {\n acc.push(seed_copy.random());\n }\n nextseq = acc.join(" ");\n }\n\n function ready_playing() {\n return ready();\n }\n\n function ready() {\n const play = getLastPlay(doc.getElementsByTagName("h4"));\n if (!play) {\n state = waiting;\n return state();\n }\n if (play[0] === "playing") {\n state = ready_playing;\n }\n if (play[0] === "waiting") {\n state = error;\n return state();\n }\n if ((play[0] === "won" || play[0] === "lost") && state == ready_playing) {\n state = ready;\n if (play[0] === "won") {\n if (play[1] !== bet) {\n state = error;\n return state();\n }\n } else {\n // Player might misclick on the wrong thing, we\'re still OK if we lose\n // but land on the predicted number.\n if (play[1] !== bet) {\n let nextnum;\n while ((nextnum = seeds[0].random()) === bet) {}\n if (play[1] !== nextnum) {\n state = error;\n return state();\n }\n }\n }\n next_bet();\n }\n const state_str = state === ready_playing ? "Playing" : "Ready";\n const bet_str = state === ready_playing ? "" : bet;\n ns.printf(\n `\nState: ${state_str}\nBet on: ${bet_str}\nGo big: \\x1b[5mYES\\x1b[0m\nBet sequence: ${nextseq}\n `.trim()\n );\n }\n\n function error() {\n const play = getLastPlay(doc.getElementsByTagName("h4"));\n if (!play) {\n state = waiting;\n return state();\n }\n // "ERROR" is written with an Omicron to avoid triggering the style regex\n ns.printf(\n `\nState: \\x1b[31mERRΟR\\x1b[0m\nCouldn\'t determine seed. Leave the Roulette table and come back.\nThis may be caused by betting incorrectly, or possibly by a logic bug.\n `.trim()\n );\n }\n\n function playing() {\n return study();\n }\n\n function study() {\n const play = getLastPlay(doc.getElementsByTagName("h4"));\n if (!play) {\n state = waiting;\n return state();\n }\n if (play[0] === "playing") {\n state = playing;\n }\n if (play[0] === "waiting" && state === waiting) {\n state = study;\n timebase = Date.now();\n seeds = [];\n for (let i = -1000; i <= 0; ++i) {\n seeds[i + 1000] = new WHRNG(timebase + i);\n }\n }\n if ((play[0] === "won" || play[0] === "lost") && state == playing) {\n state = study;\n const won = play[0] === "won";\n const num = play[1];\n // Always clear seeds if won doesn\'t line up with Low/High expectation,\n // that way we will fall into error state immediately.\n if ((won && num > 18) || (!won && num <= 18 && num > 0)) {\n seeds = [];\n }\n let pushIdx = 0;\n for (let i = 0; i < seeds.length; ++i) {\n const seed = seeds[i];\n seeds[pushIdx] = seed;\n if (seed.matches(num, won)) {\n pushIdx++;\n }\n }\n seeds.splice(pushIdx);\n if (!seeds.length) {\n state = error;\n return state();\n }\n if (seeds.length === 1) {\n state = ready;\n next_bet();\n return state();\n }\n }\n const state_str = state === playing ? "Playing" : "Finding state";\n ns.printf(\n `\nState: ${state_str}\nBet on: Low\nGo big: \\x1b[31mNO\\x1b[0m\nSeeds remaining: ${seeds.length}\n `.trim()\n );\n }\n\n function waiting() {\n const play = getLastPlay(doc.getElementsByTagName("h4"));\n if (play) {\n return study();\n }\n ns.printf(\n `\nState: Waiting to play roulette\nBet on: Low\nGo big: \\x1b[31mNO\\x1b[0m\n\\u180e\n `.trim()\n );\n }\n}\n',
};