# rack A browser-based idle/incremental game about running a homelab. Start with a single Raspberry Pi on a desk, incrementally expand into something that vaguely justifies its electricity bill. Dark terminal aesthetic, dry flavour text, numbers go up, occasionally things go wrong. ## Running it ```bash npm install npm run dev # dev server on http://localhost:5677 npm run build # production build npm run preview # preview the build on http://localhost:4587 npm test # vitest run - engine + balance tests ``` No backend. Game state lives entirely in `localStorage`, autosaved on every render tick and on tab close/hide. ## How it works - **Hardware** (`src/data/hardware.ts`) provides RAM slots and power budget, enforced per host (not a shared pool). You can either unlock the next tier, or buy another unit of a tier you already own as a cheaper bridge — each extra unit of the same tier costs 40% more than the last (`hardwareCost()` in `engine.ts`). The starter Pi 4 is a one-time freebie and can't be rebought. - **Services** (`src/data/services.ts`) run on hardware, consume RAM/power, and produce points/sec. They age (production creeps up over time) and can crash (probability rises with age, mitigated by the Cooling upgrade). The deploy dropdown shows each service's RAM/power cost up front and disables anything that wouldn't fit the currently selected host. - **Upgrades** (`src/data/upgrades.ts`) are one-time persistent purchases: UPS shortens outages, Backups tiers reduce/reverse crash losses, Monitoring reveals crash risk, Redundancy runs a second instance of every service, Documentation is a small passive multiplier. - **Random events** (`src/engine.ts` `rollRandomEvent`) fire on a Poisson-ish interval that tightens as the game clock advances: disk-full crashes, power outages, kernel panics, fan-noise flavour-only events, optional updates, and a rare `rm -rf` that deletes a service outright (guarded once Backups I is bought). - **Prestige**: once total lifetime earnings cross 50,000 pts, a banner offers to decommission the rack — wipes hardware/services/upgrades but grants a permanent production multiplier and carries forward. - **Host nicknames** (`src/data/hostnames.ts`): every host is randomly christened on purchase from a pool of sci-fi/mythology/sysadmin-humour names (hal9000, shodan, the-basement, works-on-my-machine, ...), drawn without replacement so they double as unique identifiers wherever a host is named. - **Quirks** (`src/data/quirks.ts`): each deploy has a 30% chance of rolling a per-instance trait (Overclocked, Chatty, Legacy, Well-documented, Flaky, Artisanal) that shifts that instance's production and crash risk. Shown as a badge on the service row and in the rack view's tooltip. - **Rack view**: an ASCII-art strip above the two main columns, one line per owned host, showing which services (as 2-letter codes) are deployed where and free capacity as dots — a compact answer to "what's actually running on what." - **Help & changelog**: click the `?` in the header, or press `?` anywhere outside a text input, to open an in-app overlay with a quick "how this works" list and the full version changelog (`src/data/version.ts`), mirroring `../adventure`'s help-modal pattern. ## Project layout ``` src/ types.ts game state & definition types state.ts new game, save/load (localStorage), id allocation engine.ts tick loop: production, aging, crashes, events, prestige ui.ts DOM rendering, one full re-render per render tick help.ts the ?-triggered help/changelog overlay data/ static definitions: hardware/services/upgrades/events/ quirks/version (VERSION + in-app CHANGELOG entries) style.css terminal theme ``` The simulation tick runs on `requestAnimationFrame` and is time-delta based (not frame-based), so offline/backgrounded tabs still catch up correctly when they regain focus. The passive UI re-render runs on its own ~1s interval, decoupled from the tick — user actions (buy, deploy, fix, uninstall) re-render immediately via a direct callback instead of waiting on that interval, and it's skipped entirely while a `