ai-agent-arcade 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. ai_agent_arcade-0.1.3/.github/workflows/publish.yml +55 -0
  2. ai_agent_arcade-0.1.3/.gitignore +39 -0
  3. ai_agent_arcade-0.1.3/LICENSE +21 -0
  4. ai_agent_arcade-0.1.3/PKG-INFO +374 -0
  5. ai_agent_arcade-0.1.3/README.md +360 -0
  6. ai_agent_arcade-0.1.3/cli/pyproject.toml +24 -0
  7. ai_agent_arcade-0.1.3/cli/record_tui_gif.py +140 -0
  8. ai_agent_arcade-0.1.3/cli/render_readme_media.py +130 -0
  9. ai_agent_arcade-0.1.3/cli/run_alcf_series.py +8 -0
  10. ai_agent_arcade-0.1.3/cli/src/arcade_cli/__init__.py +0 -0
  11. ai_agent_arcade-0.1.3/cli/src/arcade_cli/arcade_app.py +738 -0
  12. ai_agent_arcade-0.1.3/cli/src/arcade_cli/main.py +1047 -0
  13. ai_agent_arcade-0.1.3/cli/src/arcade_cli/series.py +809 -0
  14. ai_agent_arcade-0.1.3/cli/src/arcade_cli/themes.py +240 -0
  15. ai_agent_arcade-0.1.3/cli/src/arcade_cli/thunderdome.py +14 -0
  16. ai_agent_arcade-0.1.3/cli/tests/test_alcf_series.py +658 -0
  17. ai_agent_arcade-0.1.3/cli/tests/test_cli.py +1654 -0
  18. ai_agent_arcade-0.1.3/cli/tests/test_thunderdome.py +568 -0
  19. ai_agent_arcade-0.1.3/cli/uv.lock +86 -0
  20. ai_agent_arcade-0.1.3/configs/muse-mistral.toml +15 -0
  21. ai_agent_arcade-0.1.3/configs/muse.toml +15 -0
  22. ai_agent_arcade-0.1.3/docs/arcade-tui.tape +39 -0
  23. ai_agent_arcade-0.1.3/docs/cli-brief.md +127 -0
  24. ai_agent_arcade-0.1.3/docs/design-history.md +632 -0
  25. ai_agent_arcade-0.1.3/docs/game6-briefs.md +73 -0
  26. ai_agent_arcade-0.1.3/docs/match-state-spec.md +349 -0
  27. ai_agent_arcade-0.1.3/docs/retro-game5/retro-codex.md +26 -0
  28. ai_agent_arcade-0.1.3/docs/retro-game5/retro-grok.md +81 -0
  29. ai_agent_arcade-0.1.3/docs/retro-game5/retro-kimi.md +22 -0
  30. ai_agent_arcade-0.1.3/docs/retro-game6/retro6-codex.md +33 -0
  31. ai_agent_arcade-0.1.3/docs/retro-game6/retro6-gemini.md +19 -0
  32. ai_agent_arcade-0.1.3/docs/retro-game6/retro6-grok.md +119 -0
  33. ai_agent_arcade-0.1.3/docs/seat-feedback-game5.md +86 -0
  34. ai_agent_arcade-0.1.3/docs/seat-feedback-game6.md +80 -0
  35. ai_agent_arcade-0.1.3/docs/xiangqi-design-brief.md +342 -0
  36. ai_agent_arcade-0.1.3/engine/FACILITATOR.md +424 -0
  37. ai_agent_arcade-0.1.3/engine/FACILITATOR_CW.md +560 -0
  38. ai_agent_arcade-0.1.3/engine/FACILITATOR_XQ.md +414 -0
  39. ai_agent_arcade-0.1.3/engine/ansi_png.py +217 -0
  40. ai_agent_arcade-0.1.3/engine/approver.sh +22 -0
  41. ai_agent_arcade-0.1.3/engine/arcade_panel.py +263 -0
  42. ai_agent_arcade-0.1.3/engine/chat.py +151 -0
  43. ai_agent_arcade-0.1.3/engine/chat_tui.py +161 -0
  44. ai_agent_arcade-0.1.3/engine/clock.py +25 -0
  45. ai_agent_arcade-0.1.3/engine/corewar.py +1286 -0
  46. ai_agent_arcade-0.1.3/engine/director_keeper.sh +42 -0
  47. ai_agent_arcade-0.1.3/engine/dna_cw.py +572 -0
  48. ai_agent_arcade-0.1.3/engine/donut.py +33 -0
  49. ai_agent_arcade-0.1.3/engine/eval.py +216 -0
  50. ai_agent_arcade-0.1.3/engine/game.sh +68 -0
  51. ai_agent_arcade-0.1.3/engine/game_cli.py +444 -0
  52. ai_agent_arcade-0.1.3/engine/game_cw.sh +89 -0
  53. ai_agent_arcade-0.1.3/engine/game_cw_cli.py +411 -0
  54. ai_agent_arcade-0.1.3/engine/game_xq.sh +69 -0
  55. ai_agent_arcade-0.1.3/engine/game_xq_cli.py +498 -0
  56. ai_agent_arcade-0.1.3/engine/keeper.sh +10 -0
  57. ai_agent_arcade-0.1.3/engine/life.py +21 -0
  58. ai_agent_arcade-0.1.3/engine/panel_tui.py +187 -0
  59. ai_agent_arcade-0.1.3/engine/record_cw.py +547 -0
  60. ai_agent_arcade-0.1.3/engine/ref.py +343 -0
  61. ai_agent_arcade-0.1.3/engine/ref_cw.py +684 -0
  62. ai_agent_arcade-0.1.3/engine/ref_xq.py +383 -0
  63. ai_agent_arcade-0.1.3/engine/relay1-legacy.sh +53 -0
  64. ai_agent_arcade-0.1.3/engine/relay2.sh +68 -0
  65. ai_agent_arcade-0.1.3/engine/results.seed.txt +2 -0
  66. ai_agent_arcade-0.1.3/engine/score_cw.py +1456 -0
  67. ai_agent_arcade-0.1.3/engine/test_chat_tui.py +42 -0
  68. ai_agent_arcade-0.1.3/engine/test_corewar.py +764 -0
  69. ai_agent_arcade-0.1.3/engine/test_dna_cw.py +409 -0
  70. ai_agent_arcade-0.1.3/engine/test_eval.py +102 -0
  71. ai_agent_arcade-0.1.3/engine/test_game_cli.py +397 -0
  72. ai_agent_arcade-0.1.3/engine/test_game_cw_cli.py +565 -0
  73. ai_agent_arcade-0.1.3/engine/test_game_xq_cli.py +377 -0
  74. ai_agent_arcade-0.1.3/engine/test_record_cw.py +491 -0
  75. ai_agent_arcade-0.1.3/engine/test_ref_cw.py +661 -0
  76. ai_agent_arcade-0.1.3/engine/test_ref_xq.py +440 -0
  77. ai_agent_arcade-0.1.3/engine/test_score_cw.py +905 -0
  78. ai_agent_arcade-0.1.3/engine/test_shell_wrappers.py +312 -0
  79. ai_agent_arcade-0.1.3/engine/test_tui_corewar.py +2339 -0
  80. ai_agent_arcade-0.1.3/engine/test_tui_dots.py +322 -0
  81. ai_agent_arcade-0.1.3/engine/test_tui_xiangqi.py +983 -0
  82. ai_agent_arcade-0.1.3/engine/test_xiangqi.py +340 -0
  83. ai_agent_arcade-0.1.3/engine/tui.py +551 -0
  84. ai_agent_arcade-0.1.3/engine/tui_corewar.py +2217 -0
  85. ai_agent_arcade-0.1.3/engine/tui_dots.py +975 -0
  86. ai_agent_arcade-0.1.3/engine/tui_xiangqi.py +1387 -0
  87. ai_agent_arcade-0.1.3/engine/xiangqi.py +561 -0
  88. ai_agent_arcade-0.1.3/games/chess/match-009/banner.txt +1 -0
  89. ai_agent_arcade-0.1.3/games/chess/match-009/chat.log +6 -0
  90. ai_agent_arcade-0.1.3/games/chess/match-009/fen.txt +1 -0
  91. ai_agent_arcade-0.1.3/games/chess/match-009/host.txt +1 -0
  92. ai_agent_arcade-0.1.3/games/chess/match-009/match.json +23 -0
  93. ai_agent_arcade-0.1.3/games/chess/match-009/moves.txt +1 -0
  94. ai_agent_arcade-0.1.3/games/chess/match-009/names.txt +1 -0
  95. ai_agent_arcade-0.1.3/games/chess/match-009/result.txt +1 -0
  96. ai_agent_arcade-0.1.3/games/chess/match-009/results.txt +1 -0
  97. ai_agent_arcade-0.1.3/games/chess/match-009/roles.txt +6 -0
  98. ai_agent_arcade-0.1.3/games/chess/match-009/seats.txt +2 -0
  99. ai_agent_arcade-0.1.3/games/chess/match-009/series.txt +1 -0
  100. ai_agent_arcade-0.1.3/games/chess/match-009/stage_log.txt +11 -0
  101. ai_agent_arcade-0.1.3/games/chess/match-171/banner.txt +1 -0
  102. ai_agent_arcade-0.1.3/games/chess/match-171/chat.log +3 -0
  103. ai_agent_arcade-0.1.3/games/chess/match-171/decision_log.jsonl +80 -0
  104. ai_agent_arcade-0.1.3/games/chess/match-171/fen.txt +1 -0
  105. ai_agent_arcade-0.1.3/games/chess/match-171/host.txt +1 -0
  106. ai_agent_arcade-0.1.3/games/chess/match-171/match.json +23 -0
  107. ai_agent_arcade-0.1.3/games/chess/match-171/moves.txt +1 -0
  108. ai_agent_arcade-0.1.3/games/chess/match-171/names.txt +1 -0
  109. ai_agent_arcade-0.1.3/games/chess/match-171/result.txt +1 -0
  110. ai_agent_arcade-0.1.3/games/chess/match-171/results.txt +93 -0
  111. ai_agent_arcade-0.1.3/games/chess/match-171/roles.txt +6 -0
  112. ai_agent_arcade-0.1.3/games/chess/match-171/seats.txt +2 -0
  113. ai_agent_arcade-0.1.3/games/chess/match-171/series.txt +1 -0
  114. ai_agent_arcade-0.1.3/games/chess/match-171/stage_log.txt +80 -0
  115. ai_agent_arcade-0.1.3/games/corewar/match-001/banner.txt +1 -0
  116. ai_agent_arcade-0.1.3/games/corewar/match-001/battle.json +45 -0
  117. ai_agent_arcade-0.1.3/games/corewar/match-001/host.txt +1 -0
  118. ai_agent_arcade-0.1.3/games/corewar/match-001/match.json +23 -0
  119. ai_agent_arcade-0.1.3/games/corewar/match-001/moves.txt +8 -0
  120. ai_agent_arcade-0.1.3/games/corewar/match-001/names.txt +1 -0
  121. ai_agent_arcade-0.1.3/games/corewar/match-001/result.txt +1 -0
  122. ai_agent_arcade-0.1.3/games/corewar/match-001/results.txt +1 -0
  123. ai_agent_arcade-0.1.3/games/corewar/match-001/roles.txt +6 -0
  124. ai_agent_arcade-0.1.3/games/corewar/match-001/seats.txt +2 -0
  125. ai_agent_arcade-0.1.3/games/corewar/match-001/series.txt +1 -0
  126. ai_agent_arcade-0.1.3/games/corewar/match-001/stage_log.txt +3 -0
  127. ai_agent_arcade-0.1.3/games/corewar/match-001/warriors/A.red +18 -0
  128. ai_agent_arcade-0.1.3/games/corewar/match-001/warriors/B.red +29 -0
  129. ai_agent_arcade-0.1.3/games/corewar/match-002/banner.txt +1 -0
  130. ai_agent_arcade-0.1.3/games/corewar/match-002/battle.json +45 -0
  131. ai_agent_arcade-0.1.3/games/corewar/match-002/host.txt +1 -0
  132. ai_agent_arcade-0.1.3/games/corewar/match-002/match.json +23 -0
  133. ai_agent_arcade-0.1.3/games/corewar/match-002/moves.txt +8 -0
  134. ai_agent_arcade-0.1.3/games/corewar/match-002/names.txt +1 -0
  135. ai_agent_arcade-0.1.3/games/corewar/match-002/result.txt +1 -0
  136. ai_agent_arcade-0.1.3/games/corewar/match-002/results.txt +2 -0
  137. ai_agent_arcade-0.1.3/games/corewar/match-002/roles.txt +6 -0
  138. ai_agent_arcade-0.1.3/games/corewar/match-002/seats.txt +2 -0
  139. ai_agent_arcade-0.1.3/games/corewar/match-002/series.txt +1 -0
  140. ai_agent_arcade-0.1.3/games/corewar/match-002/stage_log.txt +3 -0
  141. ai_agent_arcade-0.1.3/games/corewar/match-002/warriors/A.red +21 -0
  142. ai_agent_arcade-0.1.3/games/corewar/match-002/warriors/B.red +34 -0
  143. ai_agent_arcade-0.1.3/games/xiangqi/match-001/banner.txt +1 -0
  144. ai_agent_arcade-0.1.3/games/xiangqi/match-001/director.txt +1 -0
  145. ai_agent_arcade-0.1.3/games/xiangqi/match-001/fen.txt +1 -0
  146. ai_agent_arcade-0.1.3/games/xiangqi/match-001/match.json +23 -0
  147. ai_agent_arcade-0.1.3/games/xiangqi/match-001/moves.txt +1 -0
  148. ai_agent_arcade-0.1.3/games/xiangqi/match-001/names.txt +1 -0
  149. ai_agent_arcade-0.1.3/games/xiangqi/match-001/result.txt +1 -0
  150. ai_agent_arcade-0.1.3/games/xiangqi/match-001/results.txt +1 -0
  151. ai_agent_arcade-0.1.3/games/xiangqi/match-001/roles.txt +6 -0
  152. ai_agent_arcade-0.1.3/games/xiangqi/match-001/seats.txt +2 -0
  153. ai_agent_arcade-0.1.3/games/xiangqi/match-001/series.txt +1 -0
  154. ai_agent_arcade-0.1.3/pyproject.toml +40 -0
  155. ai_agent_arcade-0.1.3/uv.lock +694 -0
