# Zahleninvasion

Browser game for the lecture's reverse-engineering demo (`demo/rea-lab/crossover-concept.md`):
the merge rule of **2048.c** (MIT) crossed with the invader-wave shooter **nInvaders**
(GPL-2.0-or-later). Licence: **GPL-2.0-or-later** (`LICENSE`, credits in `NOTICE`).

## Rules

- Invaders carry numbers (2 with 90 %, 4 with 10 %, as 2048 spawns tiles) and march sideways,
  step down at the edges, drop bombs and get faster as they thin out (nInvaders).
- The cannon fires the number it has loaded; the next number is shown bottom right and can be
  swapped. Half the numbers are fresh 2s/4s, half copy an invader at the bottom of a column.
- Equal numbers merge (2048): 4 on 4 → 8, score + 8. Then equal neighbours in the same row are
  absorbed one after another (chain reaction). A different number bounces off.
- A wave is cleared when one invader reaches the target: 32, 64 … 2048 in wave 7 (win).
- The saucer gives 500 points and a joker ★ that merges with anything. Three lives, an extra life
  every 6000 points; landing invaders end the game.

Controls: keyboard ← → / A D move, Space / ↑ fire, ↓ / X / Shift swap, P / Esc pause. Phone:
hold a finger on the field — the cannon follows it and fires; tap "Nächste" to swap. German by
default, `EN` button or `?lang=en` for English. `?seed=<n>` replays the same round.

## Run, test, build

From the repository root (Node 26; `make` installs the pinned dev dependencies on first use):

```sh
make demo-test    # tsc --noEmit + node --test test/*.test.ts (rules, simulation, texts, music slot)
make demo-build   # static bundle in demo/crossover/dist/
make demo-serve   # build + serve dist/ on http://127.0.0.1:8080/
```

Inside `demo/crossover/`: `npm ci`, `npm test`, `npm run build`, `npm run serve -- --host 0.0.0.0`
(to try it on a phone in the same Wi-Fi).

`dist/` is a plain static site (≈ 30 KB of JavaScript): `index.html`, `assets/app.js`,
`styles.css`, `icon.svg`, `LICENSE.txt`, `NOTICE.txt` and `source/` — the complete source of
that build, linked from the in-game screen "Quellcode & Lizenzen". Any static web server can
host it.

## What is original, what is adapted

| Part | Origin |
| --- | --- |
| Merge rule, merge scoring, 90/10 spawn values | adapted from 2048.c (`slideArray`, `findTarget`, `addRandom`) |
| Formation march/step-down, speed formula, bomb timing, front-line shooter, one missile at a time, saucer chance, 500 points, lives | adapted from nInvaders (`aliensMove`, `handleTimer`, `aliensMissileMove`, `playerLaunchMissile`, `ufoShowUfo`, `doScoring`) |
| Crossover rules (numbered shots, row chains, wave targets, joker, swap), all graphics (drawn in code, `icon.svg`), all sounds (WebAudio synthesis), texts | original |

No C code was copied; the rules were re-implemented in TypeScript after the blind REA analysis
(`demo/rea-lab/evidence/`). No image, sound or music of any other game is used, and the name
"Space Invaders" is not used (trade mark check: `demo/rea-lab/crossover-concept.md` REA-X-003).

## Privacy

No cookies, no storage, no analytics, no fonts or scripts from other hosts, no network requests
(the Content-Security-Policy in `index.html` sets `connect-src 'none'`). The web server that
hosts the files still sees visitors' IP addresses.

## Music slot

`src/music.ts` holds one optional background track and is **empty**: the rights to the speaker's
music are not confirmed. To fill it, put the file in `public/music/`, add
`demo/crossover/public/music/<file>` to `demo/LICENSES.json` (SPDX id + https source) and set
`MUSIC_TRACK`. Tests and build refuse a track that is not listed there.

## Deploy (prepared, not run)

`cortex deploy` builds Docker Compose services from a Git repository named in `.cortex/app.yml`
(`cortex docs show concepts/apps`); it has no static-site shape, and this repository is private.
So no manifest is committed here. Steps once the coordinator has decided the public repository,
workspace and host:

1. Publish `demo/crossover/` as its own public repository (GPL source next to the game).
2. Add a two-stage `Dockerfile` (Node 26: `npm ci && npm run build`; then a static web server
   serving `dist/` on port 8080 with a `/health` file) and a `docker-compose.yml` with one
   service `web`, port published on loopback only.
3. `cortex init --workspace zahleninvasion --repo <owner>/zahleninvasion --service web --public --public-url https://<host>/ --health-url http://127.0.0.1:<port>/health`
4. `cortex deploy --dry-run`, `cortex verify`, `cortex deploy`, `cortex jobs watch <job-id>`.
