# Lucid Programming Competition - Rules 2026 Teams collect resources, build bases, and recruit workers. Your program submits one action per worker for each turn. After each turn, you receive an updated view of the world visible to your team. Resource quantities are measured in kilograms (kg). Inventory, bank, cost, and deposit amounts in the API use the same numeric values: an amount of 5 means 5 kg. ## Objective and starting state Collect gold and deposit it at your bases. Final score is the gold remaining in your team's shared bank. Carried gold does not contribute to that score; spending banked gold reduces the amount left to score. When banked gold is equal, compare these values in order, with higher values winning: 1. Banked iron. 2. Total gold carried by all of the team's workers. 3. Total iron carried by all of the team's workers. 4. Number of bases. 5. Number of workers. Only move to the next criterion when the previous values are equal. If every value matches, teams share placement, skipping the following placements (1, 1, 3). There is no lottery or team-ID tie-breaker. The reported score remains banked gold in kg; the other values determine placement only. These rules rank teams within a match, not across the competition's overall leaderboard. The default match has 16 teams on a 50 by 50 map and ends after 480 turns. Each team starts with one base, one worker nearby, and a shared bank with unlimited storage containing 0 kg of gold and 15 kg of iron. All friendly bases use this bank. Workers carry up to 8 kg total. Gold deposits start with 6–9 kg outside the rich center and 12–18 kg inside it; iron deposits hold 2–4 kg; the initial layout allocates three gold and two iron deposits per team. Coordinates use `x` increasing east and `y` increasing south. The `cells` array stores the map one row at a time, from left to right and then top to bottom. Coordinates and array indices start at zero. To find cell `(x, y)`, use `cells[y * width + x]`. For example, on a 50-wide map, `(2, 1)` is `cells[52]`: skip the first 50 cells, then move two cells into the second row. Workers and bases are separate lists; multiple workers can occupy one cell. ## Worker actions Send actions under the worker's `unitId`, not a base ID or team ID. Each submitted action is consumed for its accepted turn, including actions whose requirements are not met. Submit fresh actions each turn. - **Move:** `{"type":"move","direction":"north"}`. Directions are `north`, `north-east`, `east`, `south-east`, `south`, `south-west`, `west`, and `north-west`. - Ordinary movement takes one turn, including diagonals. - Entering a mountain or crater puts the worker on cooldown for 2 additional turns. Actions will be accepted but not executed during this travel time. The worker enters the destination immediately. Ignored actions are consumed, not queued. After those two turns, submit a new action. The configurable `obstacleTraversalCost` includes the entry turn; a cost of 1 adds no cooldown. Each visible worker has `readyOnTurn`: it can act when the current view's `turn` is at least this value. - Workers cannot leave the map or enter an enemy base. - Friendly and opposing workers may overlap. Worker occupancy does not block movement, and simultaneous movement to the same tile is allowed. - **Mine:** `{"type":"mine"}` takes 1 kg from the gold or iron deposit at the worker's position, if stock and inventory space remain. Deposits within two Manhattan steps of a base are claimed: only claiming teams may mine them. If friendly and enemy claims overlap, either claiming team may mine. Unclaimed deposits are available to everyone. - Multiple workers can mine the same deposit in a turn. If stock is insufficient for all eligible miners, workers with the longest current streak of eligible mine actions on that tile mine first. An eligible attempt counts even if another worker takes the remaining ore. Waiting, taking another action, or an ineligible mine attempt ends the streak. For equal mining streaks, workers that have stayed on the tile longest mine first. Leaving and returning resets both mining and occupancy priority. Workers tied on both use a seeded random tie-breaker. Submission time does not determine mining priority. - **Deposit:** `{"type":"deposit"}` transfers all carried gold and iron to the shared bank when standing on a friendly base. - **Trade:** `{"type":"trade","from":"iron"}` exchanges 3 kg of carried iron for 1 kg of gold. `from: "gold"` exchanges 3 kg of carried gold for 1 kg of iron. Trading uses the worker's inventory, not the bank, and does not require a base. - **Spawn:** `{"type":"spawn"}` creates a worker on the acting worker's tile, spending from the bank. It does not require standing on a base. The first two recruited workers cost 5 kg and 6 kg of iron; each subsequent price is the sum of the previous two (5, 6, 11, 17, … kg). Starting workers do not count. - **Build base:** `{"type":"build-base"}` costs 6 kg of banked iron. The builder must stand on empty terrain with no base and no other worker, including friendly workers. Mountains, craters, and resource tiles are not valid sites. The site must be more than two Manhattan steps from every enemy base; friendly bases do not restrict construction. New bases affect mining eligibility in the same turn. - **Pass:** `{"type":"pass"}` does no work for the accepted turn. A worker with no accepted action also idles. Request-level passing is described below. ## Turn resolution Each turn resolves in this order: 1. Due depleted deposits respawn. 2. Workers move. 3. Workers spawn new workers or build bases. 4. Workers mine. 5. Workers trade. 6. Workers deposit. 7. Built-in bot memory updates, and the turn counter advances. Construction happens before deposits: iron deposited this turn cannot pay for construction until a later turn. The match seed determines an initial team order for construction. After each turn, the first team moves to the back of that order. All teams keep their place in the rotation, even when they do not submit a construction action. If new opposing bases would claim each other's sites, the team earlier in that turn's order builds first. Within a team, construction consumes its shared bank in action acceptance-time order, with equal times resolved by worker ID. This applies to both building workers and building bases. An unsuccessful construction action spends no resources and does not stop other workers from building. Mining scarcity uses the seeded selection described above. A worker performs only its submitted action. ## Resource respawning When a deposit is depleted, a replacement of the same resource is scheduled for the start of the following turn. It receives a newly seeded amount (6–9 kg of outside gold, 12–18 kg of center gold, or 2–4 kg of iron). Its location is selected reproducibly from empty terrain without a worker or base. Each gold replacement independently has a 35% chance (configurable) of spawning in the center and otherwise spawns outside it, regardless of where it was mined. If the chosen zone has no eligible cell, it uses the other zone. The emptied original position can be reused if no other eligible cell exists in the chosen zone. Placement prefers sites at least two movement steps from the old site and other resource sites (diagonal distance counts as one step). If none meet both spacing preferences, it relaxes spacing from other resources, then spacing from the old site. If no valid site exists, respawning waits until a later turn. ## Vision and private information Friendly workers and bases reveal tiles within Manhattan distance 3 by default. Mountains and craters do not block vision. A worker or base on either obstacle also reveals all eight neighboring cells. A live player view contains: - `map`: visible terrain and resource amounts, with unseen playable cells marked `hidden`. - `units`: workers on visible cells. Your workers include their inventory; opponents' inventories are omitted. - `bases`: bases on visible cells. - `scores`: banked gold totals for all teams, including teams outside your vision. - `teamResources`: banked gold and iron for your team only. - `baseResourceDeposits`: deposits at your bases only. - `turn`, `warnings`, `info`, and `effects`. The enclosing state response includes `match` and `serverTimeMs`, and may include `turnEndsAtMs`. Pending matches may omit `view`. Do not infer hidden terrain or inventories from missing data. Completed replays available to participants contain the full map and resources, plus `placements` calculated using the tie-break criteria for the displayed turn; admin live views are also authoritative/full-state. Live player access is different from both. ## Actions and timing The downloaded helpers take worker IDs mapped to individual action objects: ```json { "your-worker-id": { "type": "mine" } } ``` This only mines successfully when the worker stands on an eligible deposit and has inventory space. Submit a new action each turn; a sequence such as moving, mining, returning, and depositing belongs in your strategy logic. Helpers submit to the match selected at registration. Custom clients can use `PUT /api/team/matches/current/actions` or `PUT /api/team/matches/:matchId/action` to stay bound to a specific match: ```json { "submissionId": "a-new-unique-id-for-this-request", "submissions": [ { "entityId": "your-worker-id", "revision": 1, "action": { "type": "mine" } } ] } ``` - A submission replaces the worker's pending action for its accepted turn. It does not append future work. - Omitting a worker leaves any action already accepted for that turn intact. Returning `{}` does not cancel all pending actions. An empty array is not a valid worker action; submit `{"type":"pass"}` to replace a pending action with an idle action. - At the raw batch endpoint, `{"advanceOnly":true}` passes without replacing actions. It advances client-action practice, not arbitrary ranked/timed turns. - Malformed LPC actions reject the batch with HTTP 400 and code `invalid_action`. Correct the payload before resubmitting. No actions are replaced and no client-action turn advances. This does not predict whether a valid action will succeed on the map. - Revisions are non-negative safe integers (at most 9,007,199,254,740,991), scoped to one match, team, and entity. Every replacement, including a pass action, must use a revision strictly greater than that entity's highest accepted revision. Consuming actions or advancing turns does not reset the counter; a new match does. - For a new submission, if any action has a stale or equal revision, the entire batch is rejected with HTTP 409 and code `stale_action_revision`. No actions change and no client-action turn advances. Successful acknowledgments report the accepted revision and `acceptedForTurn` for each entity. - Registration and team-state responses include `actionRevisions`, a map of entity IDs to their highest accepted revisions. Helpers initialize their counters from registration, so restarting in the same match continues counting. Custom clients must do the same; do not reset to 1 mid-match. - Use a unique `submissionId` for every new request, including empty batches and `{"advanceOnly":true}`. It must contain 1–128 letters, digits, hyphens, or underscores; UUIDs work. IDs are scoped to your authenticated team across matches. The starter clients generate them. - To recover a lost acknowledgment, resend the same ID and exact revisions/actions to the same match-specific endpoint. The server returns the original acknowledgment (including `submissionId`) without replacing actions or triggering another turn, even after completion or a server restart. Object key order does not matter; array order does. Reusing an accepted ID for a different payload or explicit match returns HTTP 409, `submission_conflict`. - Receipts are retained until the associated match is deleted. The acknowledgment confirms acceptance, not completion of a turn. If the server fails after accepting a request but before advancing a client-action match, the actions remain pending and a retry only returns the receipt. Check state; use a new intentional submission/pass to advance. Timed matches continue on their server schedule. - To retry registration safely, first read the current match, choose a random UUID for `x-bot-session`, and send the selected match ID in `x-bot-match-id` on registration. Reuse both headers for every retry. Registration stays bound to that match even if a new match becomes current. - Register one bot session per match and API key. Keep the `x-bot-session` response header and send it with state, action, and event-stream requests. All action routes require it, including requests authenticated by a browser cookie. A competing session receives HTTP 409 with code `bot_session_conflict`. Browser viewing and the Join button remain available. An open bot stream holds the session; after a disconnect, the same token has 60 seconds to reconnect before another bot can claim it. Completion releases the session. Sessions reset when the server restarts. - Keep one helper process per team. Calling a helper again generates a new submission ID; it is not a retry. Helpers retry eligible transport failures at most three times with the original serialized body, ID, and revisions; they do not recalculate actions for a retry. Registration retries use the same random session token and selected match ID. Other writes through a mutable current-match endpoint are not automatically retried. See `/help` for timeout and reconnect limits. Requests without an ID (including raw `pass`) have no deduplication guarantee. Repeating a revision under a new ID is still a stale-revision error; do not automatically resubmit rejected work with a higher revision. - In scheduled modes, acceptance before the current deadline targets `match.turn + 1`; acceptance at or after that deadline targets `match.turn + 2`. Inspect `acceptedForTurn` in the acknowledgement. The server decides acceptance under its match lock; sending before a local-clock deadline does not guarantee acceptance before the server deadline. Turn modes: - `timed`: the server advances on its timer, normally every 1000ms, even if a client is idle. - `client-action`: contestant LPC practice advances when the human client submits actions or a pass request. Without submissions, the turn does not advance. - `multi-client-action`: the server can advance once every controllable entity has an accepted action, subject to a minimum turn duration (200ms); a 1000ms fallback timer prevents waiting indefinitely. Readiness is per entity, not one request per team. This mode is available through the ranked-lobby API; ordinary admin round creation uses timed matches. The organizers' default template determines contestant practice mechanics. LPC practice uses client-action turns; Solar Orbitor practice uses timed turns. The starter clients register for the team's existing current match; they do not create it. See `/help` for setup and connection behavior. The default mountain terrain weight is 20%; crater terrain weight is 10%. ## High gold area A centered 16 by 16 rectangle contains richer gold deposits, each holding 12–18 kg, twice the normal amount range. Outside this rectangle, gold deposits hold 6–9 kg. When exact centering falls between cells, the rectangle is shifted toward the top/left. Initially, 35% of generated gold deposits are assigned to the center, rounded to the nearest whole deposit. Each depleted gold deposit independently chooses its respawn zone using the same configured center percentage and receives an amount from its new zone's range. If one zone has too few eligible initial cells, placement uses remaining cells in the other zone. The 35% center share concentrates gold in the rich rectangle, which covers 10.24% of the default map area. Visible deposits expose their remaining quantity in kg in `amount`, which decreases by 1 with each successful mining action.