diff --git a/.gitignore b/.gitignore index 0f72113..00273fa 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ /rules/ /public/*.css* /public/fonts/ + +# Arbeitsdateien für Claude Code (nur lokal) +/CLAUDE.md +/PLAN.md +/.claude/ diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 1d1b1fc..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,134 +0,0 @@ -# CLAUDE.md – Ninelands für Reddit (Devvit) - -Diese Datei lädt Claude Code in jeder Sitzung. Sie hält fest, **was** dieses Projekt ist, **wie** gearbeitet wird und -**wo** wir stehen. Stand: 2026-11-13, frisch angelegt aus der Devvit-Vorlage **Bare**. - -## Projekt - -- **Ninelands auf Reddit**: eine Basic-Version von [Ninelands](https://ninelands.stackteam.at) als Reddit-Spiel - (Devvit Web) für den Jam **Reddit Game of the Day 2026** – https://redditgameofday2026.devpost.com/ (Regeln: - `/rules`). Besitzer: Lukas Müllner (StackTeam). Mit dem Besitzer **Deutsch** sprechen. -- App-Name in `devvit.json`: `ninelands-game`. Ordner: `/data/Documents/projects/ninelands-game`. -- Repo: `git@git.stackteam.at:DieLustigeTierwesen/ninelands-reddit.git` (Forgejo, privat, angelegt 2026-11-13). -- **Umfang und Phasen stehen in `PLAN.md`** (vereinbart 2026-11-13): 9×9, nur Englisch, Karte des Tages mit - Bestenliste, Streak und Ergebniskarte, kein Menü/Footer. Spieldesign entscheidet weiterhin der Besitzer. - -## Woher Fragen beantwortet werden - -- **Das Hauptspiel** (Regeln, Engine, Gestaltung): Repo `DieLustigeTierwesen/DokuHive`, lokal - `/data/Documents/projects/hivedoku` – dort `README.md`, `hicedoku_plan.md` (Entscheidungen „as built“), - Leitfaden `guide/chapters/`. Bei Fragen zu Regeln oder Absicht **dort nachlesen**, nicht raten. -- **Allgemeiner Spiele-Blueprint** (Arbeitsweise, Fallstricke, Releases): Repo `DieLustigeTierwesen/game-blueprint`, - lokal `/data/Documents/projects/game-blueprint/AGENT.md`. Erkenntnisse, die für künftige Spiele gelten (z. B. „wie - baut man ein Devvit-Spiel“), dort nachtragen. - -## Grenzen zum Hauptspiel - -- **In `/data/Documents/projects/hivedoku` nichts ändern, committen oder pushen.** Jeder Push auf dessen `main` - deployt die Live-Seite; Releases dort (itch, Desktop, Android) laufen über `npm run release` und gehören nicht zu - diesem Projekt. -- Die Engine von Ninelands (`hivedoku/src/core`, reines TypeScript ohne DOM/Server, seeded RNG, `applyMove`, - `legalMoves`, Replay) darf übernommen werden – als **Kopie mit festem Stand** (z. B. Sync-Skript, das `src/core` von - einem Ninelands-Tag holt und den Tag notiert). Die Kopie hier **nicht von Hand ändern**; Änderungen an der Engine - gehören ins Hauptspiel. Achtung: `src/core` importiert JSON aus `../campaign/` und `../puzzles/` – nur mitnehmen, - was die Basic-Version braucht. -- **Lizenz: GPL-3.0** (entschieden 2026-11-13). Das Gerüst der Vorlage bleibt BSD-3-Clause (`LICENSE-TEMPLATE.md`). - Fremd-Assets mit Nachweis in `CREDITS.md`: Musik CC BY-SA 4.0 (Nennung im Spiel nötig), Sprites CC0. - -## Jam – Regeln (Stand 2026-11-13) - -Quelle: Übersichtsseite, vom Besitzer kopiert nach `rules/` (lokal, in `.gitignore`). Devpost blockt automatische -Abrufe (403). - -- **Abgabe: 9. Dezember 2026, 03:00 MEZ** (Jam 11. Nov. bis 8. Dez.). -- Aufgabe: ein **Daily Game** auf Devvit Web (Interactive Posts): kurzer täglicher Ablauf, frische Inhalte (tägliche - Challenges, Rätsel, Karten), leicht zu lernen, wiederspielbar. -- **Portierung erlaubt**: eigene Kategorie „Best Ported Daily Game“ (6.000 $). Das Original (die IP) muss man selbst - besitzen; bei der Einreichung **angeben, dass es eine Portierung ist**. -- Einreichung: **App-Listing** auf developers.reddit.com und **Demo-Post** (Link zu einem Subreddit mit öffentlichem - Post, in dem das Spiel läuft – danach wird bewertet, also selbsterklärend machen). Optional: Feedback-Umfrage. - **Kein Repo-Link, kein Video verlangt.** -- **Nicht veröffentlichen** (`devvit publish` / App Review): hochladen und playtesten reicht, die App darf privat - bleiben. -- Bewertung: Delightful UX, Polish (nahe an Veröffentlichungsqualität, Devvit-Regeln einhalten), „Reddit-y“ - (Gemeinschaft, eigene Identität), Daily Content. -- Pluspunkte: schneller Einstieg ohne Textwände, klare eigene Optik, Startbildschirm mit Name/Datum/Statistik, - **Ergebniskarte** am Ende (Statistik, Streak, Kommentar, Abo-Hinweis), **Devvit Journeys** für Ereignisse, - Bestenliste und Streaks; Reddit-Funktionen wie Flair, Kommentare, Snoovatare. -- Nicht gewollt: offensichtliche „KI-Optik“ (KI-Nutzung ist erlaubt), Reddit-Klischees als Thema, bekannte - Spielideen ohne eigenen Dreh, Spiele, die nicht auf Reddit laufen. -- **Offizielle Regeln** (`rules/…rules.md`, vom Besitzer kopiert): - - Einreichung bis Di 8. Dez. 18:00 PT (= 9. Dez. 03:00 MEZ); **Bewertung 9.–16. Dez.**, Gewinner ca. 17. Dez. Das - Spiel muss während der Bewertung laufen (Tagesposts, Bestenliste) – in dieser Zeit keine riskanten Uploads. - - **Bestehende Projekte** zählen nur, wenn sie ab dem 11. Nov. „significantly updated“ wurden, und das muss in der - Einreichung erklärt werden. Die Devvit-Portierung ist eine neue Codebasis – das in der Beschreibung ausführen. - - Pflicht: Textbeschreibung, App-Link `developers.reddit.com/apps/ninelands-game`, **ausführliche `README.md` im - Repo-Wurzelverzeichnis** (Spiel und Anleitung, Englisch), Demo-Post in einem **öffentlichen Subreddit mit - < 200 Mitgliedern** (oder App `dr-admin-approve` installieren). - - Optional: Video (< 1 min, öffentlich auf YouTube/Vimeo), öffentliches Repo, Feedback-Umfrage. Die Jury darf auch nur - nach Beschreibung, Bildern und Video urteilen → Video und Screenshots lohnen sich. - - Nur Material mit passenden Rechten (Sprites CC0, Musik CC BY-SA mit Nennung – erfüllt). Ein Projekt gewinnt höchstens - einen Preis. Teilnahme als Einzelperson oder Organisation (StackTeam) – entscheidet der Besitzer bei der Anmeldung. - - Gleichstand: entscheidet zuerst „Delightful UX“. - -## Technik (Vorlage Bare) - -- Devvit Web: Client `src/client/` (Splash `splash.ts` → `public/splash.html`, Spiel `game.ts` → `public/game.html`), - Server `src/server/` (`index.ts`, `server.ts`, Speicher `db.ts`), gemeinsame Typen `src/shared/api.ts`. - Gebaut mit **esbuild** nach `public/` und `dist/server/`. -- Werkzeuge der Vorlage beibehalten: **Biome** (Lint + Format, `npm run lint` / `npm run format`) statt Prettier, - Tests mit `node --test` (`*.test.ts`), Typprüfung `tsc --build`. -- Befehle: `npm run test` (Typen, Lint, Unit-Tests, Build – **das ist die Prüfung vor jedem Merge**), - `npm run playtest [r/sub]` (baut, lädt hoch, installiert im Test-Subreddit), `npm run publish` (lädt hoch und - stellt einen **Review-Antrag** bei Reddit – nur nach Rückfrage beim Besitzer). -- Node 24 über nvm (global ist 20): Befehle mit `source ~/.nvm/nvm.sh && nvm use 24 >/dev/null &&` beginnen. -- Kein eigener Server, keine eigene Datenbank, keine eigenen Konten: Spieler sind Reddit-Nutzer, gespeichert wird - über Devvit (Redis). Externe Netzaufrufe sind bei Devvit eingeschränkt – vor jeder Idee, die das Ninelands-Backend - anspricht, in der Devvit-Doku prüfen (https://developers.reddit.com/docs). -- Aktuelle Devvit-Doku lesen, bevor etwas gebaut wird – die Plattform ändert sich schnell; nicht aus dem Gedächtnis - arbeiten. -- **Engine-Kopie:** `scripts/sync-engine.sh ` (Stand in `src/engine/VERSION`), holt auch Sprites und - Musik nach `public/`. `src/engine/` nie von Hand ändern; Biome ignoriert es. Unit-Tests werden vor `node --test` mit - esbuild gebündelt (die Engine importiert ohne Dateiendung). -- `.github/workflows/ci.yaml` stammt aus der Vorlage. Forgejo führt `.github/workflows` aus, wenn es kein - `.forgejo/workflows` gibt; `runs-on: ubuntu-latest` ist der gemeinsame Hetzner-Runner (für `npm run test` reicht er). - -## Arbeitsweise - -- **Ein Branch pro Änderung** (`feat/…`, `fix/…`, `chore/…`), auch für Doku. -- **Merge nur, wenn `npm run test` mit Exit-Code 0 endet.** Ausgabe in eine Log-Datei, dann `$?` prüfen – nie mit - `| grep … &&` oder `;` verketten. Muster: `npm run test > log 2>&1; code=$?; if [ $code -eq 0 ]; then … merge; fi`. -- `git merge --no-ff`, Branch löschen, `main` pushen (sobald ein Remote existiert), im Bericht erwähnen. -- Conventional Commits; Ende jeder Nachricht: `Co-Authored-By: Claude Opus 5.5 `. -- Code-Kommentare auf Deutsch. -- Größere Entscheidungen und Fallstricke in `PLAN.md` (Abschnitt „as built“, Deutsch) festhalten; am Ende eines - Schritts Erkenntnisse von sich aus nennen. -- Innerhalb eines vereinbarten Plans selbstständig weitermachen; anhalten bei Spieldesign, Geschmack, externen Konten, - Geld, Lizenz und allem, was öffentlich wird (`devvit publish`, Posts in echten Subreddits). -- **Kein Geld ausgeben.** - -## Geheimnisse – nie ausgeben, nie committen - -- Devvit-Token `~/.devvit/token` (Reddit-Anmeldung des Besitzers): nie lesen, ausgeben oder kopieren; die - `devvit`-Befehle nutzen ihn selbst. -- Forgejo-Token `~/.config/forgejo-token`: nur innerhalb von Befehlen lesen. -- Nutzer sollen nie Schlüssel in den Chat kopieren. - -## Stand (2026-11-13) - -- Vorlage **Bare** committet, Repo `DieLustigeTierwesen/ninelands-reddit` angelegt, `main` gepusht. Lint der Vorlage - repariert (Biome-Schema 2.6.4 → 2.6.6, `package.json` ohne Schlusszeilenumbruch); `npm run test` lokal grün. -- CI: Die `parallel:`-Stufe der Vorlage ersetzt (Forgejo-Runner v13: „Invalid run/uses syntax“), Schritte laufen - nacheinander. -- `PLAN.md` angelegt, Lizenz auf GPL-3.0 umgestellt. -- Phase 0 erledigt (Engine `v0.11.0`, Tageskarte `src/shared/daily.ts`, Assets). Nächster Schritt: Phase 1. -- Texte und Checkliste für Devpost und Portal: `docs/submission.md` (neue Texte dort eintragen). -- Subreddit `r/Ninelands` angelegt (2026-11-13), seit 2026-11-14 **Public**. Bis zum Go-live postet der Besitzer dort nichts. - **App erst zum Go-live installieren:** Die Installation legt sofort den Tagespost an (`onAppInstall`), danach - postet der Scheduler täglich um 00:01 UTC. -- **Go-live: 1. Dez. 2026** (Besitzer, 2026-11-14). Ablauf: App in r/Ninelands installieren (→ „Ninelands #1“), - Subreddit auf **Public** stellen (Devpost verlangt einen öffentlichen Post-Link), die drei Community-Posts, dann - Post-Link und Subreddit in die Einreichung. Alte Tagesposts zeigen ihren eigenen Tag → in der Beschreibung auch den - Subreddit verlinken (neuester Post oben). Weniger als 200 Mitglieder halten, sonst `dr-admin-approve` installieren. - -Diese Datei bei neuen Arbeitsregeln und Entscheidungen aktualisieren. diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index 112b461..0000000 --- a/PLAN.md +++ /dev/null @@ -1,355 +0,0 @@ -# PLAN – Ninelands für Reddit - -Basic-Version von Ninelands als tägliches Reddit-Spiel (Devvit Web) für den Jam **Reddit Game of the Day 2026**. -Abgabe: **9. Dezember 2026, 03:00 MEZ**. Eingereicht wird in „Best Daily Game“ und als Portierung in -„Best Ported Daily Game“. - -## Entscheidungen des Besitzers (2026-11-13) - -- **Nur 9×9**, **nur Englisch**. -- **Das Spiel steht im Vordergrund:** kein Menü, kein Footer, kein Feedback-Knopf. Ausnahmen sind ein schlanker - Startbildschirm (Name, Datum, eigene Statistik/Streak) und eine Ergebniskarte am Ende. Beides ist Teil des Spiels - und wird von der Jury ausdrücklich verlangt. -- **Kern: Karte des Tages mit Bestenliste.** PvP höchstens später als KI-Duell-Bonus (wenig Spieler gleichzeitig - online, viel Aufwand, kostet Polish). -- **Freischaltbares:** Themen und Banner aus dem Hauptspiel, gekoppelt an Streak und Sterne statt an Herrscher-Stufen. -- **Lizenz GPL-3.0** (Besitzer ist alleiniger Urheber der Engine). Das Gerüst der Devvit-Vorlage bleibt BSD-3-Clause - (`LICENSE-TEMPLATE.md`). -- **Musik ja**, mit einer kleinen Credits-Stelle (CC BY-SA 4.0 verlangt die Nennung). Alle Sprites stammen aus - „Mini World Sprites“ (CC0). Nachweise in `CREDITS.md`. - -## Phase 0 – Grundlagen - -1. Sync-Skript `scripts/sync-engine.sh`: holt `src/core` von einem festen Ninelands-Tag (Vorschlag: `v0.11.0`) nach - `src/engine/` und notiert den Tag in `src/engine/VERSION`. Die Kopie wird nie von Hand geändert. Die Campaign- und - Puzzle-JSONs nur mitnehmen, wenn der Import sonst bricht. Besser: prüfen, ob esbuild sie per Tree-Shaking weglässt. -2. Prüfen, ob die Engine mit TypeScript 7, Biome und esbuild der Vorlage baut. `src/engine/` von Biome ausnehmen, - denn fremde Formatierung soll nicht zu Änderungen an der Kopie zwingen. -3. Assets übernehmen: die Tile-Sprites, die das 9×9-Spiel braucht, sowie die Themen und Banner für Phase 2. Bei der - Musik auf die App-Größe achten: das Hauptspiel hat 21 MB (6 Titel, Opus + MP3). Vermutlich nur 1–2 Titel und nur - ein Format. Das Größenlimit von Devvit vorher in der Doku prüfen. - -## Phase 1 – Kern (Tagesrätsel) - -1. **Tagespost:** Ein Scheduler-Job legt jeden Tag um 00:00 UTC einen neuen Post an („Ninelands #N – “). - Jeder Tag bekommt so eigene Kommentare, in denen über die Karte diskutiert wird. -2. **Tageskarte:** Seed aus dem Datum, 9×9, mit den drei Sternzielen wie im Hauptspiel (★ Gold, ★ Länder, ★ beides), - berechnet aus Bot-Partien auf derselben Karte. -3. **Spiel:** Brett im Post, Ziele-Zeile live, Rückgängig wie im Hauptspiel. -4. **Abgabe und Prüfung:** Der Client schickt die Züge, der Server spielt sie mit derselben Engine nach und speichert - nur selbst berechnetes Gold (wie im Hauptspiel). So kann niemand die Bestenliste mit erfundenen Werten fälschen. -5. **Bestenliste** je Tag in Redis (Sorted Set): Sortierung wie bei den Ninelands-Ranglisten, also **Sterne ↓, - Gold ↓, früher abgegeben zuerst**. Gewertet wird nur der erste Versuch; danach darf man weiterspielen, das zählt - aber nicht. _Entscheidung Besitzer._ -6. **Streak:** aufeinanderfolgende Tage mit gespielter Tageskarte. -7. **Startbildschirm:** Name, Nummer und Datum, eigene Streak, „Play“. -8. **Ergebniskarte:** Sterne, Gold, Länder, Platz, Streak, Teilen-Zeile (`Ninelands #N ★★☆ · 9/9 lands · 85 gold`) - und der Anstoß, sie in die Kommentare zu posten. -9. **Devvit Journeys:** Ereignisse für Start, Abgabe und Streak (Jury-Kriterium). -10. **Credits:** ein kleines „ⓘ“ auf dem Startbildschirm mit Musik- und Sprite-Nachweis. -11. **Erstes Spielen:** Hinweise im Zusammenhang statt Textwand (Jury-Kriterium „Intuitive Onboarding“). - -## Phase 2 – Wiederkommen - -1. Themen freischalten über die Streak (z. B. 3 / 7 / 14 Tage), Banner über gesammelte Sterne. Die Auswahl erfolgt - direkt auf dem Startbildschirm, ohne eigenes Menü. -2. **User-Flair** im Subreddit (z. B. „🔥 12-day streak“, Tagessieger). Vorher in der Doku prüfen, wie Devvit Web - Flair setzt. -3. Winter- bzw. Winterfest-Thema passend zur Bewertung im Dezember. - -## Phase 3 – optional - -- KI-Duell gegen den Computer als Bonus nach dem Tagesrätsel. Nur, wenn Phase 1 und 2 poliert sind. - -## Phase 4 – Einreichung (alles nach Rückfrage beim Besitzer) - -- Eigenes Subreddit für das Spiel; öffentlicher Demo-Post (der einzige öffentliche Schritt). -- App-Listing auf developers.reddit.com (Upload, **kein** `devvit publish`). -- Auf Devpost angeben, dass es eine Portierung ist, und erklären, was seit dem 11. Nov. neu entstanden ist (Pflicht - für bestehende Projekte); optional die Feedback-Umfrage. -- **`README.md` (Englisch) ersetzen:** Spiel und Spielanleitung – Pflicht laut offiziellen Regeln. -- Subreddit öffentlich (oder „Restricted“: lesbar für alle) und < 200 Mitglieder. -- Video (< 1 min) und Screenshots für die Beschreibung – die Jury darf allein danach urteilen. -- **Bewertung 9.–16. Dez.:** Tagesposts müssen weiterlaufen; in der Zeit keine riskanten Uploads. - -## Grober Zeitplan - -| Bis | Was | -| :------- | :--------------------------------- | -| 16. Nov. | Phase 0 | -| 29. Nov. | Phase 1 spielbar im Test-Subreddit | -| 4. Dez. | Phase 2, Polish | -| 8. Dez. | Puffer, Demo-Post, Einreichung | - -## Weitere Entscheidungen (2026-11-13) - -- **Eigener Seed für Reddit** (Präfix ungleich `hivedoku-daily-`): Sonst wäre die Tageskarte dieselbe wie auf der - Website, und „Watch the best game“ dort verriete die Lösung. -- **Nur der erste Versuch zählt** für die Bestenliste; Weiterspielen ist erlaubt, wird aber nicht gewertet. -- **Subreddit:** Entwickelt wird im Test-Subreddit, das `devvit playtest` selbst anlegt. `r/Ninelands` (falls frei) - legt der Besitzer erst für den Demo-Post an. Hochgeladene, nicht veröffentlichte Apps lassen sich nur in Subreddits - mit < 200 Mitgliedern installieren – für die Jam-Demo reicht das. - -## Devvit-Vorgaben, die das Design betreffen (Doku, 2026-11-13) - -- **Ton:** erst nach einer Nutzeraktion abspielen, **Stumm-Knopf** im Spiel, bei `visibilitychange` stumm schalten. -- **Gesten:** nur Tippen/Klicken; kein Scroll-Abfangen, kein Zoom/Pan – man muss am Post vorbeiscrollen können. -- **Expanded Mode** nur auf ausdrückliche Aktion (Knopf), nie automatisch; die Standardansicht bleibt im Post. -- **Responsive**, die meisten Spieler sind mobil. -- **`localStorage` wird bei jedem App-Update gelöscht** → Streak, Freischaltungen, gewähltes Thema, Stumm-Schalter in - Redis speichern. -- Server: serverlos, max. 30 s je Anfrage, 4 MB Anfrage, 10 MB Antwort; kein `fs`. Client: keine externen Abrufe. - -## Risiken - -- Devvit ändert sich schnell: Scheduler, Redis, Flair und Journeys vor dem Bauen in der aktuellen Doku lesen - (`https://developers.reddit.com/docs/llms.txt`, die Seiten gibt es als `.md`, per `curl` abrufbar). -- App-Größe durch die Musik. -- Die Engine kennt viele Modi (Kampagne, Puzzles, Duell). Unnötiges darf nicht ins Bundle wandern - (Metafile von esbuild prüfen). - -## As built - -### 2026-11-13 – Repo, CI, Lizenz - -- Repo `DieLustigeTierwesen/ninelands-reddit` (privat) angelegt. -- CI: Die `parallel:`-Stufe der Vorlage kennt der Forgejo-Runner nicht („Invalid run/uses syntax“). Ersetzt durch - einzelne Schritte nacheinander. -- Lint der Vorlage war rot: Das Biome-Schema 2.6.4 passte nicht zur installierten CLI 2.6.6, und `package.json` hatte - keinen Zeilenumbruch am Ende. -- Lizenz auf GPL-3.0 umgestellt (`LICENSE` aus dem Hauptspiel, `package.json` → `GPL-3.0-only`). Der BSD-Text der - Vorlage bleibt als `LICENSE-TEMPLATE.md`, denn BSD verlangt, dass der Hinweis erhalten bleibt. `CREDITS.md` angelegt. - -### 2026-11-13 – Phase 0: Engine und Assets - -- `scripts/sync-engine.sh ` holt per `git archive` (nur lesend) `src/core`, `src/campaign`, `src/puzzles` nach - `src/engine/` und Sprites (Tiles, Brett-Themen) sowie Musik nach `public/`. Stand in `src/engine/VERSION` - (`v0.11.0`). Avatare nicht übernommen (braucht die Basic-Version nicht). -- `src/engine/` hat eine eigene `tsconfig.json` (strict, `noUncheckedIndexedAccess` aus, `resolveJsonModule`, - `lib` WebWorker für `crypto`/`structuredClone`). **`erasableSyntaxOnly` aus**, weil `duel.ts` Parameter-Properties - nutzt. Biome ignoriert `src/engine/`. -- **Fallstrick:** Die Engine importiert ohne Dateiendung (`'./Board'`). `node --experimental-strip-types` kann das - nicht auflösen. Deshalb bündelt `test:unit` die Tests jetzt erst mit esbuild nach `dist/test/` (`--packages=external`, - damit `@devvit/web` als ein Modul geladen wird und die Redis-Attrappen im Server-Test greifen), dann `node --test`. - Der Glob `src/**/*.test.ts` läuft unter `sh` ohne globstar = nur eine Ebene tief (`src/*/…`). -- `src/shared/daily.ts`: Tageskarte mit Seed `fnv1a('ninelands-reddit-daily-' + UTC-Tag)`, Nummer ab 2026-11-13 (#1), - Welt-Regel wie im Hauptspiel `rolledTrait(seed)`. **Präfix und `FIRST_DAY` nie ändern** (sonst andere Karten und - Nummern rückwirkend); Schutztests in `daily.test.ts`. -- Ziele (`computeGoals`, 7 Bot-Partien) plus Nachspielen dauern in Node ≈ 80 ms – unproblematisch für den Server. - Für die Prüfung **nicht** `verifyProgress` aus `progress.ts` nehmen: das zieht Kampagnen-JSONs ins Bundle und kennt - nur die Website-Tageskarte. Eigene Prüfung aus `applyMoves` + `computeGoals` + `starsOf`. -- Musik: ein Titel („Peaceful Village“), vom Sync-Skript aus der Opus-Datei des Hauptspiels als **MP3 64 kbit/s** - kodiert (1,0 statt 1,6 MB; Besitzer hat Varianten verglichen). MP3 läuft auch in iOS-Webviews. Ein Größenlimit für - Webview-Assets steht nicht in der Doku; weitere Titel in `scripts/sync-engine.sh` (`musik=(…)`) ergänzen. -- Biome: Ordner-Ausschluss als `"!src/engine"` schreiben – `"!src/engine/"` mit Schrägstrich griff nicht. - -### 2026-11-13 – Phase 1, Server - -- Endpunkte: `GET api/day` (Karte, Ziele, eigenes Ergebnis, Streak, Top 10, Spielerzahl) und `POST api/submit` - (Züge). Der Tag eines Posts steht in den Post-Daten (`postData.day`); ohne Daten gilt heute (UTC). -- **Prüfung:** Der Server spielt die Züge nach (`src/server/verify.ts`) und wertet nur **fertige** Partien (kein Zug - mehr möglich) – sonst ließe sich früh abbrechen. Gold, Länder und Sterne rechnet er selbst; der Client schickt nur - Züge. -- **Erster Versuch:** `hSetNX` auf `results:{day}` ist atomar; ein zweiter Versuch liefert `counted: false` und das - alte Ergebnis. -- **Bestenliste:** Sorted Set mit `Sterne·100 000 + Gold + 1/(seq+1)` – `seq` ist die laufende Nummer der Abgabe, - dadurch gewinnt bei Gleichstand die frühere, und die Werte sind eindeutig (Platz = `zCard − zRank`). -- **Streak** zählt nur, wenn der Post von heute gespielt wird; alte Posts kann man nachspielen (Bestenliste ja, - Streak nein). Angezeigt wird 0, wenn der letzte Tag älter als gestern ist. -- **Tagespost:** Scheduler `1 0 * * *` (00:01 UTC), dazu bei Installation und per Moderator-Menü. Hash `posts` - verhindert doppelte Posts. -- `devvit.json`: **`redis: true` muss gesetzt sein** (Standard ist `false`). Startbildschirm inline (`height: tall`), - Spiel als Entry `game` (Expanded). -- **Fallstrick:** `hMGet` ist laut Doku nicht für jede App freigeschaltet → einzelne `hGet`. -- Offen: Löschungen von Nutzern respektieren (Devvit-Regel) – Ergebnis und Name bei Kontolöschung entfernen. - -### 2026-11-13 – Phase 1, Client - -- **Brett aus dem Hauptspiel:** `BoardView.ts`, `atlas.ts`, `geometry.ts`, `board.css` und `env.ts` kommen per - Sync-Skript nach `src/engine/app/` (unverändert). `import.meta.env` und `__API_ORIGIN__` setzt esbuild per - `--define`; Typen dafür in `src/types/ninelands-env.d.ts`. Das Symbol `i-warn` (Warn-Badge der Vorschau) steht - direkt in `public/game.html`. -- **Startbildschirm** (`splash`, inline, `height: tall`): Name, Nummer, Datum, Mini-Karte, Gesetz, Ziele bzw. eigenes - Ergebnis, Spielen-Knopf, Streak, Top 3, „i“ mit Credits. -- **Spiel** (`game`, Expanded): Ziele-Leiste mit Sternen live, Gesetz (antippbar → Regeln), Brett, Hinweiszeile, - Figuren mit Kosten, Rückgängig (1 Schritt wie im Hauptspiel). Touch: erster Tipp zeigt Vorschau und Grund, zweiter - setzt. Nach der Burg automatisch Bauer. Ende → Abgabe → Ergebniskarte (Sterne, Gold, Länder, Platz, Streak, Top 10, - Teilen über `showShareSheet`, „Play again“ als Übung, „Done“ schließt den Expanded Mode). -- Gäste spielen ohne Wertung (Hinweis + `showLoginPrompt`); wer schon gespielt hat, sieht „Practice“. -- **Layout-Fallstrick:** Brett und Hinweis liegen in einem Container mit `container-type: size`; das Brett ist - `min(100cqw, 100cqh − 3.2em)` breit. Mit `flex: 1` + `aspect-ratio` allein blieb viel Leerraum zwischen Brett und - Hinweis. -- Schrift Pixelify Sans über `@fontsource/pixelify-sans` (OFL), von esbuild nach `public/fonts/` kopiert – externe - Schrift-Abrufe sind im Devvit-Client gesperrt. -- Musik: Start beim ersten Tippen, Stumm-Knopf, Pause bei `visibilitychange`. Stumm-Schalter vorerst in - `localStorage` (geht bei App-Updates verloren – vertretbar; Redis erst, wenn es stört). -- Lokale Vorschau ohne Reddit: kleine Server-Attrappe + Playwright-Screenshots (Skripte im Scratchpad, nicht im Repo). -- Biome ergänzt bei `--write` Dateiendungen an Engine-Importen (`'../engine/core/Game.ts'`) – ist so gewollt. - -### 2026-11-13 – Spielende und Ziel-Hervorhebung (Rückmeldung Besitzer nach erstem Playtest) - -- Problem: Man wusste nicht, wie man die Partie beendet – sie endete nur, wenn kein Zug mehr möglich war (Bauern - sind gratis, das dauert). -- **„End game“-Knopf** wie im Hauptspiel, mit zweitem Tipp zur Bestätigung (3 s). Der Server wertet jetzt auch früh - beendete Partien; abgelehnt wird nur eine leere (`EMPTY`). Kein Vorteil durch frühes Aufhören: Gold zählt vor der - Abgabezeit. -- **Kein automatisches Ende bei 3 Sternen** – unter Spielern mit 3 Sternen entscheidet das Gold; stattdessen Hinweis - „All 3 stars! Keep going for more gold – or end the game.“ -- Erreichte Ziele werden **golden** hervorgehoben (wie `goal-chip.done` im Hauptspiel). -- `devvit playtest` trägt `dev.subreddit` (`ninelands_game_dev`) in `devvit.json` ein. -- Rückmeldung Besitzer: Bei „One farm per land“ bleiben am Ende oft nur Ritter, die Gold kosten. Darum prüft der - Client nach jedem Zug, ob noch ein Zug Gold bringt (`gainLeft`, alle gültigen Züge per `preview`, < 1 ms). Wenn - nicht: Hinweis „None of your remaining moves earns gold – end the game to keep your gold.“ und „End game“ leuchtet golden. -- Fix: Im goldenen Ziel-Feld waren Stern und „/ Ziel“ ebenfalls gold (Gold auf Gold, unsichtbar) – beim Umsortieren - der CSS-Regeln für Biome (`noDescendingSpecificity`) waren die Deklarationen vertauscht. Jetzt alles in Tinte. - -### 2026-11-13 – Katalog-Assets (Entwurf) - -- `npm run capture` (`scripts/capture/`): baut eine Aufnahmeseite nur mit dem Brett, ein Bot (`smart`) spielt eine - Partie mit Vorschau, Playwright nimmt auf, ffmpeg macht daraus `dist/marketing/featuring.mp4` (1500×1000, 30 fps, - ≈ 11 s, ≈ 1,7 MB), `fallback.gif` (1000×667, ≤ 2 MB), `fallback.png` (1500×1000) und `tile.png` (560×256 mit - Schriftzug). Maße laut Developer Portal; Größen werden bei Bedarf automatisch heruntergeregelt. -- **Karte vom 2026-10-10** (vor dem ersten Spieltag): läuft nie als Tageskarte – eine künftige Tageskarte im Video - wäre ein Spoiler. Der Bot holt dort in 26 Zügen alle 3 Sterne. -- Playwright als devDependency in der Version des Hauptspiels (1.55.1), damit die vorhandenen Browser im Cache passen. -- Vor der Einreichung neu aufnehmen (z. B. mit Winter-Thema aus Phase 2). - -### 2026-11-13 – Devvit Journeys - -- `@devvit/analytics` 0.15.3 (passend zu Devvit 0.15.3), `permissions.journeys: true`. -- **Fallstrick:** Der fertige `createTelemetryRouter` ist ein Express-Router; unser Server hat kein Framework. Darum - bauen `src/server/journeys.ts` die fünf Endpunkte (`api/telemetry/journey/start|progress|interaction|end|app-ready`) - nach und reichen an `telemetry` aus `@devvit/analytics/server/reddit` weiter. Das Paket zieht Express als Peer mit - ins Server-Bundle (nur importiert, nicht benutzt). -- Ereignisse: `App.Ready` in Start- und Spielansicht; `Journey.Start` beim **Setzen der Burg** (bewusste Aktion, nicht - das Öffnen – Vorgabe der Doku); `Journey.Progress` je erreichtem Stern (1/3, 2/3, 3/3); `Journey.End` mit - `win` = mindestens ein Stern, `score` = Gold. Alles „feuern und vergessen“. -- Reddit leitet Ereignisse erst nach Freischaltung in der App-Review weiter (Allowlist); bis dahin „dropped“. -- Katalog (Besitzer): App-Icon aus dem Hauptspiel, Texte wie vorgeschlagen, Kategorie Game → Strategy. - -### 2026-11-13 – README und Löschregel - -- **`README.md`** (Pflicht laut Jam und Devvit-Regeln, Großschreibung): Überblick, Spielanleitung, Sterne/Bestenliste/ - Streak, Einrichtung für Moderatoren, Datenschutz, Entwicklung, Credits, Lizenz. Die Vorlage hieß `readme.md`. -- **Devvit-Regel „Enable and respect user deletions“:** Für Kontolöschungen gibt es keinen Trigger. Deshalb laufen - alle Schlüssel mit User-ID oder Namen nach **30 Tagen** ab (`RETENTION_S`), jede Abgabe verlängert die Frist. - Spielerdaten liegen jetzt je Spieler unter `player:{userId}` statt im Hash `players` (einzelne Hash-Felder können - nicht ablaufen). Folge: Bestenlisten älterer Posts sind nach 30 Tagen leer; wer 30 Tage nicht spielt, verliert - Statistik und Freischaltungen (Phase 2 – beim Entwurf der Freischaltungen bedenken). Schutztest in - `server.test.ts`. -- **Playtest-Fallstrick:** Mehrere Änderungen in kurzer Folge (vor allem an `devvit.json`) lösten zwei Uploads mit - derselben Versionsnummer aus („This AppVersion already exists“), `devvit playtest` brach ab → neu starten. - -### 2026-11-13 – Phase 2: Aussehen, Flair, Kommentar - -- **Freischaltungen** (`src/shared/look.ts`, Browser und Server gleich): Farbe frei; Thema über die **beste** Streak - (3 Herbst, 7 Winter, 14 Nacht); Banner je 10 Sterne (Kreuz 50, Stern 60, Krone 80); Volk über Sterne (Orks 20, - Piraten 40). **Winterfest der Reddit-Fassung 1. Dez. – 6. Jan.** (Hauptspiel: ab 15. Dez.) – damit Winterfest-Thema - und Schneeflocken-Banner während der Jury-Bewertung (9.–16. Dez.) für alle frei sind. -- Auswahl über **„🎨 Realm“** auf dem Startbildschirm (einziger zusätzlicher Knopf, Besitzer einverstanden). Gesperrtes - zeigt die Bedingung. Speichern per `POST api/look`; der Server prüft gegen die Freischaltungen (`sanitizeLook`) – - Gesperrtes fällt auf den Standard zurück. Angewendet über `setBoardTheme/Banners/Look` aus der Brett-Kopie. -- Ergebniskarte: **„🎁 Unlocked: …“** bei neuen Freischaltungen (Vergleich der Werte vor und nach der Partie). -- **Ergebnis als Kommentar** (`POST api/comment`): `reddit.submitComment({runAs: 'USER'})`, braucht - `permissions.reddit.asUser: ["SUBMIT_COMMENT"]` (Reddit prüft das in der Review). Text baut der Server aus dem - gespeicherten Ergebnis, einmal je Spieler und Tag (`comments:{day}`, hSetNX). -- **Flair**: nach jeder gewerteten Partie `🔥 N-day streak` (ab 2); beim Tageswechsel bekommt der Sieger von gestern - `👑 Ruler of the day` (`crownDay`, gilt am Folgetag). Fehler beim Flair werden nur protokolliert. Reddit-Vorgabe: - kein Komma im Flair. -- **Fallstrick:** Jede Änderung an `devvit.json` lässt `devvit playtest` die Konfiguration neu laden; zusammen mit dem - Build gab es zweimal doppelte Versionsnummern und Abbruch. Bei Änderungen an `devvit.json` Playtest stoppen und neu - starten. -- Folge der 30-Tage-Regel: Freischaltungen gehen nach 30 Tagen ohne Spiel verloren. - -### 2026-11-13 – Nummer je Subreddit - -- „Ninelands #N“ zählt **je Subreddit ab dem ersten Tag dort** (`firstDay` in Redis, beim ersten Aufruf per `nx` - gesetzt, enthält keine Nutzerdaten und läuft nicht ab). So beginnt `r/Ninelands` mit #1, auch wenn es erst im - Dezember startet. Die Karte hängt weiter nur am Datum (alle spielen am selben Tag dieselbe). `FIRST_DAY` entfällt. -- Plan Besitzer/Claude: `r/ninelands_game_dev` bleibt Test-Subreddit; `r/Ninelands` (Name frei) etwa eine Woche vor - der Abgabe anlegen (≈ 1. Dez.), öffentlich oder „Restricted“, < 200 Mitglieder bis zur Bewertung. - -### 2026-11-13 – Trailer, Galerie, Vimeo-Cover - -- `npm run capture:trailer` (`scripts/capture/trailer.mjs`, Bühne `stage.html/.ts/.css`, Partie `plan.ts`): baut den - Client, startet eine API-Attrappe und lässt das **echte** Spiel (`public/`) in einer Bühne laufen – Untertitel links, - Handy (420 × 840, skaliert) rechts, ein „Finger“ zeigt jeden Tipp. Der Bot (`smart`) spielt die Karte vom 2026-10-10; - die Partie wird beim besten Stand abgeschnitten und per „End game“ beendet (zeigt die Funktion gleich mit). - Ergebnis: `trailer.mp4` (1920 × 1080, ≈ 47 s, Musik mit Ein-/Ausblenden), `vimeo-cover.png`, `gallery/*.png` (3:2). -- Szenen laufen dreimal: Video mit Pausen, dann ohne Pausen für Galerie (1800 × 1200) und Cover (1920 × 1080). -- **Fallstricke:** `UNIT_TYPES` ist `Burg, Ritter, Bauer, Läufer` – die Knopf-Reihenfolge kommt deshalb aus der Engine, - nicht von Hand. Pixelify Sans verbindet „fi“ zu einem Zeichen, das wie „A“ aussieht („Arst“) → auf der Bühne - `font-variant-ligatures: none`. „Play“ im Startbildschirm ruft `requestExpandedMode` (gibt es außerhalb von Reddit - nicht) → nur angetippt, die Bühne wechselt selbst ins Spiel. Playwright-Chromium spielt kein H.264 → eigene - Aufnahmen werden vorher nach VP9/WebM umgewandelt. -- Reddit selbst (Feed, Kommentar im Thread, Flair) nimmt der Besitzer mit seinem Konto auf (Reddit blockt - Automatisierung); optional als `dist/trailer/input/feed.*` / `comment.*`. - -### 2026-11-13 – Realm im Vollbild (Bug vom Besitzer) - -- **Bug:** Am Handy öffnete „🎨 Realm“ das Fenster im Inline-Post (512 px hoch). Die Liste ist höher, „Done“ lag - außerhalb, und scrollen darf ein Inline-Post nicht (Devvit-Regel „Scrolling within inline webviews is prohibited“, - Scroll-Falle im Feed) → man saß fest. -- **Lösung:** eigener Einstiegspunkt `realm` (`public/realm.html`, `src/client/realm-page.ts`), geöffnet mit - `requestExpandedMode(ev, 'realm', {title: 'Your realm'})`; „Done“ ruft `exitExpandedMode`. Der Knopf klebt unten - (`position: sticky`), damit er auch bei langer Liste sichtbar bleibt. Der Startbildschirm lädt beim `focus`-Ereignis - (Rückkehr aus dem Vollbild, laut Doku der Ersatz für `addWebViewModeListener`) Tag und Aussehen neu – damit zeigt er - nach dem Spielen auch gleich das eigene Ergebnis. -- Credits-Fenster im Post geprüft: passt auch bei 320 px Breite (Knopf endet bei 413 von 512 px). -- Pixelify-Ligatur „fi“ (sieht aus wie „A“) jetzt für die ganze App aus (`font-variant-ligatures: none`). -- **Fallstrick:** `devvit.json` geändert → laufenden Playtest stoppen und neu starten. - -### 2026-11-13 – Bilder für die Community-Posts - -- `npm run capture:community` (`scripts/capture/community.{mjs,ts,html,css}`): zwei Bilder 1080 × 1350 (4:5, im Feed - am Handy groß) – „How to play“ (vier Figuren mit beanspruchten Feldern, Gold je Gelände, Sterne) und „Choose your - realm“ (Völker, Bretter, Banner mit Freischalt-Bedingung). Muster kommen aus `UNIT_DEFS` (Strahlen/Ring, Budget), - Bedingungen aus `look.ts` – stimmen also nach Regeländerungen. Seite 540 × 675 mit Faktor 2 aufgenommen, Pixel scharf. -- Das Skript meldet, wenn der Inhalt höher als das Bild ist („… px zu hoch“). -- Texte der drei Posts: `docs/community-posts.md`. - -### 2026-11-13 – Realm: „Play“ direkt ins Spiel (Rückmeldung Besitzer) - -- Realm im Vollbild klappt am Handy. Aber „Done“ führte zurück in den Post, und für „Play“ ging es wieder ins Vollbild – - umständlich. Jetzt steht neben „Done“ ein „Play“ (bzw. „Play again“), das mit `requestExpandedMode(ev, 'game')` - direkt ins Spiel wechselt; laut Doku geht das auch aus einer Vollbild-Ansicht heraus. „Done“ bleibt der Weg zurück. -- Besitzer bestätigt im Test-Subreddit: Ergebnis als Kommentar funktioniert. Video-Posts sind in r/Ninelands aus - (gewollt) → Go-live-Post mit Cover-Bild und Vimeo-Link. - -### 2026-11-14 – „Faction“ statt „People“, Logs vom ersten Tageswechsel - -- Wortwahl (Besitzer): Die Völker heißen auf Englisch überall **Faction/Factions** (Realm-Zeile, Freischalt-Hinweis - auf der Ergebniskarte, Community-Bild und -Post, Trailer-Untertitel, Einreichungstext). Interne Namen (`faction`, - CSS `.peoples`) bleiben. -- `devvit logs ninelands_game_dev --since 1d`: keine Fehler (weder `server error` noch `flair`/`crown flair`), der - Tageswechsel lief also ohne Ausnahme. Sonst nur „Telemetry event dropped: event was valid, but Journey telemetry is - not recorded for playtest app versions.“ → **Journeys werden gültig gesendet, aber nur für installierte - (Nicht-Playtest-)Versionen gezählt.** Auswertung also erst nach Upload + Installation in r/Ninelands sichtbar. - -### 2026-11-14 – Ich-Form, keine langen Gedankenstriche - -- Entscheidung Besitzer: Alle englischen Texte (Devpost, Community-Posts, README, CREDITS, Spiel, Bilder, Trailer) in - der **Ich-Form** und **ohne „–“/„—“**: stattdessen Punkt, Komma, Doppelpunkt oder Semikolon; Datumsbereiche als - „Dec 1 to Jan 6“. Die Devpost-Überschriften („How we built it“ usw.) sind fest vorgegeben und bleiben. -- Deutsche interne Doku und Code-Kommentare bleiben unverändert. Prüfen: - `grep -rn '[–—]' src/client src/shared src/server scripts/capture | grep -vE ':\s*(//|\*)'` muss leer sein. - -### 2026-11-14 – Fehler in den Regeltexten: Gold gibt das Feld unter der Figur - -- Beim Abgleich mit dem Leitfaden des Hauptspiels gefunden: Gold bringt **nur das Feld, auf dem eine Figur steht** - (`Game.resolveYield`: Weide 5/7, Erz/Kräuter 20, Ritter ×2), **nicht** jedes beanspruchte Feld. Falsch stand es im - Hilfe-Fenster des Spiels, in der README, im Community-Post „How to play“ und auf dessen Bild. Alles korrigiert, das - Bild neu erzeugt. „7 next to a lake“ heißt richtig „7 in a land with a lake“. -- Lehre: Regeltexte nie aus dem Gedächtnis schreiben, sondern gegen die Engine (`src/engine/core/Game.ts`, - `units.ts`, `WorldRules.ts`) oder `hivedoku/guide/chapters/` prüfen. - -### 2026-11-14 – Wiki für r/Ninelands - -- `docs/wiki/*.md`: fünf Seiten im Reddit-Markdown zum Einfügen (Mod-Werkzeuge → Wiki, Seitenname = Dateiname): - `index`, `how_to_play`, `laws`, `strategy`, `faq`. Inhalt aus dem Leitfaden des Hauptspiels - (`hivedoku/guide/chapters/01–04, 07, 09`), gekürzt auf die Reddit-Ausgabe (nur 9 × 9, keine Duelle, Banditen, Nebel, - Kampagne) und gegen Engine und `look.ts` geprüft. Englisch, Ich-Form, keine langen Gedankenstriche. -- Bilder bettet das Reddit-Wiki nicht ein → nur Text und Tabellen. Links zwischen den Seiten als `/r/Ninelands/wiki/…`. -- Anlegen per App (`reddit.createWikiPage`) wäre möglich, lohnt aber nicht: braucht Mod-Rechte für die App und ist ein - Schritt mehr, der bei der Installation schiefgehen kann. Der Besitzer fügt die Seiten von Hand ein. -- Ändern sich Kosten, Freischaltungen oder Gesetze, Wiki-Texte mit anpassen. diff --git a/README.md b/README.md index 1a475e1..21bc463 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,11 @@ npm run capture:community # images for the community posts into dist/marketing/ `scripts/sync-engine.sh` (see `src/engine/VERSION`) - `docs/`: submission texts, community posts, the subreddit wiki and the README images +## Made with + +I built this edition with Claude Code (Anthropic) as a coding assistant; you can see that in the commit messages. The +game, its rules and every design decision are mine. + ## Credits - Music: “Dreamscape (part I)” by Vee Ray, CC BY-SA 4.0 (re-encoded) diff --git a/docs/submission.md b/docs/submission.md index 40cac6a..d8de52e 100644 --- a/docs/submission.md +++ b/docs/submission.md @@ -47,9 +47,8 @@ Alternativen: ### About the project (Markdown, LaTeX erlaubt) -> Abschnitt „Inspiration“ bei Bedarf persönlicher machen (eigene Worte des Besitzers). Ob die KI-Unterstützung -> erwähnt wird, entscheidet der Besitzer (die Regeln erlauben technische Hilfe, solange Ideen und Rechte beim -> Teilnehmer liegen). +> Abschnitt „Inspiration“ bei Bedarf persönlicher machen. Die Mitarbeit von Claude Code wird offen genannt +> (Abschnitt „How we built it“, README), so wie es auch die Commit-Nachrichten zeigen. ```markdown ## Inspiration @@ -97,6 +96,8 @@ focused, bite-sized daily edition that lives inside a post, with the comment thr - **Daily posts** via the Devvit scheduler, **Devvit Journeys** for analytics (journey starts when the castle is placed, a progress event per star, the end carries the gold as score). - **Privacy by design:** all user data expires 30 days after a player's last game. +- **AI-assisted coding:** I built this edition together with Claude Code (Anthropic) as a coding assistant, as the + commit history shows. The game, its rules and every design decision are mine. ## Challenges we ran into @@ -128,7 +129,7 @@ focused, bite-sized daily edition that lives inside a post, with the comment thr ### Built with (Tags, max. 25) typescript, javascript, html5, css3, svg, node.js, devvit, devvit-web, reddit, reddit-api, redis, devvit-scheduler, -devvit-journeys, esbuild, biome, playwright, ffmpeg, pixel-art +devvit-journeys, esbuild, biome, playwright, ffmpeg, pixel-art, claude-code ### „Try it out“-Links