diff --git a/README.md b/README.md index aeb340b..cb95f94 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ No dependencies. No build step. Vanilla JS + Canvas 2D. | **left click** | proton bolt — pushes charge in (**+**) | | **right click** | vacuum bolt — rips charge out (**−**) | | **space** (hold) | grab two things and squeeze them together | -| **Q** | heat — area shove, breaks bonds | +| **Q** | heat — area shove, breaks every bond in radius (40% go homolytic) | | **R** | retry | | **shift+A** | jump straight to CASCADE | @@ -34,6 +34,16 @@ slip through a membrane. and you're dry — the left hand always works, for free. - **Bonds can't form on their own.** Every pair has an activation barrier that parks them at arm's length. Your hands are the only way over it. +- **Saturation is the firebreak.** A radical takes an atom by grabbing an + *open valence slot*. Close every slot on something and the fire can't cross + it. A bonded pair of hydrogens is the cheapest wall in the game; a lone + carbon has four open sockets and is the best kindling. Wall a radical in + with nothing left to take and it goes out on its own in a few seconds — + which is the only way to kill the last one, since terminating needs a pair. +- **Nothing you build is permanent.** A detonation snaps a bond and *both* + halves come away radical. **Q breaks every bond it touches** and 40% of + those splits go homolytic — two new radicals. The button that unsticks a + jam is the button that opens a hole in your own wall. - **The membrane blocks anything charged and ignores anything neutral.** It has no guard and no keyhole. You get through by becoming the right kind of thing. - **Sour air keeps shoving protons onto everything.** Including you. @@ -55,6 +65,9 @@ the fire out and it clears. Let it burn and the field melts down. Density is the difficulty dial. Everything else follows from it. +The one number on screen is **terminations**, and back-to-back kills stack a +streak that lapses after three quiet seconds. No label, no praise, no record. + ## Layout ``` @@ -76,6 +89,7 @@ test/*.test.mjs headless sim tests — `node test/sim.test.mjs` node test/sim.test.mjs core physics, radicals, molly, regression node test/arena.test.mjs arena waves, win/lose, difficulty ramp node test/overload.test.mjs fuses, cascade, juice budget, perf +node test/firebreak.test.mjs saturation, homolysis, the score, arena both-ends ``` The sim is seedable (`seedRandom`), so runs are reproducible. Tests drive the @@ -90,10 +104,13 @@ The three knobs that matter, in order: two numbers. 3. **`SNAP_TRAUMA` / `HITSTOP`** — the payoff. -Two constants have non-obvious constraints, documented where they live: +Some constants have non-obvious constraints, documented where they live: `BARRIER_FORCE` must stay above `MAX_CHARGE_FORCE` (or atoms bond themselves -and the wrestle is skipped), and `RAD_SPLIT_R` must stay above the field's -natural packing distance (or outbreaks never bloom). +and the wrestle is skipped); `RAD_SPLIT_R` must stay above the field's natural +packing distance (or outbreaks never bloom); and `RAD_STARVE_TIME` / +`RAD_DRIFT_TIME` must stay far apart — a radical walled in by matter you built +should die fast, but one merely adrift in a sparse early arena must not, or +the outbreak evaporates unattended and the meltdown lose state disappears. ## Dev note diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100755 index 0000000..47f0247 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Deploy Molly Cool to monsterrobot.games/mollycool +# +# The lander lives on the botchat/games VPS and forum-nginx serves it from a +# bind mount of /home/humanjing/monsterrobot.games. That mount is READ-ONLY +# inside the container, so `docker cp` fails silently — always write the HOST +# path. (Same trap as beyondmorp V2.) +# +# monsterrobot.games -> /home/humanjing/monsterrobot.games (nginx root) +# monsterrobot.games/mollycool -> games/mollycool via a root symlink +# +# nginx needs no config change: the server block's `location /` try_files +# resolves the symlink, which is the same convention not-tonight uses. +set -euo pipefail + +HOST="humanjing@100.71.119.27" +LANDER="/home/humanjing/monsterrobot.games" +DEST="$LANDER/games/mollycool" +URL="https://monsterrobot.games/mollycool/" +SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +echo "==> shipping $SRC -> $HOST:$DEST" +# Static game only. serve.py is the dev server, test/ is the headless suite, +# and neither belongs on a public web root. +rsync -az --delete \ + --include='index.html' \ + --include='src/' --include='src/**' \ + --exclude='*' \ + "$SRC/" "$HOST:$DEST/" + +echo "==> ensuring the /mollycool route" +ssh "$HOST" "ln -sfn games/mollycool '$LANDER/mollycool' && ls -ld '$LANDER/mollycool'" + +echo "==> verifying live" +STAMP="$(date +%s)" +code=$(curl -s -o /dev/null -w '%{http_code}' "$URL?cb=$STAMP") +title=$(curl -s "$URL?cb=$STAMP" | grep -o '