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.
- ai_agent_arcade-0.1.3/.github/workflows/publish.yml +55 -0
- ai_agent_arcade-0.1.3/.gitignore +39 -0
- ai_agent_arcade-0.1.3/LICENSE +21 -0
- ai_agent_arcade-0.1.3/PKG-INFO +374 -0
- ai_agent_arcade-0.1.3/README.md +360 -0
- ai_agent_arcade-0.1.3/cli/pyproject.toml +24 -0
- ai_agent_arcade-0.1.3/cli/record_tui_gif.py +140 -0
- ai_agent_arcade-0.1.3/cli/render_readme_media.py +130 -0
- ai_agent_arcade-0.1.3/cli/run_alcf_series.py +8 -0
- ai_agent_arcade-0.1.3/cli/src/arcade_cli/__init__.py +0 -0
- ai_agent_arcade-0.1.3/cli/src/arcade_cli/arcade_app.py +738 -0
- ai_agent_arcade-0.1.3/cli/src/arcade_cli/main.py +1047 -0
- ai_agent_arcade-0.1.3/cli/src/arcade_cli/series.py +809 -0
- ai_agent_arcade-0.1.3/cli/src/arcade_cli/themes.py +240 -0
- ai_agent_arcade-0.1.3/cli/src/arcade_cli/thunderdome.py +14 -0
- ai_agent_arcade-0.1.3/cli/tests/test_alcf_series.py +658 -0
- ai_agent_arcade-0.1.3/cli/tests/test_cli.py +1654 -0
- ai_agent_arcade-0.1.3/cli/tests/test_thunderdome.py +568 -0
- ai_agent_arcade-0.1.3/cli/uv.lock +86 -0
- ai_agent_arcade-0.1.3/configs/muse-mistral.toml +15 -0
- ai_agent_arcade-0.1.3/configs/muse.toml +15 -0
- ai_agent_arcade-0.1.3/docs/arcade-tui.tape +39 -0
- ai_agent_arcade-0.1.3/docs/cli-brief.md +127 -0
- ai_agent_arcade-0.1.3/docs/design-history.md +632 -0
- ai_agent_arcade-0.1.3/docs/game6-briefs.md +73 -0
- ai_agent_arcade-0.1.3/docs/match-state-spec.md +349 -0
- ai_agent_arcade-0.1.3/docs/retro-game5/retro-codex.md +26 -0
- ai_agent_arcade-0.1.3/docs/retro-game5/retro-grok.md +81 -0
- ai_agent_arcade-0.1.3/docs/retro-game5/retro-kimi.md +22 -0
- ai_agent_arcade-0.1.3/docs/retro-game6/retro6-codex.md +33 -0
- ai_agent_arcade-0.1.3/docs/retro-game6/retro6-gemini.md +19 -0
- ai_agent_arcade-0.1.3/docs/retro-game6/retro6-grok.md +119 -0
- ai_agent_arcade-0.1.3/docs/seat-feedback-game5.md +86 -0
- ai_agent_arcade-0.1.3/docs/seat-feedback-game6.md +80 -0
- ai_agent_arcade-0.1.3/docs/xiangqi-design-brief.md +342 -0
- ai_agent_arcade-0.1.3/engine/FACILITATOR.md +424 -0
- ai_agent_arcade-0.1.3/engine/FACILITATOR_CW.md +560 -0
- ai_agent_arcade-0.1.3/engine/FACILITATOR_XQ.md +414 -0
- ai_agent_arcade-0.1.3/engine/ansi_png.py +217 -0
- ai_agent_arcade-0.1.3/engine/approver.sh +22 -0
- ai_agent_arcade-0.1.3/engine/arcade_panel.py +263 -0
- ai_agent_arcade-0.1.3/engine/chat.py +151 -0
- ai_agent_arcade-0.1.3/engine/chat_tui.py +161 -0
- ai_agent_arcade-0.1.3/engine/clock.py +25 -0
- ai_agent_arcade-0.1.3/engine/corewar.py +1286 -0
- ai_agent_arcade-0.1.3/engine/director_keeper.sh +42 -0
- ai_agent_arcade-0.1.3/engine/dna_cw.py +572 -0
- ai_agent_arcade-0.1.3/engine/donut.py +33 -0
- ai_agent_arcade-0.1.3/engine/eval.py +216 -0
- ai_agent_arcade-0.1.3/engine/game.sh +68 -0
- ai_agent_arcade-0.1.3/engine/game_cli.py +444 -0
- ai_agent_arcade-0.1.3/engine/game_cw.sh +89 -0
- ai_agent_arcade-0.1.3/engine/game_cw_cli.py +411 -0
- ai_agent_arcade-0.1.3/engine/game_xq.sh +69 -0
- ai_agent_arcade-0.1.3/engine/game_xq_cli.py +498 -0
- ai_agent_arcade-0.1.3/engine/keeper.sh +10 -0
- ai_agent_arcade-0.1.3/engine/life.py +21 -0
- ai_agent_arcade-0.1.3/engine/panel_tui.py +187 -0
- ai_agent_arcade-0.1.3/engine/record_cw.py +547 -0
- ai_agent_arcade-0.1.3/engine/ref.py +343 -0
- ai_agent_arcade-0.1.3/engine/ref_cw.py +684 -0
- ai_agent_arcade-0.1.3/engine/ref_xq.py +383 -0
- ai_agent_arcade-0.1.3/engine/relay1-legacy.sh +53 -0
- ai_agent_arcade-0.1.3/engine/relay2.sh +68 -0
- ai_agent_arcade-0.1.3/engine/results.seed.txt +2 -0
- ai_agent_arcade-0.1.3/engine/score_cw.py +1456 -0
- ai_agent_arcade-0.1.3/engine/test_chat_tui.py +42 -0
- ai_agent_arcade-0.1.3/engine/test_corewar.py +764 -0
- ai_agent_arcade-0.1.3/engine/test_dna_cw.py +409 -0
- ai_agent_arcade-0.1.3/engine/test_eval.py +102 -0
- ai_agent_arcade-0.1.3/engine/test_game_cli.py +397 -0
- ai_agent_arcade-0.1.3/engine/test_game_cw_cli.py +565 -0
- ai_agent_arcade-0.1.3/engine/test_game_xq_cli.py +377 -0
- ai_agent_arcade-0.1.3/engine/test_record_cw.py +491 -0
- ai_agent_arcade-0.1.3/engine/test_ref_cw.py +661 -0
- ai_agent_arcade-0.1.3/engine/test_ref_xq.py +440 -0
- ai_agent_arcade-0.1.3/engine/test_score_cw.py +905 -0
- ai_agent_arcade-0.1.3/engine/test_shell_wrappers.py +312 -0
- ai_agent_arcade-0.1.3/engine/test_tui_corewar.py +2339 -0
- ai_agent_arcade-0.1.3/engine/test_tui_dots.py +322 -0
- ai_agent_arcade-0.1.3/engine/test_tui_xiangqi.py +983 -0
- ai_agent_arcade-0.1.3/engine/test_xiangqi.py +340 -0
- ai_agent_arcade-0.1.3/engine/tui.py +551 -0
- ai_agent_arcade-0.1.3/engine/tui_corewar.py +2217 -0
- ai_agent_arcade-0.1.3/engine/tui_dots.py +975 -0
- ai_agent_arcade-0.1.3/engine/tui_xiangqi.py +1387 -0
- ai_agent_arcade-0.1.3/engine/xiangqi.py +561 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/banner.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/chat.log +6 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/fen.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/host.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/match.json +23 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/moves.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/names.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/result.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/results.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/roles.txt +6 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/seats.txt +2 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/series.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-009/stage_log.txt +11 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/banner.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/chat.log +3 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/decision_log.jsonl +80 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/fen.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/host.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/match.json +23 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/moves.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/names.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/result.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/results.txt +93 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/roles.txt +6 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/seats.txt +2 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/series.txt +1 -0
- ai_agent_arcade-0.1.3/games/chess/match-171/stage_log.txt +80 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/banner.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/battle.json +45 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/host.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/match.json +23 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/moves.txt +8 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/names.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/result.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/results.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/roles.txt +6 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/seats.txt +2 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/series.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/stage_log.txt +3 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/warriors/A.red +18 -0
- ai_agent_arcade-0.1.3/games/corewar/match-001/warriors/B.red +29 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/banner.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/battle.json +45 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/host.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/match.json +23 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/moves.txt +8 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/names.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/result.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/results.txt +2 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/roles.txt +6 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/seats.txt +2 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/series.txt +1 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/stage_log.txt +3 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/warriors/A.red +21 -0
- ai_agent_arcade-0.1.3/games/corewar/match-002/warriors/B.red +34 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/banner.txt +1 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/director.txt +1 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/fen.txt +1 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/match.json +23 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/moves.txt +1 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/names.txt +1 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/result.txt +1 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/results.txt +1 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/roles.txt +6 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/seats.txt +2 -0
- ai_agent_arcade-0.1.3/games/xiangqi/match-001/series.txt +1 -0
- ai_agent_arcade-0.1.3/pyproject.toml +40 -0
- 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).
|