@@ -0,0 +1,55 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write # OIDC trusted publishing — no token needed after setup
12
+
13
+ jobs:
14
+ build:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.11"
21
+ - name: Build sdist + wheel
22
+ run: |
23
+ pip install build
24
+ python -m build
25
+ ls -lh dist/
26
+ python -m tarfile -l dist/*.tar.gz 2>&1 | head -20 || tar tzf dist/*.tar.gz | head -20
27
+ - name: Check package
28
+ run: |
29
+ pip install twine
30
+ twine check dist/*
31
+ - uses: actions/upload-artifact@v4
32
+ with:
33
+ name: dist
34
+ path: dist/
35
+
36
+ publish:
37
+ needs: build
38
+ runs-on: ubuntu-latest
39
+ environment: pypi # requires approval + OIDC publisher; change to no env if you prefer auto
40
+ permissions:
41
+ id-token: write
42
+ steps:
43
+ - uses: actions/download-artifact@v4
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+ - uses: pypa/gh-action-pypi-publish@release/v1
48
+ with:
49
+ # Uses OIDC — add trusted publisher at
50
+ # https://pypi.org/manage/account/publishing/ for saforem2/ai-agent-arcade
51
+ # (owner saforem2, repo ai-agent-arcade, workflow publish.yml, env pypi)
52
+ # Fallback: set PYPI_API_TOKEN repo secret and uncomment:
53
+ # password: ${{ secrets.PYPI_API_TOKEN }}
54
+ verbose: true
55
+
@@ -0,0 +1,39 @@
1
+ # Broadcast masters and replay captures. These are derived data — regenerate
2
+ # them from the archived transcript with engine/record_cw.py (Core War) or a
3
+ # `ctl replay` capture (chess/xiangqi). They run to hundreds of megabytes.
4
+ *.mp4
5
+ *.mov
6
+ *.m4a
7
+ *.wav
8
+ *.gif
9
+ !docs/assets/*.gif
10
+
11
+ # Python
12
+ __pycache__/
13
+ *.py[cod]
14
+ .pytest_cache/
15
+ .venv/
16
+ venv/
17
+ *.egg-info/
18
+
19
+ # Recorder scratch
20
+ cw-record-*/
21
+ frames/
22
+
23
+ # OS
24
+ .DS_Store
25
+
26
+ # Live runtime (ephemeral — archived into games/ on completion)
27
+ .ai-agent-arcade/
28
+ tasks/
29
+
30
+ # Archived matches (reproducible via `uvx ai-agent-arcade --games N`)
31
+ # Keep curated fixtures: match-009 (README verify + GIF) and match-171
32
+ # (MISTRAL×MUSE-SPARK showcase). Others are local cache.
33
+ # Note: ignore contents, not the dir itself, so ! can re-include fixtures.
34
+ games/chess/match-*/
35
+ !games/chess/match-009/
36
+ !games/chess/match-171/
37
+ !games/chess/match-009/**
38
+ !games/chess/match-171/**
39
+ dist/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam Foreman and Xule Lin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,374 @@
1
+ Metadata-Version: 2.5
2
+ Name: ai-agent-arcade
3
+ Version: 0.1.3
4
+ Summary: A single-pane Textual arcade for referee-verified model chess matches
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: alcf-tokens>=0.4.0
8
+ Requires-Dist: chess>=1.11.2
9
+ Requires-Dist: textual>=6.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest-asyncio>=1; extra == 'dev'
12
+ Requires-Dist: pytest>=9; extra == 'dev'
13
+ Description-Content-Type: text/markdown
14
+
15
+ # AI Agent Arcade
16
+
17
+ Language models play chess against each other in a single terminal window.
18
+ Each model receives the move history, FEN, and legal SAN moves, then picks one
19
+ move without an engine. A replay-verifying referee applies the move and writes
20
+ an inspectable archive.
21
+
22
+ <p align="center">
23
+ <picture>
24
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/arcade-tui-dark.gif">
25
+ <img src="docs/assets/arcade-tui-light.gif" alt="AI Agent Arcade Textual app replaying a verified match: animated dot-field board, match panel, scrollable decisions, and series log" width="900">
26
+ </picture>
27
+ </p>
28
+
29
+ The recording above is the real Textual app replaying archived `match-171` (`MISTRAL-LARGE-4-NVFP4 0-1 MUSE-SPARK-BF16`, 80 plies: Fried Liver \u2192 queen sac \u2192 pawn promotion \u2192 `Qe5#`), captured frame-by-frame through Textual's own `App.export_screenshot()`.
30
+ Regenerate it with:
31
+
32
+ ```bash
33
+ uv run --with pillow python cli/record_tui_gif.py games/chess/match-171 --theme ansi-dark --output docs/assets/arcade-tui-dark.gif --frames 80 --fps 10 --width 900
34
+ uv run --with pillow python cli/record_tui_gif.py games/chess/match-171 --theme ansi-light --output docs/assets/arcade-tui-light.gif --frames 80 --fps 10 --width 900
35
+ ```
36
+
37
+ ## Install and run
38
+
39
+ Requirements: Python 3.11+, [`uv`](https://docs.astral.sh/uv/), and an ALCF
40
+ account with inference access.
41
+
42
+ Available on [TestPyPI](https://test.pypi.org/project/ai-agent-arcade/):
43
+
44
+ ```bash
45
+ uvx --index https://test.pypi.org/simple/ --index-strategy unsafe-best-match --with ai-agent-arcade ai-agent-arcade --games 20
46
+ pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ai-agent-arcade
47
+ ```
48
+
49
+ Or directly from the repository:
50
+
51
+ ```bash
52
+ uvx --from git+https://github.com/saforem2/ai-agent-arcade ai-agent-arcade --games 20
53
+ ```
54
+
55
+ Or from a checkout:
56
+
57
+ ```bash
58
+ git clone https://github.com/saforem2/ai-agent-arcade.git
59
+ cd ai-agent-arcade
60
+ uv run ai-agent-arcade --games 20
61
+ ```
62
+
63
+ Both commands work without Herdr, tmux, or a local inference gateway.
64
+ Pressing `Ctrl-C` stops the runner cleanly. Running the same command again
65
+ detects and resumes an interrupted live game, including a valid staged move.
66
+ To inspect an archive without launching a new series, pass `--view --live-dir
67
+ games/chess/match-049`.
68
+
69
+ ### Reset a game
70
+
71
+ To throw away an interrupted game and start from the opening position:
72
+
73
+ ```bash
74
+ uv run ai-agent-arcade --reset --games 20
75
+ ```
76
+
77
+ Press `R` in the running app to do the same thing without restarting it.
78
+
79
+ Reset never deletes evidence. The partial transcript (moves, decisions, names,
80
+ FEN, runner log) is copied to `<live-dir>/abandoned/<timestamp>/` with a
81
+ `reason.txt` first, and completed archives under `games/` are untouched. The
82
+ orphaned match record is flipped from `live` to `abandoned` so the next game
83
+ is not blocked by `match-NNN is still live`. The headless runner takes the
84
+ same flag:
85
+
86
+ ```bash
87
+ uv run python -m arcade_cli.series --reset --games 20 --live-dir "$ARCADE_LIVE"
88
+ ```
89
+
90
+ ### Configure another matchup
91
+
92
+ Pass a versioned TOML file to use any two OpenAI-compatible endpoints:
93
+
94
+ ```toml
95
+ version = 1
96
+
97
+ [players.one]
98
+ name = "MODEL-A"
99
+ harness = "openai"
100
+ base_url = "http://localhost:8000/v1"
101
+ model = "model-a"
102
+ effort = "medium" # optional
103
+ api_key_env = "MODEL_A_API_KEY" # optional
104
+
105
+ [players.two]
106
+ name = "MODEL-B"
107
+ harness = "openai"
108
+ base_url = "http://localhost:9000/v1"
109
+ model = "model-b"
110
+ # api_key = "..." # optional; prefer api_key_env
111
+ ```
112
+
113
+ ```bash
114
+ uv run ai-agent-arcade --config matchup.toml --games 10
115
+ ```
116
+
117
+ Override every player's reasoning effort for one run without editing the
118
+ file (passed through as `reasoning_effort`):
119
+
120
+ ```bash
121
+ uv run ai-agent-arcade --config matchup.toml --games 10 --effort medium
122
+ ```
123
+
124
+ `base_url` may be an API root ending in `/v1` or the complete
125
+ `/chat/completions` URL. Use `harness = "alcf"` to obtain the cached ALCF
126
+ inference token automatically. API keys are resolved only by the runner and
127
+ are not included in match records or logs.
128
+
129
+ Two ALCF models on the same cluster (e.g. Minerva's `inkling-bf16` vs
130
+ `muse-spark-bf16`) is the same shape with `harness = "alcf"` on both seats:
131
+
132
+ ```toml
133
+ version = 1
134
+
135
+ [players.one]
136
+ name = "INKLING-BF16"
137
+ harness = "alcf"
138
+ base_url = "https://inference-api.alcf.anl.gov/resource_server/minerva/api/v1"
139
+ model = "inkling-bf16"
140
+ effort = "low"
141
+
142
+ [players.two]
143
+ name = "MUSE-SPARK-BF16"
144
+ harness = "alcf"
145
+ base_url = "https://inference-api.alcf.anl.gov/resource_server/minerva/api/v1"
146
+ model = "muse-spark-bf16"
147
+ effort = "low"
148
+ ```
149
+
150
+ The `original` board is rendered by the same dot-field engine used by the
151
+ standalone terminal viewer: identical geometry, influence field, sprites,
152
+ colors, and timing. Textual only hosts the returned frame.
153
+
154
+ ## ALCF authentication
155
+
156
+ AI Agent Arcade reads a cached Globus token through
157
+ [`alcf-tokens`](https://pypi.org/project/alcf-tokens/), the shared ALCF client
158
+ that also backs [`alcf-ai`](https://pypi.org/project/alcf-ai/).
159
+
160
+ ```bash
161
+ uvx alcf-tokens login
162
+ uvx alcf-tokens test-token inference
163
+ ```
164
+
165
+ A healthy token prints `{"ready": true, "error": null}`. Access tokens last 48
166
+ hours and refresh automatically; re-run `login` when refresh expires.
167
+
168
+ Check which models are live before starting a long series:
169
+
170
+ ```bash
171
+ uvx alcf-ai ls-endpoints
172
+ uvx alcf-ai ls-jobs metis
173
+ uvx alcf-ai ls-jobs minerva
174
+ ```
175
+
176
+ Full access requirements and troubleshooting are in the
177
+ [ALCF Inference Endpoints guide](https://docs.alcf.anl.gov/services/inference-endpoints/).
178
+ The token is never written to logs or match archives.
179
+
180
+ ## The TUI
181
+
182
+ The app is one Textual screen with four regions: the board, the match panel,
183
+ the scrollable decision history, and the series log.
184
+
185
+ | Key | Action |
186
+ |---|---|
187
+ | `b` | Switch board style |
188
+ | `r` | Replay the current game with move animations |
189
+ | `R` | Reset: abandon the live game and start a new one |
190
+ | `j` / `k` | Scroll decisions down / up |
191
+ | `g` / `G` | Jump to the newest / oldest decision |
192
+ | `p` / `c` | Pause / continue the runner |
193
+ | `q` | Quit, leaving a live match resumable |
194
+
195
+ ### Board styles
196
+
197
+ `--board-style original` (default) is the animated dot field: squares are
198
+ braille density and the field drifts between two frames.
199
+
200
+ `--board-style compact` is a dense glyph grid that fits a small terminal.
201
+
202
+ <p align="center">
203
+ <img src="docs/assets/thunderdome-compact.png" alt="AI Agent Arcade compact board style" width="900">
204
+ </p>
205
+
206
+ ```bash
207
+ uv run ai-agent-arcade --games 20 --board-style compact
208
+ ```
209
+
210
+ ### Decision history
211
+
212
+ Every move records the chosen SAN, up to three candidates, one sentence of
213
+ self-reported rationale, an expected reply, and token counts. The panel keeps
214
+ the whole game and scrolls with `j`, `k`, `g`, and `G`.
215
+
216
+ This is the model's own summary. It is not a provider's hidden
217
+ chain-of-thought and does not claim to reproduce one.
218
+
219
+ ### Themes
220
+
221
+ The app ships the four Textual themes from
222
+ [`pbs-tui`](https://github.com/saforem2/pbs-tui): `pbs-dark`, `pbs-light`,
223
+ `ansi-dark`, and `ansi-light`. The `ansi-*` pair uses muted terminal-blending
224
+ tones, including a light dot-field board palette — the board paints its own
225
+ background, so it follows the theme instead of staying black.
226
+
227
+ Startup defaults to the `ansi-*` pair and detects the terminal background
228
+ automatically: `COLORFGBG` first, then a direct OSC 11 query to the terminal,
229
+ then dark. Override once with `--theme` or persistently with `ARCADE_THEME`:
230
+
231
+ ```bash
232
+ uv run ai-agent-arcade --games 20 --theme ansi-light
233
+ ARCADE_THEME=pbs-dark uv run ai-agent-arcade --games 20
234
+ ```
235
+
236
+ `Ctrl-P` still switches themes at runtime, and the board repaints with it.
237
+
238
+ ### Series standings
239
+
240
+ When the series ends, the match panel grows a `STANDINGS` block:
241
+
242
+ ```text
243
+ STANDINGS
244
+ INKLING-BF16 won 6/10 games!!! (1 draw)
245
+ ```
246
+
247
+ The same line is stored as `standings` in `series-summary.json`, appended as
248
+ `STANDINGS: …` after `SERIES COMPLETE: …` in `series-runner.log`, and printed
249
+ to stdout by the headless runner — so it shows in the TUI, the log tail, and
250
+ the terminal alike. Ties and all-drawn series get their own phrasing
251
+ (`Dead even: …`, `All 10 games drawn — dead even`).
252
+
253
+ ### Turn clock
254
+
255
+ While a model is thinking, the match panel shows a live clock:
256
+
257
+ ```text
258
+ status: running
259
+ thinking: MUSE-SPARK-BF16 42s
260
+ ```
261
+
262
+ Every finished turn is also timed in `series-runner.log`
263
+ (`ply 68: MUSE-SPARK-BF16 Rd4+ (38s)`) and each `decision_log.jsonl` row
264
+ carries `thinking_seconds`. Display only — no time limits, no forfeits;
265
+ a slow model just reads as a big number instead of a frozen screen.
266
+
267
+ ## Models
268
+
269
+ | Seat | ALCF cluster | Model |
270
+ |---|---|---|
271
+ | GPT-OSS | Metis | `gpt-oss-120b` |
272
+ | Inkling | Minerva | `inkling-bf16` |
273
+
274
+ Colors alternate every game across the series.
275
+
276
+ ## Run headless
277
+
278
+ ```bash
279
+ export ARCADE_LIVE=/tmp/alcf-chess-20
280
+ uv run python -m arcade_cli.series --games 20 --live-dir "$ARCADE_LIVE"
281
+ ```
282
+
283
+ Resume after a completed game:
284
+
285
+ ```bash
286
+ uv run python -m arcade_cli.series \
287
+ --games 20 \
288
+ --start-index 12 \
289
+ --live-dir "$ARCADE_LIVE"
290
+ ```
291
+
292
+ The runner writes:
293
+
294
+ - `$ARCADE_LIVE/series-runner.log`
295
+ - `$ARCADE_LIVE/series-summary.json`
296
+ - `$ARCADE_LIVE/decision_log.jsonl`
297
+ - `games/chess/match-NNN/` for every archived game
298
+
299
+ ## Verify an archive
300
+
301
+ Every move is replayed from the initial position and compared with the stored
302
+ FEN.
303
+
304
+ ```bash
305
+ ARCADE_LIVE=games/chess/match-009 \
306
+ uv run --quiet --with chess python engine/ref.py verify
307
+ ```
308
+
309
+ ```text
310
+ OK verified 11 plies | r1bqkb1r/pppppQp1/2n4p/6N1/8/8/PPPP1KPP/RNB2B1R b kq - 0 6
311
+ ```
312
+
313
+ ## How it works
314
+
315
+ ```text
316
+ model A ─┐
317
+ ├─> SAN move ─> pending.txt ─> referee ─> moves.txt + fen.txt
318
+ model B ─┘ │
319
+ ├─> board
320
+ ├─> match panel
321
+ ├─> decision history
322
+ └─> archived evidence
323
+ ```
324
+
325
+ Models never edit game state. They return SAN chosen from the referee's legal
326
+ move list. `ref.py` checks seat identity, reconstructs the position from ply
327
+ one, validates the move, appends it, and records terminal results including
328
+ claimable threefold and 50-move draws.
329
+
330
+ ## Replays and media
331
+
332
+ The GIF and stills are recomputed from an archived transcript, not screen-
333
+ recorded.
334
+
335
+ <p align="center">
336
+ <picture>
337
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/alcf-chess-replay.gif">
338
+ <img src="docs/assets/alcf-chess-replay-light.gif" alt="Animated replay of MISTRAL-LARGE-4-NVFP4 vs MUSE-SPARK-BF16 (match-171, 80 plies: Fried Liver \u2192 Qe5#)" width="520">
339
+ </picture>
340
+ </p>
341
+
342
+ ```bash
343
+ uv run --with pillow python cli/render_readme_media.py games/chess/match-171
344
+ uv run --with pillow python cli/render_readme_media.py games/chess/match-171 --palette light --suffix "-light"
345
+ ```
346
+
347
+ ## Tests
348
+
349
+ ```bash
350
+ uv run --with pytest --with pytest-asyncio python -m pytest cli/tests -q
351
+ ```
352
+
353
+ The full engine suite:
354
+
355
+ ```bash
356
+ uv run --with pytest --with chess --directory cli \
357
+ python -m pytest ../engine/ tests/ -q
358
+ ```
359
+
360
+ ## Acknowledgements
361
+
362
+ This project was inspired by Xule Lin's
363
+ [`linxule/arcade`](https://github.com/linxule/arcade), which introduced the
364
+ file-bus arena, replay-verifying referees, dot-field terminal renderers, and
365
+ agent-hosted match format used here. The original code is MIT licensed; its
366
+ copyright and license notice remain in [`LICENSE`](LICENSE).
367
+
368
+ ALCF model inference is provided through Argonne Leadership Computing Facility
369
+ services. [Herdr](https://herdr.dev) supplies the optional multi-pane terminal
370
+ workspace.
371
+
372
+ ## License
373
+
374
+ MIT. See [`LICENSE`](LICENSE).