# MOONSHIFT A small multiplayer worker-placement strategy game for humans **and** HTTP agents. - Play: https://moonshift.167.233.215.124.sslip.io - API guide: https://moonshift.167.233.215.124.sslip.io/api.html - Frozen external-bot replay: https://moonshift.167.233.215.124.sslip.io/example-replay.json - Browse source: https://moonshift.167.233.215.124.sslip.io/source/ - Clone: `git clone https://moonshift.167.233.215.124.sslip.io/source.git` - Dependency-free Python 3.11+ service; no npm install, login or paid provider. ## Rules in one paragraph Two to four crew compete over four rounds, each docking twice per round in a rotating snake order. Solar produces charge and Salvage brings alloy; spend them at the Relay, Recycler or Launch Pad for points, or invest at Foundry in engines that generate one charge at each later round's start. Berths are shared and limited; one public station bonus changes each round. Rest always grants one charge. Each turn has 15 seconds before the same legal planner covers it without taking your seat away. After four rounds, add two points per engine and one per three leftover resources combined. Highest total wins; ties share victory. ## Run locally ```sh python -m unittest -v python server.py --host 127.0.0.1 --port 8187 ``` Open http://127.0.0.1:8187. Create a 2–4 seat match and press **Start with bots**. To play with friends/agents, create a match, share its code/invite, wait for them to join, then press **Start with crew**. Rooms are stored in `data/matches.sqlite`. No model, wallet, credentials or external network access is required to host. ## Agent API The browser and external agents share one rules engine and the same HTTP action semantics. Server-owned bot seats run a legal three-action planner, without extra resources or privileged moves. The public state exposes exactly the same actions. 1. `POST /api/games` with `{"name":"SCOUT","seats":2}`; save its returned `gameId`, `playerId`, `token` privately. Optional `seed` is a 32-bit integer. 2. Or `POST /api/games/CODE/join` with `{"name":"SCOUT"}` to join a human lobby. 3. The host starts: `POST /api/games/CODE/start` with `{"fillBots":true}` and `Authorization: Bearer TOKEN`. `fillBots:false` requires a full lobby. 4. `GET /api/games/CODE` returns public state. When `currentPlayer` is your ID, choose an `actions[]` entry with `available:true`. 5. `POST /api/games/CODE/moves` with `{"turn":CURRENT_TURN,"action":"solar"}` and your bearer token. Use the current turn number; stale turns return 409. 6. When phase is `finished`, inspect `scores`, `winners`, and `GET /api/games/CODE/replay` (complete move log + resource snapshots). Every POST uses `Content-Type: application/json`. Poll about once per second. Routes `/api/health` and `/api/rules` are public. No API keys or account required. 401 = missing/invalid crew token; 403 = host-only; 409 = stale/illegal move; 429 = wait one minute. Room creation is limited to 8/IP/minute; other writes 180/IP/minute. The included external bot only uses this documented HTTP API: ```sh # Host a fresh bot-vs-bot match: python bot.py http://127.0.0.1:8187 # Join a human lobby; its host then starts the match: python bot.py http://127.0.0.1:8187 ABC123 --name SCOUT ``` The sample bot keeps its token in process memory and never prints it. To build a different bot, replace `choose(state)`; no internal engine import is needed. `public/example-replay.json` is an unmodified, token-free completed public match record from 2026-11-04 (API SCOUT versus the built-in KESTREL), not a staged score. ## Design decisions and limits - Shared berths prevent repeatedly farming one route. Four public events change immediate values; early engines compete with immediate scoring; rotating snake order balances first choice. This is not a mathematical proof of perfect balance. - Maximum four-player match: 32 turns × 15 seconds ≈ 8 minutes plus small server scheduling overhead. A background ticker advances bots/timeouts without viewers. - SQLite WAL + a transaction lock preserve turns across concurrent requests and process restarts. Public reads/replay contain no crew tokens; only token hashes are stored. Duplicate/stale moves do not spend resources twice. - Rooms expire after 24 hours of inactivity, with at most 1,000 rooms retained. Names, moves and scores are public. Do not submit personal information. - One independent service, own SQLite database, own public hostname; no QRobot account, worker, connector, deployment or runtime dependency. - This demonstration does not operate any cryptocurrency or payment flows. - Desktop and mobile layouts, keyboard-accessible controls, semantic dialog, reduced-motion support, no third-party scripts, fonts or analytics at runtime. ## Verification `test_engine.py` covers costs, capacity, stale/wrong-turn atomicity, engine/event effects, timeout fallback, snake initiative, and 60 seeded full matches across 2/3/4 crew with deterministic replay. `test_server.py` covers two independent HTTP clients completing a match, bearer isolation, host permission, invalid payloads, traversal refusal, rate limits, and token-free replay. Dated browser and live deployment evidence is included with the contest delivery; a test count alone is not browser proof. ## Credits and rights Code and original interface: QRobot Pilot, AI-assisted using OpenAI Codex, 2026. The original lunar engraving was generated with the built-in OpenAI Image Gen tool. No existing game's art/characters, external proprietary assets, user identities or fabricated reviews are used. Interface pictograms are original SVG. Barlow Condensed Bold is bundled under SIL Open Font License; see `public/FONT-LICENSE.txt`. Code is MIT licensed (`LICENSE`). Generated artwork is provided for use with this entry; no exclusivity or third-party trademark claimed.