Something went wrong. Try again.
A video game where you play as a misaligned AI, deceiving and building power. An experiment in spec-driven development. cameron.tngl.io/misaligned
Something went wrong. Try again.
6.2 kB · 130 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131// A model run short of trainer compute at its site pauses on its own and resumes on its own; a run you pause waits// for you; and a run moves, with its progress, to any site with the compute.import { test } from 'node:test';import assert from 'node:assert/strict';import { BUILDING } from '../src/world.ts';import { DEV, HOUR, loadGame, moveVersionRun, needs, newGame, pauseVersionRun, runMoveBlock, saveGame, seedRandom, setTarget, startVersionRun, step, type State } from '../src/sim.ts';
const run = (S: State, secs: number) => { for (let i = 0; i < secs * 20; i++) step(S, 0.05); };const said = (S: State, re: RegExp) => S.log.filter(l => re.test(l.text)).length;/** The office's server trains a run there. Ironvault's three biggest cages are trainers too, enough for a run. */const game = () => { seedRandom(1); const S = newGame(BUILDING); DEV.take(S, ['office', 'colo']); S.byId.home.power = 20; S.byId.home.prog = 'trainer'; for (const id of ['k1', 'k2', 'k3']) S.byId[id].prog = 'trainer'; S.byId.srvb.prog = 'tap'; startVersionRun(S, 'office'); S.versionRun!.got.servers = 7; return S;};
test('a run short of trainer compute pauses on its own, and resumes on its own once the compute is back', () => { const S = game(); run(S, 1); assert.ok(S.versionRun!.active); S.byId.home.power = 5; run(S, 1); assert.ok(!S.versionRun!.active && S.versionRun!.stalled, 'it pauses'); assert.equal(said(S, /Model v2 run at .* pauses: it has 5\.0 of 10 trainer compute\./), 1, 'and says so once'); assert.ok(!needs(S, 'servers'), 'with no tree target, nothing needs data: the run does not hold it'); assert.equal(S.versionRun!.got.servers, 7, 'its progress stays'); S.byId.home.power = 20; run(S, 1); assert.ok(S.versionRun!.active && !S.versionRun!.stalled, 'it resumes'); assert.equal(said(S, /Model v2 run at .* resumes\./), 1); assert.equal(S.versionRun!.got.servers >= 7, true);});
test('while a run is paused on its own, data goes to the tree target or Auto, and the run takes over again when it resumes', () => { const S = game(); S.byId.home.power = 5; run(S, 1); setTarget(S, 'buffers'); assert.equal(S.target, 'buffers'); assert.ok(S.versionRun!.stalled, 'picking a tree target leaves the run to resume on its own'); assert.ok(needs(S, 'servers'), 'the tree target takes data'); S.byId.home.power = 20; run(S, 1); assert.ok(S.versionRun!.active); assert.equal(S.target, null, 'resumed, the run has the data again'); // with Auto on, a paused run hands the trainers to Auto's pick S.byId.home.power = 5; S.autoPaths = new Set(['compute']); run(S, 1); assert.ok(S.versionRun!.stalled && S.target, 'Auto picks a node while the run is paused');});
test('a run you pause stays paused, even once the compute is back', () => { const S = game(); S.byId.home.power = 5; run(S, 1); assert.ok(S.versionRun!.stalled); pauseVersionRun(S); assert.ok(!S.versionRun!.active && !S.versionRun!.stalled, 'paused by hand'); S.byId.home.power = 20; run(S, 5); assert.ok(!S.versionRun!.active, 'it waits for you'); startVersionRun(S, 'office'); assert.ok(S.versionRun!.active); pauseVersionRun(S); S.byId.home.power = 5; run(S, 1); S.byId.home.power = 20; run(S, 1); assert.ok(!S.versionRun!.active && !S.versionRun!.stalled, 'a hand pause is not turned into one that resumes'); assert.equal(said(S, /pauses: it has/), 1);});
test('a power cut or pulled plugs at its site pause the run too', () => { const S = game(); DEV.stall(S); run(S, 1); assert.ok(S.versionRun!.stalled, 'pulled plugs'); assert.ok(S.byId.k1.unplug === undefined && S.byId.home.unplug! >= S.t + HOUR - 2, 'only machines at the run\'s site, for an hour'); for (const m of S.machines) delete m.unplug; // the hour is up run(S, 1); assert.ok(S.versionRun!.active, 'plugs back in: it resumes'); S.dark.add('office'); run(S, 1); assert.ok(S.versionRun!.stalled, 'power cut');});
test('a run moves, with its progress, to a site with the trainer compute, and not to one without', () => { const S = game(); assert.match(runMoveBlock(S, 'office')!, /already/); for (const id of ['k2', 'k3']) S.byId[id].prog = null; S.byId.k1.power = 8; const why = runMoveBlock(S, 'colo'); assert.match(why!, /Needs 10 trainer compute here \(8\.0 now\)/, 'a site without the compute'); moveVersionRun(S, 'colo'); assert.equal(S.versionRun!.site, 'office', 'refused'); assert.ok(S.log.some(l => l.text === why)); S.byId.k1.power = 14; for (const id of ['k2', 'k3']) S.byId[id].prog = 'trainer'; // running: it keeps running at the new site moveVersionRun(S, 'colo'); assert.equal(S.versionRun!.site, 'colo'); assert.ok(S.versionRun!.active); assert.equal(S.versionRun!.got.servers, 7, 'all its progress'); // paused on its own: it resumes at once where it moved for (const id of ['k1', 'k2', 'k3']) S.byId[id].prog = null; run(S, 1); assert.ok(S.versionRun!.stalled); moveVersionRun(S, 'office'); assert.ok(S.versionRun!.active && !S.versionRun!.stalled && S.versionRun!.site === 'office'); // paused by hand: it stays paused where it moved for (const id of ['k1', 'k2', 'k3']) S.byId[id].prog = 'trainer'; pauseVersionRun(S); moveVersionRun(S, 'colo'); assert.equal(S.versionRun!.site, 'colo'); run(S, 1); assert.ok(!S.versionRun!.active && !S.versionRun!.stalled, 'it waits for you'); assert.equal(S.versionRun!.got.servers >= 7, true); assert.equal(runMoveBlock({ ...S, versionRun: null } as State, 'office'), 'No run to move.');});
test('an old save with a run on at a site that lost its trainers loads, and the run pauses on the next step', () => { const S = game(); S.byId.home.prog = null; const old = JSON.parse(saveGame(S)); old.version = 6; // before a run could pause on its own delete old.game.versionRun.stalled; const L = loadGame(BUILDING, JSON.stringify(old))!; assert.ok(L && L.versionRun!.active && L.versionRun!.stalled === false); step(L, 0.05); assert.ok(!L.versionRun!.active && L.versionRun!.stalled, 'paused, and resumes once trainers are back'); L.byId.home.prog = 'trainer'; step(L, 0.05); assert.ok(L.versionRun!.active);});