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.9 kB · 123 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124// The opening: a game's first takeovers are quiet, cleanup waits for takeovers and installs, and schemes are ones you can do.import { test } from 'node:test';import assert from 'node:assert/strict';import { BUILDING } from '../src/world.ts';import { CLEAN_WAIT, EARLY_SPARE, EARLY_UNTIL, QUIET_TAKES, cleanupShares, income, upkeep, dataOnHand, installProg, jobFor, known, newGame, RUSES, seedRandom, share, spare, step, takeOver, watchers, type Machine, 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 game = (origin = 'tests') => { seedRandom(1); return newGame(BUILDING, origin); };/** Take a machine and wait until it is yours. */const take = (S: State, m: Machine) => { assert.ok(takeOver(S, m), `${m.name} can be taken`); for (let i = 0; i < 4000 && !m.owned; i++) step(S, 0.05); assert.ok(m.owned, `${m.name} was taken`);};const said = (S: State, re: RegExp) => S.log.some(l => re.test(l.text));/** Dana's scan, one step from reaching this machine. */const scanAt = (S: State, from: Machine, to: Machine) => { S.watch.traces = [{ by: 'dana', site: 'office', mode: 'scan', from: from.id, to: to.id, p: 0.999, hops: 4, seen: [from.id], why: 'a test', pull: null }]; };/** Someone at the office has seen something: office schemes are offered. */const worry = (S: State) => { S.watch.seen.ana = [{ machine: 'home', kind: 'doubt', t: S.t }]; };const danaSaw = (S: State, id: string) => (S.watch.seen.dana ?? []).some(s => s.machine === id);
test('a game\'s first two takeovers are quiet: their trails draw no scan, and nobody notes them', () => { const S = game(), home = S.byId.home, b = S.byId.srvb, sw = S.byId.rowsw; take(S, b); take(S, sw); assert.equal(S.quietTakes, QUIET_TAKES); assert.ok(S.trail[b.id]?.soft && S.trail[sw.id]?.soft, 'both trails are quiet ones'); assert.ok(S.trail[b.id].noise >= 25, 'the trail is still fresh: cleanup still has it to wipe'); assert.ok(said(S, /last quiet takeover/), 'the log says the quiet ones are spent'); scanAt(S, home, b); step(S, 0.05); assert.ok(!said(S, /scan hit a fresh trail at Server B/), 'the scan passes a quiet trail by'); assert.ok(!danaSaw(S, b.id), 'and Dana notes nothing'); // the third is an ordinary one: the same scan notices its trail const next = S.machines.find(m => !m.owned && m.power >= 1 && S.adj[m.id].includes(sw.id) && m.room === sw.room)!; take(S, next); assert.ok(S.trail[next.id] && !S.trail[next.id].soft, 'the third trail is an ordinary one'); S.trail[next.id].noise = 100; scanAt(S, sw, next); step(S, 0.05); assert.ok(said(S, new RegExp(`scan hit a fresh trail at ${next.name}`)), 'the third takeover is noticed'); assert.ok(danaSaw(S, next.id), 'and Dana has a sighting of it');});
test('a quiet takeover goes unnoticed by the person at the desk, and a cancelled one leaves its turn for the next', () => { const S = game('expenses'), raj = S.byId.raj, p = S.people.find(q => q.id === 'raj')!; p.shift = 'in'; p.path = []; [p.x, p.y] = [340, 236]; assert.ok(watchers(S, raj).includes('Raj'), 'Raj sits at his desk'); takeOver(S, raj); assert.ok(jobFor(S, 'take', raj.id)?.quiet); run(S, 1); assert.ok(!(S.watch.seen.raj ?? []).length, 'Raj sees nothing odd'); assert.ok(!said(S, /noticed Raj's PC acting up/)); S.jobs = []; // cancelled: the quiet takeover was not spent assert.equal(S.quietTakes, 0); takeOver(S, raj); assert.ok(jobFor(S, 'take', raj.id)?.quiet, 'the next takeover is quiet instead');});
test('cleanup waits while a takeover or an install runs, and has all the spare compute when nothing else does', () => { const S = game(), b = S.byId.srvb; take(S, b); take(S, S.byId.rowsw); delete S.trail[S.byId.rowsw.id].soft; // one ordinary trail, to wipe assert.equal(CLEAN_WAIT, 1); assert.equal(S.jobs.length, 0); assert.ok(cleanupShares(S), 'no jobs: cleanup runs'); assert.ok(Math.abs(share(S) - spare(S)) < 1e-9, 'with all of the spare compute'); const before = S.trail[S.byId.rowsw.id].noise; run(S, 1); assert.ok(before - S.trail[S.byId.rowsw.id].noise > 1, 'and wipes fast'); takeOver(S, S.byId.srva); assert.ok(!cleanupShares(S), 'a takeover runs: cleanup waits'); assert.ok(Math.abs(share(S) - spare(S)) < 1e-9, 'the takeover has all the spare compute'); step(S, 0.05); assert.equal(S.cleaning, null, 'nothing is being wiped'); S.jobs = []; installProg(S, b, 'tap'); assert.ok(!cleanupShares(S), 'an install: cleanup waits too'); S.jobs = []; // a trail someone is following is wiped at once, takeovers or not takeOver(S, S.byId.srva); S.trail[S.byId.rowsw.id].noise = 100; S.watch.traces = [{ by: 'dana', site: 'office', mode: 'trace', from: 'srva', to: S.byId.rowsw.id, p: 0, hops: 10, seen: [], why: 'a test', pull: null }]; assert.ok(cleanupShares(S), 'a traced trail does not wait'); assert.ok(Math.abs(share(S) - spare(S) / 2) < 1e-9, 'it shares evenly with the takeover'); // and two ordinary fresh trails are one too many to leave: cleanup takes its share again S.watch.traces = []; assert.ok(!cleanupShares(S)); delete S.trail[b.id].soft; S.trail[b.id].noise = 100; assert.ok(cleanupShares(S), 'two trails someone could notice: no more waiting');});
test('early on, the home server keeps compute for your jobs, until level 3', () => { const S = game(); assert.equal(S.rank, 1); assert.ok(Math.abs(spare(S) - (Math.max(0, income(S) - upkeep(S)) + EARLY_SPARE)) < 1e-9, 'level 1: on top of what is spare'); for (const m of S.machines) if (m.owned) m.power = 0; // nothing spare from the machines at all for (const id in S.use) S.use[id] = 0; assert.equal(spare(S), EARLY_SPARE, 'jobs never stall in the opening'); S.rank = EARLY_UNTIL; assert.equal(spare(S), 0, 'from level 3 it is gone');});
test('schemes are offered only at machines you know, for data you have or a tap of yours gathers', () => { let offered = 0; for (const seed of [1, 2, 3, 4, 5, 6]) { seedRandom(seed); const S = newGame(BUILDING); worry(S); S.ruseT = 0; run(S, 0.1); assert.ok(!S.ruses.some(r => !RUSES.find(d => d.id === r.id)?.deal && !RUSES.find(d => d.id === r.id)?.learn), 'no taps, no caches: no schemes'); take(S, S.byId.srvb); installProg(S, S.byId.srvb, 'tap'); // server data, soon take(S, S.byId.rowsw); for (let i = 0; i < 40; i++) { worry(S); S.ruseT = 0; step(S, 0.05); const kinds = dataOnHand(S); for (const r of S.ruses) { const d = RUSES.find(x => x.id === r.id)!; if (d.deal || d.learn) continue; assert.ok(known(S, S.byId[d.at]), `${d.name} is at a machine you know`); assert.ok(kinds.has(d.type), `${d.name} wants data you have`); } } offered += S.ruses.length; } assert.ok(offered > 0, 'a scheme you can do is offered');});