citar 0.1.0__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.
- citar-0.1.0/.gitignore +69 -0
- citar-0.1.0/CHANGELOG.md +115 -0
- citar-0.1.0/DESIGN.md +210 -0
- citar-0.1.0/LICENSE +373 -0
- citar-0.1.0/NOTICE.md +54 -0
- citar-0.1.0/PKG-INFO +271 -0
- citar-0.1.0/README.md +187 -0
- citar-0.1.0/alembic.ini +52 -0
- citar-0.1.0/citar/__init__.py +21 -0
- citar-0.1.0/citar/agents/__init__.py +20 -0
- citar-0.1.0/citar/agents/bot_agent.py +52 -0
- citar-0.1.0/citar/agents/llm_agent.py +469 -0
- citar-0.1.0/citar/agents/mcp_server.py +116 -0
- citar-0.1.0/citar/agents/prompts.py +105 -0
- citar-0.1.0/citar/agents/providers/__init__.py +19 -0
- citar-0.1.0/citar/agents/providers/anthropic_provider.py +102 -0
- citar-0.1.0/citar/agents/providers/base.py +76 -0
- citar-0.1.0/citar/agents/providers/dryrun.py +97 -0
- citar-0.1.0/citar/agents/providers/openai_provider.py +289 -0
- citar-0.1.0/citar/agents/providers/worker_provider.py +129 -0
- citar-0.1.0/citar/aggregate.py +171 -0
- citar-0.1.0/citar/auth/__init__.py +25 -0
- citar-0.1.0/citar/auth/access.py +396 -0
- citar-0.1.0/citar/auth/accounts.py +477 -0
- citar-0.1.0/citar/auth/audit.py +87 -0
- citar-0.1.0/citar/auth/captcha.py +98 -0
- citar-0.1.0/citar/auth/deps.py +189 -0
- citar-0.1.0/citar/auth/invites.py +149 -0
- citar-0.1.0/citar/auth/mailer.py +209 -0
- citar-0.1.0/citar/auth/oauth.py +308 -0
- citar-0.1.0/citar/auth/passwords.py +113 -0
- citar-0.1.0/citar/auth/policy.py +191 -0
- citar-0.1.0/citar/auth/ratelimit.py +207 -0
- citar-0.1.0/citar/auth/sessions.py +212 -0
- citar-0.1.0/citar/auth/tokens.py +70 -0
- citar-0.1.0/citar/balance.py +332 -0
- citar-0.1.0/citar/bench.py +162 -0
- citar-0.1.0/citar/bots/__init__.py +15 -0
- citar-0.1.0/citar/bots/basic.py +1894 -0
- citar-0.1.0/citar/bots/frozen_0de641a1.py +1325 -0
- citar-0.1.0/citar/bots/frozen_27c13cf7.py +1596 -0
- citar-0.1.0/citar/bots/frozen_31ef9854.py +1781 -0
- citar-0.1.0/citar/bots/frozen_44984a2b.py +1756 -0
- citar-0.1.0/citar/bots/frozen_4ce67344.py +1301 -0
- citar-0.1.0/citar/bots/frozen_52aec52c.py +1536 -0
- citar-0.1.0/citar/bots/frozen_7149efb1.py +1705 -0
- citar-0.1.0/citar/bots/frozen_8f953b89.py +1669 -0
- citar-0.1.0/citar/bots/frozen_b9e1b21c.py +1707 -0
- citar-0.1.0/citar/bots/frozen_c45e5ba2.py +1725 -0
- citar-0.1.0/citar/cli.py +145 -0
- citar-0.1.0/citar/costing.py +374 -0
- citar-0.1.0/citar/data/collectors/collect_hardware.ps1 +87 -0
- citar-0.1.0/citar/data/collectors/collect_hardware.sh +149 -0
- citar-0.1.0/citar/data/custom/nations.json +21 -0
- citar-0.1.0/citar/data/game.json +77 -0
- citar-0.1.0/citar/data/ruleset/NOTICE.md +10 -0
- citar-0.1.0/citar/data/ruleset/beliefs.json +461 -0
- citar-0.1.0/citar/data/ruleset/buildings.json +1792 -0
- citar-0.1.0/citar/data/ruleset/city_state_types.json +67 -0
- citar-0.1.0/citar/data/ruleset/difficulties.json +281 -0
- citar-0.1.0/citar/data/ruleset/eras.json +419 -0
- citar-0.1.0/citar/data/ruleset/global_uniques.json +12 -0
- citar-0.1.0/citar/data/ruleset/improvements.json +460 -0
- citar-0.1.0/citar/data/ruleset/nations.json +2435 -0
- citar-0.1.0/citar/data/ruleset/personalities.json +1304 -0
- citar-0.1.0/citar/data/ruleset/policies.json +968 -0
- citar-0.1.0/citar/data/ruleset/promotions.json +1372 -0
- citar-0.1.0/citar/data/ruleset/quests.json +183 -0
- citar-0.1.0/citar/data/ruleset/religions.json +13 -0
- citar-0.1.0/citar/data/ruleset/resources.json +805 -0
- citar-0.1.0/citar/data/ruleset/ruins.json +112 -0
- citar-0.1.0/citar/data/ruleset/specialists.json +34 -0
- citar-0.1.0/citar/data/ruleset/speeds.json +210 -0
- citar-0.1.0/citar/data/ruleset/techs.json +1155 -0
- citar-0.1.0/citar/data/ruleset/terrains.json +665 -0
- citar-0.1.0/citar/data/ruleset/unit_types.json +166 -0
- citar-0.1.0/citar/data/ruleset/units.json +2146 -0
- citar-0.1.0/citar/data/ruleset/victories.json +49 -0
- citar-0.1.0/citar/db/__init__.py +118 -0
- citar-0.1.0/citar/db/models.py +547 -0
- citar-0.1.0/citar/doctor.py +357 -0
- citar-0.1.0/citar/engine/__init__.py +30 -0
- citar-0.1.0/citar/engine/actions.py +225 -0
- citar-0.1.0/citar/engine/automation.py +454 -0
- citar-0.1.0/citar/engine/barbarians.py +493 -0
- citar-0.1.0/citar/engine/briefing.py +660 -0
- citar-0.1.0/citar/engine/cities.py +2387 -0
- citar-0.1.0/citar/engine/city_states.py +1320 -0
- citar-0.1.0/citar/engine/combat.py +1219 -0
- citar-0.1.0/citar/engine/conquest.py +288 -0
- citar-0.1.0/citar/engine/diplomacy.py +831 -0
- citar-0.1.0/citar/engine/economy.py +745 -0
- citar-0.1.0/citar/engine/espionage.py +515 -0
- citar-0.1.0/citar/engine/game.py +826 -0
- citar-0.1.0/citar/engine/great_people.py +369 -0
- citar-0.1.0/citar/engine/hexmap.py +152 -0
- citar-0.1.0/citar/engine/mapgen.py +1222 -0
- citar-0.1.0/citar/engine/maps.py +352 -0
- citar-0.1.0/citar/engine/movement.py +663 -0
- citar-0.1.0/citar/engine/policies.py +169 -0
- citar-0.1.0/citar/engine/religion.py +796 -0
- citar-0.1.0/citar/engine/research.py +367 -0
- citar-0.1.0/citar/engine/ruins.py +67 -0
- citar-0.1.0/citar/engine/rules.py +341 -0
- citar-0.1.0/citar/engine/scenario.py +641 -0
- citar-0.1.0/citar/engine/state.py +325 -0
- citar-0.1.0/citar/engine/tiles.py +391 -0
- citar-0.1.0/citar/engine/tools.py +1068 -0
- citar-0.1.0/citar/engine/triggers.py +374 -0
- citar-0.1.0/citar/engine/turns.py +200 -0
- citar-0.1.0/citar/engine/unique_types.py +640 -0
- citar-0.1.0/citar/engine/uniques.py +1083 -0
- citar-0.1.0/citar/engine/units.py +737 -0
- citar-0.1.0/citar/engine/victory.py +485 -0
- citar-0.1.0/citar/engine/views.py +757 -0
- citar-0.1.0/citar/engine/visibility.py +243 -0
- citar-0.1.0/citar/engine/workers.py +640 -0
- citar-0.1.0/citar/fsutil.py +35 -0
- citar-0.1.0/citar/hwinfo.py +411 -0
- citar-0.1.0/citar/keystore.py +228 -0
- citar-0.1.0/citar/lab.py +1007 -0
- citar-0.1.0/citar/migrations/env.py +74 -0
- citar-0.1.0/citar/migrations/script.py.mako +26 -0
- citar-0.1.0/citar/migrations/versions/20260920_0939_initial_schema_accounts_servers_sharing.py +427 -0
- citar-0.1.0/citar/migrations/versions/20260920_1050_user_seat_limit_game_and_report_.py +39 -0
- citar-0.1.0/citar/paths.py +236 -0
- citar-0.1.0/citar/pool/__init__.py +344 -0
- citar-0.1.0/citar/pool/admission.py +298 -0
- citar-0.1.0/citar/pool/budgets.py +306 -0
- citar-0.1.0/citar/pool/windows.py +327 -0
- citar-0.1.0/citar/probes.py +646 -0
- citar-0.1.0/citar/reports/__init__.py +329 -0
- citar-0.1.0/citar/reports/analysis.py +229 -0
- citar-0.1.0/citar/reports/charts.py +359 -0
- citar-0.1.0/citar/reports/data.py +321 -0
- citar-0.1.0/citar/reports/render.py +679 -0
- citar-0.1.0/citar/server/__init__.py +26 -0
- citar-0.1.0/citar/server/__main__.py +60 -0
- citar-0.1.0/citar/server/admin_api.py +367 -0
- citar-0.1.0/citar/server/admin_cli.py +432 -0
- citar-0.1.0/citar/server/app.py +1613 -0
- citar-0.1.0/citar/server/auth_api.py +666 -0
- citar-0.1.0/citar/server/benchmarks.py +959 -0
- citar-0.1.0/citar/server/boot.py +167 -0
- citar-0.1.0/citar/server/lmstudio.py +113 -0
- citar-0.1.0/citar/server/metrics.py +245 -0
- citar-0.1.0/citar/server/ownership.py +192 -0
- citar-0.1.0/citar/server/pool_api.py +524 -0
- citar-0.1.0/citar/server/scoring.py +212 -0
- citar-0.1.0/citar/server/session.py +710 -0
- citar-0.1.0/citar/server/setup_api.py +332 -0
- citar-0.1.0/citar/server/share_api.py +263 -0
- citar-0.1.0/citar/server/workers.py +361 -0
- citar-0.1.0/citar/servers.py +1271 -0
- citar-0.1.0/citar/settings.py +360 -0
- citar-0.1.0/citar/sim.py +75 -0
- citar-0.1.0/citar/usage.py +448 -0
- citar-0.1.0/citar/web/index.html +16 -0
- citar-0.1.0/citar/web/js/account.js +240 -0
- citar-0.1.0/citar/web/js/api.js +236 -0
- citar-0.1.0/citar/web/js/app.js +116 -0
- citar-0.1.0/citar/web/js/auth.js +355 -0
- citar-0.1.0/citar/web/js/benchmarks.js +429 -0
- citar-0.1.0/citar/web/js/console.js +163 -0
- citar-0.1.0/citar/web/js/editor.js +630 -0
- citar-0.1.0/citar/web/js/game.js +911 -0
- citar-0.1.0/citar/web/js/hex.js +59 -0
- citar-0.1.0/citar/web/js/lab.js +154 -0
- citar-0.1.0/citar/web/js/landing.js +168 -0
- citar-0.1.0/citar/web/js/lobby.js +422 -0
- citar-0.1.0/citar/web/js/metrics.js +178 -0
- citar-0.1.0/citar/web/js/models.js +73 -0
- citar-0.1.0/citar/web/js/nav.js +66 -0
- citar-0.1.0/citar/web/js/panels.js +832 -0
- citar-0.1.0/citar/web/js/pool.js +517 -0
- citar-0.1.0/citar/web/js/probes.js +319 -0
- citar-0.1.0/citar/web/js/render.js +763 -0
- citar-0.1.0/citar/web/js/replay.js +216 -0
- citar-0.1.0/citar/web/js/reports.js +221 -0
- citar-0.1.0/citar/web/js/scenario.js +458 -0
- citar-0.1.0/citar/web/js/servers.js +759 -0
- citar-0.1.0/citar/web/js/setup.js +224 -0
- citar-0.1.0/citar/web/js/util.js +113 -0
- citar-0.1.0/citar/web/style.css +464 -0
- citar-0.1.0/citar/wizard/__init__.py +27 -0
- citar-0.1.0/citar/wizard/cli.py +134 -0
- citar-0.1.0/citar/wizard/detect.py +215 -0
- citar-0.1.0/citar/wizard/local.py +276 -0
- citar-0.1.0/citar/wizard/prompts.py +191 -0
- citar-0.1.0/citar/wizard/server.py +550 -0
- citar-0.1.0/citar/wizard/worker.py +211 -0
- citar-0.1.0/citar/worker/__init__.py +11 -0
- citar-0.1.0/citar/worker/__main__.py +183 -0
- citar-0.1.0/citar/worker/agent.py +355 -0
- citar-0.1.0/citar/worker/protocol.py +224 -0
- citar-0.1.0/deploy/Caddyfile +33 -0
- citar-0.1.0/deploy/citar-backup.service +11 -0
- citar-0.1.0/deploy/citar-backup.sh +93 -0
- citar-0.1.0/deploy/citar-backup.timer +12 -0
- citar-0.1.0/deploy/citar-secrets.sh +159 -0
- citar-0.1.0/deploy/citar.service +64 -0
- citar-0.1.0/deploy/nginx-citar.conf +69 -0
- citar-0.1.0/docs/AI_PLAYERS.md +159 -0
- citar-0.1.0/docs/API.md +155 -0
- citar-0.1.0/docs/ARCHITECTURE.md +199 -0
- citar-0.1.0/docs/BENCHMARKS.md +120 -0
- citar-0.1.0/docs/BOTS.md +116 -0
- citar-0.1.0/docs/CONFIGURATION.md +179 -0
- citar-0.1.0/docs/FAQ.md +143 -0
- citar-0.1.0/docs/INSTALL.md +225 -0
- citar-0.1.0/docs/MODDING.md +157 -0
- citar-0.1.0/docs/PLAYING.md +102 -0
- citar-0.1.0/docs/QUICKSTART.md +147 -0
- citar-0.1.0/docs/REPORTS.md +146 -0
- citar-0.1.0/docs/SCENARIOS.md +107 -0
- citar-0.1.0/docs/TROUBLESHOOTING.md +159 -0
- citar-0.1.0/docs/assets/README.md +35 -0
- citar-0.1.0/docs/assets/icon.png +0 -0
- citar-0.1.0/docs/index.md +69 -0
- citar-0.1.0/docs/reference.md +109 -0
- citar-0.1.0/docs/research/BOT_TUNING.md +185 -0
- citar-0.1.0/docs/server/ACCOUNTS.md +276 -0
- citar-0.1.0/docs/server/DEPLOY.md +184 -0
- citar-0.1.0/docs/server/OAUTH.md +133 -0
- citar-0.1.0/docs/server/RUNBOOK.md +252 -0
- citar-0.1.0/docs/server/VPS.md +128 -0
- citar-0.1.0/docs/server/WORKERS.md +121 -0
- citar-0.1.0/installer/citar.ico +0 -0
- citar-0.1.0/installer/citar.iss +169 -0
- citar-0.1.0/installer/citar.spec +145 -0
- citar-0.1.0/installer/cli_entry.py +12 -0
- citar-0.1.0/installer/launcher.py +130 -0
- citar-0.1.0/installer/make_icon.py +91 -0
- citar-0.1.0/installer/welcome.txt +31 -0
- citar-0.1.0/pyproject.toml +202 -0
- citar-0.1.0/scripts/audit_routes.py +230 -0
- citar-0.1.0/scripts/build_wiki.py +201 -0
- citar-0.1.0/scripts/check_links.py +121 -0
- citar-0.1.0/scripts/check_refs.py +49 -0
- citar-0.1.0/scripts/check_uniques.py +38 -0
- citar-0.1.0/scripts/dev_server.py +82 -0
- citar-0.1.0/scripts/docstring_coverage.py +115 -0
- citar-0.1.0/scripts/gen_unique_types.py +45 -0
- citar-0.1.0/scripts/import_unciv.py +199 -0
- citar-0.1.0/scripts/release_checksums.py +149 -0
- citar-0.1.0/scripts/sync_requirements.py +75 -0
- citar-0.1.0/scripts/unciv_json.py +46 -0
- citar-0.1.0/scripts/vps_create_deploy_user.sh +117 -0
- citar-0.1.0/scripts/vps_harden.sh +163 -0
- citar-0.1.0/scripts/vps_harden_stage2.sh +88 -0
- citar-0.1.0/scripts/vps_recon.sh +166 -0
- citar-0.1.0/scripts/vps_recon_paste.sh +129 -0
- citar-0.1.0/tests/__init__.py +42 -0
- citar-0.1.0/tests/test_access.py +269 -0
- citar-0.1.0/tests/test_auth.py +445 -0
- citar-0.1.0/tests/test_benchmarks.py +261 -0
- citar-0.1.0/tests/test_bots.py +116 -0
- citar-0.1.0/tests/test_costs.py +186 -0
- citar-0.1.0/tests/test_editor.py +179 -0
- citar-0.1.0/tests/test_engine.py +319 -0
- citar-0.1.0/tests/test_mechanics.py +403 -0
- citar-0.1.0/tests/test_metrics.py +46 -0
- citar-0.1.0/tests/test_paths.py +173 -0
- citar-0.1.0/tests/test_pool.py +382 -0
- citar-0.1.0/tests/test_providers.py +193 -0
- citar-0.1.0/tests/test_session.py +218 -0
- citar-0.1.0/tests/test_setup.py +254 -0
- citar-0.1.0/tests/test_sharing.py +258 -0
- citar-0.1.0/tests/test_ui_support.py +246 -0
- citar-0.1.0/tests/test_worker.py +412 -0
citar-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------- Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg-info/
|
|
9
|
+
.eggs/
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
.tox/
|
|
14
|
+
.nox/
|
|
15
|
+
.coverage
|
|
16
|
+
.coverage.*
|
|
17
|
+
htmlcov/
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.mypy_cache/
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
|
|
22
|
+
# --------------------------------------------------------------------------- CITAR state
|
|
23
|
+
# Everything CITAR writes at runtime. In a source checkout these live beside the code; in an
|
|
24
|
+
# installed copy they live in the per-user data directory (see citar/paths.py).
|
|
25
|
+
saves/
|
|
26
|
+
benchmarks/runs/
|
|
27
|
+
benchmarks/suites/*.json
|
|
28
|
+
!benchmarks/suites/example_*.json
|
|
29
|
+
config/servers.json
|
|
30
|
+
config/*.local.json
|
|
31
|
+
*.citar
|
|
32
|
+
*.citarscn
|
|
33
|
+
*.db
|
|
34
|
+
*.db-wal
|
|
35
|
+
*.db-shm
|
|
36
|
+
|
|
37
|
+
# --------------------------------------------------------------------------- secrets
|
|
38
|
+
citar.env
|
|
39
|
+
.env
|
|
40
|
+
.env.*
|
|
41
|
+
# The example files are the documentation for what goes in a real one, and `.env.*` above would
|
|
42
|
+
# otherwise swallow them - which it did, leaving docker-compose.yml referring to a file that was
|
|
43
|
+
# not in the repository.
|
|
44
|
+
!.env.example
|
|
45
|
+
!.env.docker.example
|
|
46
|
+
!*.example
|
|
47
|
+
*.pem
|
|
48
|
+
*.key
|
|
49
|
+
!**/testdata/*.key
|
|
50
|
+
|
|
51
|
+
# --------------------------------------------------------------------------- operator notes
|
|
52
|
+
# Real deployment notes, hostnames, and the live server registry. Never committed: this is the
|
|
53
|
+
# stuff that identifies a specific machine and the people running it.
|
|
54
|
+
ops/
|
|
55
|
+
|
|
56
|
+
# --------------------------------------------------------------------------- build artefacts
|
|
57
|
+
installer/output/
|
|
58
|
+
installer/build/
|
|
59
|
+
*.spec.bak
|
|
60
|
+
site/
|
|
61
|
+
wiki/.git/
|
|
62
|
+
|
|
63
|
+
# --------------------------------------------------------------------------- editors / OS
|
|
64
|
+
.vscode/
|
|
65
|
+
.idea/
|
|
66
|
+
*.swp
|
|
67
|
+
.DS_Store
|
|
68
|
+
Thumbs.db
|
|
69
|
+
desktop.ini
|
citar-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to CITAR. The format follows [Keep a Changelog](https://keepachangelog.com), and
|
|
4
|
+
versions follow [semantic versioning](https://semver.org) — with the pre-1.0 caveat that tool names
|
|
5
|
+
and arguments may still change between minor versions, and the release notes will say when they do.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
Nothing yet.
|
|
10
|
+
|
|
11
|
+
## [0.1.0] - 2026-09-20
|
|
12
|
+
|
|
13
|
+
The first public release. CITAR has existed for a while as a private project; this is the version
|
|
14
|
+
somebody else can install.
|
|
15
|
+
|
|
16
|
+
### The game
|
|
17
|
+
|
|
18
|
+
- A Civilization V-style 4X game with the rules, numbers and much of the logic derived from
|
|
19
|
+
[UnCiv](https://github.com/yairm210/Unciv)'s "Civ V – Gods & Kings" ruleset: all nine eras, 35
|
|
20
|
+
civilizations, 40 city-states, religion, social policies, wonders, great people and golden ages,
|
|
21
|
+
espionage, city-state and diplomatic victory, and nuclear weapons.
|
|
22
|
+
- Map sizes from Duel to Gargantuan (160×100, 24 civilizations), four game speeds, eight
|
|
23
|
+
difficulty levels, five victory conditions.
|
|
24
|
+
- A browser client with no build step: canvas map, city management, tech tree, policies, religion,
|
|
25
|
+
espionage, a diplomacy deal builder, and a turn-by-turn recap with every AI's recorded reasoning.
|
|
26
|
+
|
|
27
|
+
### AI players
|
|
28
|
+
|
|
29
|
+
- **LLM seats** driven by the server, through the Anthropic API or any OpenAI-compatible endpoint
|
|
30
|
+
(LM Studio, Ollama, llama.cpp, vLLM).
|
|
31
|
+
- **MCP seats** for external agents — Claude Code, Claude Desktop, anything that speaks MCP —
|
|
32
|
+
including `wait_for_turn`, which blocks instead of polling.
|
|
33
|
+
- **Scripted bots** that research by need, pick buildings by simulating the city with each
|
|
34
|
+
candidate, run religion and espionage, and wage war with siege units and rally points.
|
|
35
|
+
- Guard rails for weaker models: repeated actions refused, unchanged queries collapsed, tool calls
|
|
36
|
+
written as text recovered, per-turn progress notes, and an ALERTS section in every briefing.
|
|
37
|
+
|
|
38
|
+
### Research tooling
|
|
39
|
+
|
|
40
|
+
- **Benchmarks**: suites of models against seeded maps, sequential or parallel, with restricted
|
|
41
|
+
hours per server, and runs that survive a restart by reloading games from their autosaves.
|
|
42
|
+
- **Model scoring**: performance against the strongest bot, reliability and speed, with adjustable
|
|
43
|
+
weights.
|
|
44
|
+
- **Scenarios and probes**: a map editor, a scenario editor, and repeatable per-case decision tests
|
|
45
|
+
with expected outcomes and pass rates.
|
|
46
|
+
- **Metrics**: per-seat, per-turn timing, tool mix, errors, loops, tokens and turn endings,
|
|
47
|
+
exportable as CSV.
|
|
48
|
+
- **Servers, ledger and reports**: a registry of every machine that runs models, a usage ledger
|
|
49
|
+
that records work without prices, and self-contained HTML reports that price it at report time —
|
|
50
|
+
so correcting a rate corrects every report.
|
|
51
|
+
- **The lab**: a resumable queue of bot experiments, with bot code frozen at submit time and
|
|
52
|
+
factorial screening of parameters.
|
|
53
|
+
|
|
54
|
+
### Multi-user
|
|
55
|
+
|
|
56
|
+
- Accounts, invitations, single sign-on with Google, GitHub, Discord and Microsoft, e-mail
|
|
57
|
+
verification and password reset.
|
|
58
|
+
- Sharing with per-object visibility, public spectator links that never imply the right to play,
|
|
59
|
+
and seat tokens that grant exactly one seat.
|
|
60
|
+
- Pooled hardware: groups, grants, availability windows and per-account budgets, which are
|
|
61
|
+
accounting and admission control only — there are no payments anywhere.
|
|
62
|
+
- **Workers**: a machine at home serves its models to a remote CITAR over an outbound WebSocket, so
|
|
63
|
+
nothing needs to be opened on a router.
|
|
64
|
+
|
|
65
|
+
### Packaging and setup — new in this release
|
|
66
|
+
|
|
67
|
+
- `pip install citar`, with extras per role (`all`, `server`, `worker`, and one per provider).
|
|
68
|
+
- A **Windows installer** that needs no Python, an `install.sh` for macOS and Linux, an
|
|
69
|
+
`install.ps1` for Windows, a **Docker image** with a compose file that terminates TLS, and
|
|
70
|
+
Homebrew, Scoop and winget manifests.
|
|
71
|
+
- A unified **`citar` command**: `serve`, `setup`, `doctor`, `where`, `admin`, `worker`, `mcp`,
|
|
72
|
+
`bench`, `sim`, `balance`, `lab`.
|
|
73
|
+
- **`citar setup`**, an interactive wizard with three flows — this computer, a public server, or a
|
|
74
|
+
worker — each of which collects a plan, shows it, and asks once before writing anything. Every
|
|
75
|
+
question has a flag, so installers run the same code unattended.
|
|
76
|
+
- **A first-run wizard in the browser** that finds the model servers already running, reads the
|
|
77
|
+
machine's GPU, and suggests a model that will fit — or explains what to install for the hardware
|
|
78
|
+
it found.
|
|
79
|
+
- **An operator console** (`#/console`) showing what is configured, what is missing and what each
|
|
80
|
+
gap costs, with runtime policy editable in place and a test-e-mail button.
|
|
81
|
+
- **A front door.** On a public server, the root now explains what CITAR is to signed-out visitors
|
|
82
|
+
and offers the install command for their platform, instead of showing them a login box and
|
|
83
|
+
nothing else.
|
|
84
|
+
- **`citar doctor`**: versions, dependencies, directories and their permissions, configuration,
|
|
85
|
+
database, ruleset, every model endpoint, and whether the port is free.
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- **State no longer lands in `site-packages`.** Every module used to resolve its own directory from
|
|
90
|
+
`__file__`, which worked in a checkout and wrote saved games into the installed package
|
|
91
|
+
otherwise. `citar/paths.py` is now the single answer, and it distinguishes a writable source
|
|
92
|
+
checkout from an installed copy. The database and secret key never land in a checkout at all,
|
|
93
|
+
because a synced project folder corrupts a live SQLite file.
|
|
94
|
+
- **The route audit was checking about half of what it claimed.** Recent FastAPI wraps each
|
|
95
|
+
`include_router` call in an object with no `.path`, so a loop over `app.routes` walked past every
|
|
96
|
+
route on every included router — which is the whole authenticated API. It reported 83 routes and
|
|
97
|
+
a clean bill of health; there are 172. It now recurses into included routers, carries their
|
|
98
|
+
prefixes and router-level dependencies, and recognises the gates that are inner functions. It
|
|
99
|
+
also no longer prints "OK" underneath a list of unguarded routes.
|
|
100
|
+
- **The lab runner could not start a game.** A refactor removed the module-level `ROOT` that the
|
|
101
|
+
subprocess launch used, leaving an undefined name on a path only the runner takes.
|
|
102
|
+
- **Three closures captured a loop variable** in the scenario editor, natural-wonder discovery and
|
|
103
|
+
the lab's queue mover. Each was safe as written and would have broken the moment the call became
|
|
104
|
+
lazy; all three now bind explicitly.
|
|
105
|
+
- Database migrations are packaged with the wheel. They were being dropped by a rule that collects
|
|
106
|
+
data files and skips `.py`, which would have produced an installed copy with a migration
|
|
107
|
+
environment and no migrations in it.
|
|
108
|
+
|
|
109
|
+
### Known issues
|
|
110
|
+
|
|
111
|
+
The scripted bot is limited by happiness and stalls at two to five cities by turn 150, which caps
|
|
112
|
+
how hard it can push a model. See [KNOWN_ISSUES.md](KNOWN_ISSUES.md).
|
|
113
|
+
|
|
114
|
+
[Unreleased]: https://github.com/jprodgers/CITAR/compare/v0.1.0...HEAD
|
|
115
|
+
[0.1.0]: https://github.com/jprodgers/CITAR/releases/tag/v0.1.0
|
citar-0.1.0/DESIGN.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# CITAR (Civ Inspired Tool for AI Research) — Design Document
|
|
2
|
+
|
|
3
|
+
A top-down hex *Civilization V*-style game in which the opponents, or all of the players, are AI models. They connect
|
|
4
|
+
to the game, take turns, negotiate, bluff and wage war as a human would. The rules, numbers and underlying logic
|
|
5
|
+
come from **UnCiv**'s "Civ V – Gods & Kings" ruleset (see §2). CITAR adds the AI interface, benchmarking, a text
|
|
6
|
+
harness and a browser client.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Goals
|
|
11
|
+
|
|
12
|
+
- Civ V (Gods & Kings) rules at UnCiv's numbers: all nine eras, 80 techs, 127 units, 70 buildings, 40 wonders, 14
|
|
13
|
+
national wonders, 10 policy branches, religion, city-states, great people, golden ages, espionage, the UN and
|
|
14
|
+
diplomatic victory, and nukes.
|
|
15
|
+
- Fog of war enforced by the **server**: no player, human or AI, receives information their units, cities or spies
|
|
16
|
+
can't see.
|
|
17
|
+
- Any mix of seats: human, AI via MCP, AI via the built-in LLM adapter, or a scripted bot. Full AI-vs-AI spectating.
|
|
18
|
+
- A deterministic engine: saves, benchmarks and replays are reproducible from a seed.
|
|
19
|
+
- **Benchmarking:** models play full games against scripted bots on fixed seeds. `BenchmarkCiv`, a civilization with
|
|
20
|
+
no unique abilities, units or buildings, keeps every seat equal.
|
|
21
|
+
|
|
22
|
+
**Not imported:** graphics, sounds, civilopedia and flavour text, leader dialogue and tutorials. CITAR's renderer
|
|
23
|
+
draws flat vector terrain and unit glyphs.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Rule data and the UnCiv import
|
|
28
|
+
|
|
29
|
+
- `scripts/import_unciv.py` reads the UnCiv G&K JSON (`android/assets/jsons/Civ V - Gods & Kings`), keeps the
|
|
30
|
+
numbers and the rule text ("uniques"), drops flavour text, and writes `citar/data/ruleset/*.json`. Rerun it to
|
|
31
|
+
pick up a newer UnCiv version. `scripts/gen_unique_types.py` generates `engine/unique_types.py` from UnCiv's
|
|
32
|
+
`UniqueType.kt`. `scripts/check_uniques.py` lists ruleset uniques whose text matches no UnCiv unique type
|
|
33
|
+
(usually a typo), and `scripts/check_refs.py` finds calls to missing functions across engine modules.
|
|
34
|
+
- `citar/data/custom/` holds CITAR additions (currently `BenchmarkCiv`). `citar/data/game.json` holds CITAR's own
|
|
35
|
+
constants: map sizes, map types, lobby defaults, barbarian levels, and limits for the AI interface.
|
|
36
|
+
- **Licence:** UnCiv is MPL-2.0. The derived data and the engine code ported from UnCiv's Kotlin carry that licence.
|
|
37
|
+
Attribution is in `citar/data/ruleset/NOTICE.md` and the README.
|
|
38
|
+
- **Uniques:** as in UnCiv, most behaviour is written as rule text on techs, buildings, policies, beliefs, promotions,
|
|
39
|
+
nations and eras, for example `[+15]% Strength <when attacking>`. `engine/uniques.py` parses the placeholders,
|
|
40
|
+
parameters and conditionals, collects uniques from every source that applies (`UniqueMap`), and evaluates
|
|
41
|
+
conditionals against a context (`Ctx`). Triggered uniques (`engine/triggers.py`) run on events such as adopting a
|
|
42
|
+
policy, founding a city or discovering a tech.
|
|
43
|
+
- Every ruleset object is keyed by its UnCiv display name ("Bronze Working", "Great Library"). `Rules.resolve`
|
|
44
|
+
also accepts lower-case or snake_case ids, so AI players can write either form.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Architecture
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
┌───────────────────────────────────────────────────┐
|
|
52
|
+
│ Game Server (Python) │
|
|
53
|
+
│ ┌──────────────┐ ┌──────────────────────────┐ │
|
|
54
|
+
│ │ Engine │ │ Tool registry (tools.py) │ │
|
|
55
|
+
│ │ (pure rules, │◄──┤ one schema per action → │ │
|
|
56
|
+
│ │ seeded RNG, │ │ REST, web UI, MCP, LLM │ │
|
|
57
|
+
│ │ no I/O) │ └──────────────────────────┘ │
|
|
58
|
+
│ └──────┬───────┘ │
|
|
59
|
+
│ per-seat fog-filtered views event log │
|
|
60
|
+
│ ┌──────┴──────────────────────────────────────┐ │
|
|
61
|
+
│ │ HTTP JSON API + WebSocket push │ │
|
|
62
|
+
└──┴──────┬──────────────┬──────────────┬─────────┴───┘
|
|
63
|
+
│ │ │
|
|
64
|
+
Browser client MCP bridge (stdio) LLM adapter (in-process)
|
|
65
|
+
(human / spectator)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Engine modules** (`citar/engine/`, pure standard library):
|
|
69
|
+
|
|
70
|
+
| Module | Covers |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `rules`, `uniques`, `unique_types`, `triggers` | ruleset loading, the unique language, triggered effects |
|
|
73
|
+
| `state`, `game`, `hexmap` | serializable state, the `Game` facade (caches, events, RNG), hex math |
|
|
74
|
+
| `mapgen` | UnCiv-style map generation: landmass, elevation, climate, rivers along tile edges, natural wonders, resources, fair starts, city-state placement, ruins |
|
|
75
|
+
| `tiles`, `movement`, `visibility` | yields, movement (UnCiv costs, ZOC, embarkation, roads/railroads), line of sight with elevation |
|
|
76
|
+
| `cities`, `economy` | stats, growth, borders, citizens and specialists, production and purchases, happiness, maintenance, supply, resources |
|
|
77
|
+
| `research`, `policies`, `religion`, `great_people` | techs, social policies, pantheons and religions, great people and golden ages |
|
|
78
|
+
| `combat`, `units`, `conquest` | damage formula and modifiers, promotions, upgrades, air and nuclear combat, city capture |
|
|
79
|
+
| `workers`, `actions`, `automation` | improvements and pillaging, unit special actions, exploration and worker automation |
|
|
80
|
+
| `diplomacy`, `city_states`, `espionage`, `victory` | relations, deals, city-state influence and quests, spies, the UN, victories and score |
|
|
81
|
+
| `barbarians`, `ruins`, `turns` | camps and raiders, ancient ruins, turn order |
|
|
82
|
+
| `views`, `briefing`, `tools` | fog-filtered JSON views, text briefings and ASCII maps, the tool registry |
|
|
83
|
+
|
|
84
|
+
**Turn order** follows UnCiv's `TurnManager`: start-of-turn (resources, city processing, golden ages, great
|
|
85
|
+
people, religion), player actions, end-of-turn (production, growth, research, culture, faith, gold, unit healing
|
|
86
|
+
and upkeep), then an end-of-round pass (barbarians, city-state elections, UN votes, victory checks).
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 4. Game setup (lobby)
|
|
91
|
+
|
|
92
|
+
| Setting | Options (default **bold**) |
|
|
93
|
+
|---|---|
|
|
94
|
+
| Map size | Duel 44×28 (2p, 4 CS) · **Small 60×38 (4p, 8 CS)** · Standard 76×48 (6p, 12 CS) · Large 92×58 (8p, 16 CS) |
|
|
95
|
+
| Map type | **Continents** · Pangaea · Archipelago · Inland Sea · Fractal |
|
|
96
|
+
| Speed | Quick (330 turns) · **Standard (500)** · Epic (750) · Marathon (1500) |
|
|
97
|
+
| Difficulty | Settler … **Prince** … Deity (UnCiv difficulty table: happiness, costs, AI bonuses, barbarian bonuses) |
|
|
98
|
+
| Civilizations | each seat picks one of 35 civilizations, BenchmarkCiv, or random. Civ and city names are still free to edit. |
|
|
99
|
+
| City-states | count (default by map size), from 40 city-states |
|
|
100
|
+
| Victories | Scientific · Cultural · Domination · Diplomatic · Time (all **on**) |
|
|
101
|
+
| Options | religion · espionage · ancient ruins · tech trading (all **on**); barbarians off / **normal** / raging; nukes always on |
|
|
102
|
+
| Turn limit | the speed's time-victory turn by default |
|
|
103
|
+
|
|
104
|
+
**Start:** as in UnCiv, a Settler, a Warrior and (from the era table) extra starting units at a fair start location
|
|
105
|
+
chosen from the nation's start bias.
|
|
106
|
+
|
|
107
|
+
**Benchmarks** default to **Quick speed, 330 turns, BenchmarkCiv in every seat**.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 5. The AI interface
|
|
112
|
+
|
|
113
|
+
### Seats
|
|
114
|
+
| Seat type | How it plays |
|
|
115
|
+
|---|---|
|
|
116
|
+
| **Human** | Browser client with that seat's token. Several humans can join over the LAN. |
|
|
117
|
+
| **MCP** | `citar_mcp.py` bridges an MCP client (Claude Code, Claude Desktop, …) to the seat; `wait_for_turn` blocks until it's the seat's turn or a negotiation needs a reply. |
|
|
118
|
+
| **LLM adapter** | The server drives the model: `anthropic` or any OpenAI-compatible endpoint (LM Studio, Ollama, llama.cpp, vLLM). API keys come from environment variables and are never saved. |
|
|
119
|
+
| **Scripted bot** | `citar/bots/basic.py`: needs-based research, production and policies; religion, great people, spies, city-state gifts, trading, war preparation and sieges. |
|
|
120
|
+
|
|
121
|
+
### One tool set
|
|
122
|
+
Each action is declared once with `@tool(...)` in `engine/tools.py`. The REST API, browser UI, MCP bridge and LLM
|
|
123
|
+
adapter all derive from that registry. Invalid actions return a clear error ("Swordsman requires Iron") so models
|
|
124
|
+
can adapt.
|
|
125
|
+
|
|
126
|
+
- **Information:** `get_briefing`, `get_map`, `get_tile`, `get_unit(s)`, `get_city`/`get_cities`, `get_empire`,
|
|
127
|
+
`get_players`, `get_diplomacy`, `get_city_states`, `get_tech_tree`, `get_policies`, `get_religion`,
|
|
128
|
+
`get_great_people`, `get_espionage`, `get_victory_status`, `get_rules`, `get_events`, `preview_attack`,
|
|
129
|
+
`read_notes`.
|
|
130
|
+
- **Units:** `move_unit`, `attack`, `air_sweep`, `unit_order` (fortify, sleep, explore, automate, disband, …),
|
|
131
|
+
`unit_action` (found a city or religion, spread religion, great-person actions, great improvements, spaceship
|
|
132
|
+
parts, …), `build_improvement`, `found_city`, `upgrade_unit`, `promote_unit`.
|
|
133
|
+
- **Cities:** `set_production`, `change_queue`, `set_auto_production`, `buy` (gold or faith), `set_city_focus`,
|
|
134
|
+
`work_tile`, `set_specialists`, `buy_tile`, `city_attack`, `rename_city`, `city_status` (annex, puppet, raze).
|
|
135
|
+
- **Empire:** `set_research`, `choose_free_tech`, `adopt_policy`, `found_pantheon`, `choose_great_person`,
|
|
136
|
+
`set_civ_name`.
|
|
137
|
+
- **Diplomacy:** `send_message`, `open_negotiation`, `respond_negotiation`, `declare_war`, `denounce`,
|
|
138
|
+
`city_state_action` (gift gold or units, pledge protection, demand tribute), `un_vote`, `move_spy`,
|
|
139
|
+
`stage_coup`.
|
|
140
|
+
- **Turn:** `write_notes`, `log_thought`, `end_turn`.
|
|
141
|
+
|
|
142
|
+
**Briefing:** the text briefing opens with ALERTS (falling gold, unhappiness, threatened cities with the exact
|
|
143
|
+
`city_attack` call, civilians in danger, starving cities, free techs, policies or great people to pick, and so on).
|
|
144
|
+
It then shows empire stats, cities, units needing orders with suggested actions, available techs, a local ASCII map
|
|
145
|
+
using UnCiv terrain names, events, and diplomacy. After each batch of actions the model gets a TURN PROGRESS note.
|
|
146
|
+
|
|
147
|
+
### Negotiation
|
|
148
|
+
Messages are free text and never binding. Deals are binding and enforced by the engine. They can include gold,
|
|
149
|
+
gold per turn, resources, open borders, embassies, peace, declarations of friendship, research agreements,
|
|
150
|
+
defensive pacts, declaring war on a third party, cities, maps and technologies. The other side is interrupted
|
|
151
|
+
out of turn to accept, reject, counter or reply. The exchange repeats until one side accepts or walks away, or
|
|
152
|
+
the round cap is reached.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 6. Saves, replays and recap
|
|
157
|
+
|
|
158
|
+
- Games autosave every turn to `saves/<game id>/autosave.citar` (gzipped JSON with the full state, event log,
|
|
159
|
+
messages, AI thoughts and notebooks). Named saves come from the game screen.
|
|
160
|
+
- Per-turn frames record territory, cities, units and stats for the **recap viewer**: timeline scrubbing, "as player
|
|
161
|
+
X saw it" fog, graphs, events, diplomacy transcripts and AI reasoning.
|
|
162
|
+
- Saves from CIGAR (the pre-UnCiv ruleset) are incompatible. They were moved to `saves/_cigar_archive/` along with
|
|
163
|
+
the old rule files.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 7. Decisions log
|
|
168
|
+
|
|
169
|
+
- **CIGAR v0.1 (2026-09-16/17):** the original simplified ruleset (56 techs, 6 eras, CITAR's own numbers), the
|
|
170
|
+
benchmark suite and scheduler, model scoring, the AI harness (alerts, turn progress, guard rails for weak
|
|
171
|
+
models), and the human-play improvements (queue editing, route previews, tile locking, and more). The balance
|
|
172
|
+
passes for that ruleset no longer apply.
|
|
173
|
+
- **CITAR refactor (2026-09-18):** renamed from CIGAR to CITAR, "Civ Inspired Tool for AI Research".
|
|
174
|
+
- All rule data and logic come from UnCiv's G&K ruleset at pure UnCiv numbers: no flavour text and no graphics.
|
|
175
|
+
Every missing subsystem was added: religion, social policies, city-states with the diplomatic victory and the
|
|
176
|
+
UN, great people and golden ages, espionage, difficulty levels, ancient ruins, natural wonders and nukes.
|
|
177
|
+
- Every speed is available. Standard is the default for normal games, and benchmarks use Quick (330 turns), even
|
|
178
|
+
though that roughly doubles test time compared with the old 200-turn games.
|
|
179
|
+
- Seats pick a civilization, or get a random one, and keep free naming. BenchmarkCiv, with no abilities, fills
|
|
180
|
+
every benchmark seat by default.
|
|
181
|
+
- Turn numbering: CITAR's turn 1 is UnCiv's turn 0, both for the year and for the time victory, which fires
|
|
182
|
+
after the speed's last turn.
|
|
183
|
+
- The legacy v0.1 bot was removed, since it targeted the old rules. BasicBot was rewritten for the new systems.
|
|
184
|
+
Its building choices simulate the city's stats with the building added, following UnCiv's
|
|
185
|
+
`getStatDifferenceFromBuilding`.
|
|
186
|
+
- **Bot balance (2026-09-18), first pass from bot-vs-bot Quick games:** the bots were limited by happiness, not
|
|
187
|
+
production. BasicBot now values techs that unlock improvements for the luxuries it owns and improves new luxury
|
|
188
|
+
types first, including those beyond the 3-tile work radius and those at sea. It weighs happiness buildings
|
|
189
|
+
against the UnCiv expansion rule (happiness above the city count) and spends surplus gold on buildings and
|
|
190
|
+
city-states.
|
|
191
|
+
- **Servers and Reports (2026-09-19):**
|
|
192
|
+
- A server registry (`citar/servers.py`, `config/servers.json`) replaces every inline provider/URL/key setting.
|
|
193
|
+
Seats, suites, probe runs and report narratives store a reference (`server_id`, `model_id`, `profile_id` plus
|
|
194
|
+
overrides) that `servers.resolve_llm` turns into an agent config when the AI starts, so key or endpoint changes
|
|
195
|
+
apply without editing games. Pre-registry saves, benchmark runs, suites and probe runs were moved to
|
|
196
|
+
`saves/_archive_2026-09-19_pre-servers/`; lab results were kept for the bot-tuning campaign.
|
|
197
|
+
- API keys: OS credential store via `keyring`, an environment variable, or a passphrase-encrypted file outside
|
|
198
|
+
the project (`citar/keystore.py`). The browser can store a key but never read it back.
|
|
199
|
+
- Hardware: `citar/hwinfo.py` is a standalone, stdlib-only collector for Windows, Linux and macOS (also served as
|
|
200
|
+
`collect_hardware.py`); `scripts/collect_hardware.ps1` covers Windows machines without Python. Other machines are
|
|
201
|
+
imported from the collector's JSON rather than contacted over the network.
|
|
202
|
+
- Restricted hours are per server (replacing the global quiet hours): benchmark games finish the model turn in
|
|
203
|
+
progress (up to the grace minutes) before pausing; probes and the lab wait; lobby games only warn.
|
|
204
|
+
- Usage and cost are separate: `citar/usage.py` records what each activity used (server time held, model
|
|
205
|
+
generation time, tokens, host CPU time, one-minute power samples) and `citar/costing.py` prices it at report time
|
|
206
|
+
with effective-dated settings. Fixed costs are charged on a calendar basis (unused time stays unallocated),
|
|
207
|
+
idle power is shared by time held and power above idle by work done, and tokens at the model's prices.
|
|
208
|
+
- Reports (`citar/reports/`) are built on demand into self-contained HTML with inline SVG charts, deterministic
|
|
209
|
+
findings (confidence intervals, Welch tests, Pareto frontier, what-if pricing, lifespan sensitivity), and an
|
|
210
|
+
optional narrative written by a model picked from the registry.
|