Skip to content

Feat/competition tooling - #138

Open
mrinmoy2developer wants to merge 5 commits into
strakam:masterfrom
mrinmoy2developer:feat/competition-tooling
Open

Feat/competition tooling#138
mrinmoy2developer wants to merge 5 commits into
strakam:masterfrom
mrinmoy2developer:feat/competition-tooling

Conversation

@mrinmoy2developer

Copy link
Copy Markdown

No description provided.

mrinmoy and others added 5 commits July 26, 2026 19:53
The GUI hardcoded a 50px cell, so a 22x22 competition board wanted
1436x1101 and simply did not fit a 1440x932 screen. Cell size is now a
property of the GUI: with no explicit size it is derived from the desktop
dimensions and never scales above the old default, so small boards look
exactly as before. Cell contents follow — icons are smoothscaled and army
counts get a proportional font. The right panel keeps its fixed sizing so
the scoreboard stays readable at any zoom.

ReplayGUI/GUI take cell_size=None and expose the value actually used.

protocol.encode_observation: build each row with row.tolist() + map(str)
instead of indexing numpy scalars one at a time. Byte-identical output,
0.125ms -> 0.071ms per call, and it runs 3xH times per turn per agent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
matchup2 is matchup with the ruleset gaps closed. matchup drives game.step
directly, which skips the build-castles and deathtouch modifiers that live
in GeneralsEnv.step — so under --mode competition a `2 r c 0 0` build was
not merely ignored, it fell through to _execute_move and silently marched
the army upward. It also always generated a max_grid_size square instead of
drawing each side per seed. Both fixed here, plus:

  --log PATH     transcript of the engine<->agent conversation, with
                 per-decision timing, flushed so a hang leaves a usable tail
  --show-grids   full observation grids in the log (~2MB/match, opt-in)
  --save PATH    compact recording
  --cell-size    GUI zoom

replay.py stores seed + action stream only and re-simulates on playback:
2.0KB versus 51KB for a compressed state dump, verified bit-exact across
every frame and field against a live re-run.

arena.py plays every seed in both seat orders and reports a score with a
95% interval. Fusing the per-turn modifier dispatches under one jit and
batching the action transfer took a 200-match run from 76s to ~55s with
identical results.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nine generations, each its own directory so every earlier bot stays
runnable and every claim in my_bot.md stays reproducible. my_bot9 goes
12-0-0 against expander_python and 81W-27L-12D over 120 matches against a
frozen my_bot5.

What drove it:

  * A castle is worth ~25 land. global_update grants +1/cell every 50 ticks
    but +1/structure every 2, and competition maps spawn with no neutral
    castles — building is the only economy there is (my_bot..my_bot3).
  * Economy alone wins nothing: my_bot3 reached 10,607 army against 943 and
    still went 0-1-11, every game a truncation draw. BFS pathfinding plus
    deathtouch awareness converted that into wins (my_bot4).
  * Attacking the nearest enemy cell nibbles the shared border and never
    reveals anything; aiming at the fog behind enemy territory found the
    general (my_bot6, 4W-0L-8D -> 11W-0L-1D).
  * my_bot9: the starter's _is_passable treats type 5 (structure under fog)
    as impassable, but 5 is a mountain OR a castle. Bots ring their own
    general with castles, so both sides were unreachable by construction —
    "general known for 500 turns, no path exists". Two-tier routing with
    learned walls halved the draws.

my_bot.md records two negative results as well (my_bot7's gather and
my_bot8's rush both measured flat) and the harness discipline that exposed
them: fixed opponent, 120 matches minimum, replicate on a disjoint seed
range before believing anything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
matchup3 serves a generals.io-style client and drives the bot over the
ordinary stdio protocol, so competition agents run unmodified. It uses the
same engine path as matchup2, and the player only ever receives
get_observation(state, seat) — the same fog the bot gets.

relay.py runs on a public box and holds no engine at all: the laptop dials
out to it, so bots can run behind NAT while the site stays reachable. When
no worker has checked in the site reports "no bots live" and Start is
disabled. Liveness counts an in-flight long-poll as connected and derives
its timeout from LONG_POLL, because a shorter window declared a healthy
worker dead between polls.

Client: click selects, double-click arms a half-army move, right-click
builds. The move queue lives server-side like generals.io, so a closed tab
just drains it. Replays are archived (--replays N) and can be watched from
either seat or with no fog — fog is a 3x3 dilation of owned cells,
reproduced in the browser and cross-checked against the engine's own
get_visibility. The true board is released only after a match ends.
Autopilot hands the player's seat to a bot and back mid-game. Export
produces a real .rpl that competition/replay.py can open.

Stdlib only on both sides, so the public box needs no pip install.
deploy/ has a systemd unit and a Caddyfile for automatic HTTPS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One page per tool — matchup/matchup2/matchup3, relay, arena, replay — with
a template command, a flag table and the operational details that actually
cost time: --seed defaults to 0 so runs repeat the same map, --mode
overrides the manual ruleset flags, --relay wants the base URL, -u matters
because block-buffered logs look like a dead process, arena results under
~120 matches are noise, and Oracle Cloud needs both a VCN ingress rule and
the instance firewall opened.

Every command in it was executed; all flags checked against argparse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant