Commit Graph
219 Commits
Author SHA1 Message Date
coolguyandClaude Opus 4.8 299c788545 Fix web client interaction and layout bugs
Placing a card locked the turn in: the chosen card left the hand and no
affordance reverted the placement, forcing the move through. Clicking the
chosen destination again or pressing Escape now steps the selection back.

A failing ONNX inference left the AI's turn unadvanced, permanently
stalling the game. The AI turn now falls back to the heuristic policy.

Other fixes: the score plaque no longer covers hand cards (plaques become
compact chips at narrow widths and hand spacing tracks the viewport),
opponent cards drawn from a discard pile render face up, long expedition
stacks stay inside their lane, undo no longer bumps the generation counter
on empty history, and small viewports scroll instead of clipping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmyprzuzanXRhpomc3Ga1i
2026-07-14 21:04:27 +09:00
coolguy e47db5eb1e Show placed cards before drawing 2026-07-14 20:37:59 +09:00
coolguy 891cc113f0 Align web client with pygame table 2026-07-14 20:28:47 +09:00
coolguy ef4b9d82b0 Focus project on JAX PPO 2026-07-14 20:09:03 +09:00
coolguy 79273f7eb3 Add on-device web client 2026-07-14 19:49:03 +09:00
coolguy 9517fd3ba8 Document JAX PPO model capacity experiment 2026-07-12 15:21:09 +09:00
coolguy f854d97764 Add match replay and JSONL export 2026-07-12 15:14:24 +09:00
coolguy 7e481ca064 Add polished Lost Cities table GUI 2026-07-12 14:58:59 +09:00
coolguy 3b86f81bf6 Add JAX PPO opponent to classic GUI 2026-07-12 04:14:02 +09:00
coolguy 19560029b5 Add final cycle report and human play CLI 2026-07-06 00:14:24 +09:00
coolguy 16cc31676c Add diminishing returns diagnostic report 2026-07-05 18:32:47 +09:00
coolguy fa9a1c5286 Add gates 1-2 audit and repair workflow 2026-07-05 17:55:22 +09:00
coolguy 94e9ac1854 Add JAX PPO league self-play v1 2026-07-05 06:33:22 +09:00
coolguy 6037b650f3 Add JAX PPO ladder v2 expert pass 2026-07-05 02:06:22 +09:00
coolguy 7fe0bfdcfe Add JAX PPO ladder verification pass 2026-07-05 00:17:49 +09:00
coolguy bb52ef9ec1 Document JAX PPO ladder results 2026-07-04 23:19:41 +09:00
coolguy 4c0c2e9add Reset finished JAX PPO environments 2026-07-04 22:33:51 +09:00
coolguy 9e27f42f27 Add JAX PPO static-opponent trainer 2026-07-04 22:30:16 +09:00
coolguy 768f74693d Plan JAX PPO static-opponent ladder 2026-07-04 22:14:37 +09:00
coolguy b37b841eef Record CUDA JAX throughput 2026-07-04 21:56:24 +09:00
coolguy 72893250ec Record full JAX differential verification 2026-07-04 20:39:01 +09:00
coolguy 30ccc3cf41 Batch JAX differential verification 2026-07-04 19:42:05 +09:00
coolguy f872204b13 Document JAX engine and benchmark 2026-07-04 19:38:09 +09:00
coolguy ac54f98189 Add JAX engine verification tests 2026-07-04 19:38:04 +09:00
coolguy 1d7758b7d3 Add JAX Lost Cities rules engine 2026-07-04 19:37:27 +09:00
coolguy 8f380928e6 docs(research): split SO-ISMCTS into its own group in catalog
ismcts-bc-ceiling-2026-05-11 was filed under "Other"; promote it to a
proper SO-ISMCTS section between Deep CFR and Engine/Performance so the
catalog actually reflects the project's two algorithm families. As more
ISMCTS notes accrue they have an obvious home.
2026-05-12 00:22:10 +09:00
coolguy 2a8a082a12 Catalog research notes for new agents
Give new agents a single entrypoint into accumulated research, and point AGENTS.md at that catalog.
2026-05-11 21:04:44 +09:00
coolguy 44b8faba3d docs(research): SO-ISMCTS BC ceiling write-up from 2026-05-11 autonomous session
Summarizes the 13-cycle trap-exploration session: BC pretrain (heuristic
clone) is the self-play ceiling under our compute budget (1 GPU + 50
sims + 768x4 MLP). All variants (naive finetune, KL anchor, mirror
descent, mixed-opponent + opponent-aware search) either preserved BC
(~17-21/100 vs heuristic-cautious) or regressed to catastrophic
forgetting. The single largest improvement of the session — 4× win rate
on the same checkpoint — came from PUCT Q-value normalization at search
time, not from any learning change.

Records the mechanism (negative training signal from BC-vs-heuristic
games; search too shallow to find heuristic-beating moves), the
hypotheses we negated, and the dials left in code for future runs with
more compute.
2026-05-11 20:45:10 +09:00
coolguy cba6caee2f Add mixed-opponent self-play with opponent-aware MCTS
C13/C14 cycles: heuristic-balanced bot plays a configurable fraction of
self-play games (training.mixed_opponent_fraction). Trainee turns are
stored as policy samples; opponent turns are taken by the bot directly
and not stored. When mcts.opponent_aware_search is set, the MCTS tree
also treats the opponent seat as that bot — opponent moves are applied
without expanding into the search tree, and all values are taken from
the traverser's perspective. This was Codex's top recommendation for
breaking the symmetric self-play weak fixed point.

Empirical: opponent-aware mixed self-play does NOT lift win-rate above
BC pretrain (vs heuristic-cautious 100-game eval):
  C13 (mixed=0.5, no KL):     0/100 — catastrophic forgetting
  C14 (mixed=0.2, KL beta=1): 17/100 — preserved BC, no improvement
  BC pretrain baseline:       21/100

Combined with C10-C12 results, BC remains the ceiling under our
compute budget (1 GPU + 50 sims + 768x4 net). Code is left in place as
configurable dials for future runs with more compute.
2026-05-11 20:42:13 +09:00
coolguy b9fc5693a4 Normalize PUCT Q + add mirror-descent policy target
Codex follow-up diagnostics identified two MCTS+training-loop issues that
together cap finetune-from-BC at the heuristic ceiling:

1. PUCT Q is in raw score units (~±100 for value_scale=100), but the
   exploration bonus c_puct * prior * sqrt(N) / (1+n) is on order of 1-10
   for our parameter ranges. Result: a single bad backup pushes q_eff
   well below the bonus floor and that action is effectively never
   visited again. With only 50 sims/move this is catastrophic for the
   policy-improvement operator. Fix: divide q_eff by config.q_scale
   (default 100, configurable) inside _select_action. Backups and value
   targets remain in raw score units; only the selection signal is
   normalized. AlphaZero canonical convention.

2. The current kl_anchor_beta path adds KL(current || ref) directly to
   the loss. That preserves BC but prevents improvement (gradient
   actively pulls policy back to reference). The standard regularized
   policy improvement operator is to mix the target instead:
     pi_target = softmax(alpha * log(pi_mcts) + (1-alpha) * log(pi_ref))
   Anneal alpha from low (rely on BC) to high (rely on MCTS) over
   training. Network learns to follow the regularized target, which
   stays near BC early but lets MCTS-discovered improvements through
   later.

Config additions:
- mcts.q_scale (default 100.0): PUCT Q divisor
- training.md_target_ref_ckpt: reference policy path (alternative to kl_anchor)
- training.md_target_alpha_start / _end / _iters: linear alpha schedule

Both Python mcts.py and Cython mcts.pyx updated; parity test passes.
Tests: 19/19.

Hypothesis: with normalized PUCT the network can actually explore and
exploit prior knowledge competently at 50 sims, and the mirror-descent
target lets self-play improvement happen while BC anchors the trajectory.
This is the operator-side fix that c9 (no anchor, collapsed) and c10/c11
(loss-side KL anchor, preserved-but-stuck) both missed.
2026-05-11 16:31:02 +09:00
coolguy d850070ed4 Add KL anchor to BC reference policy in trainer
Self-play drift fix: regularize loss with KL(current || BC_reference).
Config: training.kl_anchor_ckpt + training.kl_anchor_beta. Loaded once
at trainer init, frozen. KL computed over legal actions only.
Hypothesis: appropriate beta keeps pretrained competence during self-play
finetune, escaping the c9 catastrophic forgetting.
2026-05-11 15:24:14 +09:00
coolguy 9fdfa88b23 Add lost-cities-ismcts pretrain: behavior-clone heuristic into network 2026-05-11 13:36:28 +09:00
coolguy 33c44c708e Add --resume-from for warm-starting training from a checkpoint
Lets c6+ layer new exploration hyperparams on top of c5's learned
value head instead of restarting from random init. Saves ~60min per
cycle while preserving VPE-down trajectory observed in c5.
2026-05-11 10:41:08 +09:00
coolguy 0d35341bbe Cycle 5 setup: long run with use_rollout_value=false + 768x4 network
C4 (768x4 + rollout=True, 150 iter) result: 0/300 natural wins, score
avg -85 to -97 vs three heuristic opponents. Bigger network alone did
not produce wins; PA shifted up to 0.23-0.25 (similar to c3 without
rollout) but agent still loses every natural-end game.

Capacity hypothesis rejected: 2.5x more params (~2M vs ~800k) did not
break the loss pattern. Score average actually slightly worse than
512x3 baseline. So the bottleneck is not network capacity.

Going to the long-run experiment: AlphaZero-correct setup with the
network value loop closed. use_rollout_value=false means leaf Q comes
from network value head. Training signal: network value learns from
actual game outcomes; MCTS uses those values to pick actions; better
actions produce better outcomes; cycle closes.

100 iter previously gave essentially the same result as rollout=true
(comparing c3 to trapfix baseline). Both are too early in the AlphaZero
training curve. Standard AlphaZero papers train 1000s of iterations.
Going long: 1000 iter with the current config. Self-play ~9s/iter
without rollout, total wall ~150 min for the train phase.

Plotting strategy: at iter 200, 500, 1000, run 100-game standalone eval
and generate analyze.py plots to visualize trajectory.

Network kept at 768x4 since bigger capacity does not actively hurt.
2026-05-11 07:56:35 +09:00
coolguy a501a93223 Cycle 4: revert use_rollout_value=true, scale network 512x3 -> 768x4
c3 showed use_rollout_value=false alone is not the fix: without the
heuristic rollout safety net the random-init network value gives bad
MCTS Q early on, the agent plays more (PA 0.10 -> 0.22+) but eats more
-20 expedition penalties (score worsened from -57 to -97 avg).

Mathematical intuition: in Lost Cities, opening an expedition is a
20-point commitment. Break-even requires rank-sum × (handshakes+1) >= 20.
The model has to learn:
  - which colors to open (based on hand handshake/high-rank holdings)
  - when to commit vs discard
  - card-ordering constraints (ascending only)

This is a moderately rich value function. 512x3 (~800k params, ~290
input dim) might be undersized. Test capacity hypothesis with 768x4
(~2M params) while keeping the rollout safety net so MCTS Q stays
competent.

Other params from c1 kept: c_puct=5, virtual_loss=5, dirichlet
α=0.3/ε=0.4, parallel_simulations=64, n_simulations=50.
2026-05-11 07:03:45 +09:00
coolguy 200129d16d Add diagnostic value-head metrics: rmse, target stats
Codex flagged that mcts/value_prediction_error mathematically reconciles
with loss/value (MSE / value_scale^2 = 0.05) but the latter looks healthy
while the former says the value head is far off. To make this clearer in
W&B, expose:

- mcts/value_rmse — sqrt(MSE), in raw score units (interpretable)
- mcts/v_target_abs_mean — magnitude of |v_target|, indicates if game
  outcomes are very lopsided (always negative for a losing agent)
- mcts/v_target_std — spread, low std means targets are saturated to
  one end (e.g., always -100ish)

These let us see whether value head is failing because targets are
unlearnable variance, or just hard-to-predict, or because of saturation
at the value_scale=100 tanh boundary.

Tests: 19/19 passing.
2026-05-11 06:43:50 +09:00
coolguy 8b7ed66ffd Cycle 3 prep: fix search() ignoring parallel_simulations + flip use_rollout_value=false
Codex deep diagnosis surfaced two real issues in our MCTS pipeline:

1. mcts.pyx::search() hardcoded prepare_simulation_batch(state, traverser, 1)
   instead of respecting MctsConfig.parallel_simulations. Standalone evals
   (eval_checkpoint, evaluate_with_mcts sequential path, eval_worker) all
   use this entry point, so all eval-time MCTS was running 1 sim per batch
   regardless of the configured 64. Training was unaffected because it
   goes through interleaved_self_play._run_search_jobs which respects the
   config. Fix uses min(config.parallel_simulations, sims - completed).

2. use_rollout_value defaulted to True (config.py) but was never set in
   the YAML. With this, _expand_with_prior returns the heuristic rollout
   value and discards network_value, so the network value head is trained
   from final game scores but its outputs are never fed back into MCTS
   backups. This explains why mcts/value_prediction_error stays high
   despite training -- learning the value head produces no behavioral
   change because MCTS never reads it.

Now setting use_rollout_value=false in default.yaml so the network value
head closes the loop. Combined with the existing Dirichlet root noise +
heuristic rollout removal, this should give the network's value learning
actual leverage on action selection.

Also: updated test_search_visit_counts_match_with_parallel_simulations
to test the correct invariant (legal-action set match + total visit
count near n_sims) rather than literal visit-count equality, which was
only true under the previous bug.

Tests: 19/19 passing.
2026-05-11 06:42:20 +09:00
coolguy be0c1a8d62 Add training.value_loss_weight (default 1.0) for value loss reweighting
Diagnosis: mcts/value_prediction_error stuck at 300-1000 (RMSE ~22 on score
range ±100), while loss/value stays at 0.05 because the loss divides
prediction and target by value_scale=100 (so MSE / 10000). Net effect: the
value head receives a tiny gradient relative to the policy head's
~1.75 cross-entropy loss, so it never learns to predict score scale well.

This adds a config knob to multiply the normalized value loss without
re-engineering the loss formula. value_loss_weight=50 recovers the raw
MSE magnitude (~2.5 vs policy loss ~1.75), giving the value head
comparable gradient signal.
2026-05-11 06:34:05 +09:00
coolguy 169d4dcb14 Cycle 1: c_puct 3->5, dirichlet_eps 0.25->0.4 produced first natural-end wins
50-iter sweep on default.yaml with stronger MCTS exploration:
- c_puct: 3.0 -> 5.0 (UCB weight, more exploration of low-prior actions)
- root_dirichlet_epsilon: 0.25 -> 0.4 (more noise injected at root prior)

Standalone eval at iter 50 (30 games/opponent, all natural-end, timeouts=0):
- vs heuristic-balanced:    W=0/30 S=-70.5  (PA 0.15)
- vs heuristic-aggressive:  W=2/30 S=-65.1  (PA 0.14)  [+10, +4]
- vs heuristic-cautious:    W=1/30 S=-48.0  (PA 0.14)  [+2]

3 natural-end wins vs prev trapfix baseline iter 44 (which had 0 natural
wins + 1 timeout-tie). Stall trap fixed remains true (timeouts=0 in c1).

Trade-off observed: more exploration -> higher variance. Score avg vs
cautious worsened (-32 -> -48), but win events appeared. For the
non-terminal-win objective, exploration win > score-avg loss.

Next: commit to long run (300 iter) with these params before tuning more.
2026-05-11 05:53:54 +09:00
coolguyandClaude Opus 4.7 f289997c1c Add Dirichlet root noise + standalone eval CLI, fix self-play stall trap
Trap diagnosis: agent learned to stall (avoid opening expeditions, draw
from discard pile to extend deck) until max_steps timeout, then squeak by
on opponents' negative scores. All eval wins were from timeouts; agent
never won a naturally-terminating game. Self-play reinforced this because
timeout games still got a positive value target.

Fixes (no algorithm change, all MCTS hyperparameters or signal shaping):

- Dirichlet noise at root prior (AlphaZero standard, was missing):
  mcts.pyx `_expand_with_prior` takes `is_root` flag; root expansion
  mixes prior with Dirichlet(α). Callers in interleaved_self_play and
  the internal evaluate_and_backup pass `not item.path`.
- Default config strengthens exploration on the 50-sim batched search:
  c_puct 1.5 -> 3.0, virtual_loss_value 1.0 -> 5.0, plus new
  root_dirichlet_alpha=0.3 / root_dirichlet_epsilon=0.25.
- Self-play timeout signal zeroed: `_finalize_context` sets v_target=0
  if context.state is not terminal. Stops the network from learning
  "stall = positive value".

New standalone evaluator:
- `lost-cities-ismcts eval` subcommand (eval_checkpoint.py): loads a
  checkpoint, runs N games per opponent across a parallel pool, reports
  win/score with 95% CIs plus per-game logging via --verbose. Defaults
  cover heuristic-balanced/aggressive/cautious (rollout policy isn't in
  the training-eval opponent list, so this is the natural way to compare
  the trained policy against its rollout target).

Tests (19) still pass; .so rebuilt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 05:17:32 +09:00
coolguyandClaude Opus 4.7 651175e5bd Add multi-process self-play, eval workers, MCTS Cython port
Key changes for ISMCTS speed and correctness:
- Cython port: HeuristicBot helpers (`heuristic_cy.pyx` + new `.pxd`) and
  ISMCTS searcher (`mcts.pyx`) now run as cdef. Both share a fast
  unified-action path through GameState's C interface to avoid Python
  round-trips on hot rollout/tree-walk paths.
- Multi-process self-play and eval: `workers.py`, `eval_worker.py`,
  `interleaved_self_play.py`, plus trainer wiring with ProcessPoolExecutor
  + spawn context. Eval inside `evaluate.py` is parallel per opponent.
- ISMCTS-specific eval (`evaluate.py`) runs MCTS at decision time so the
  metric matches deploy mode; `evaluation.eval_with_mcts` flag preserves
  backwards-compatible policy-only eval when needed.
- Trainer logs progress per phase (self-play start/done, eval per
  opponent), and value loss is now scaled by `value_scale` so policy and
  value losses sit on comparable magnitudes.
- Compact info-set key (`info_set.py`) using packed-struct format and
  child-key reuse during MCTS descent to cut per-step canonicalization.

Tests: 19 ISMCTS suite passing, including parity (Cython-vs-Python
sequential, batched-vs-sequential visit counts, push/pop round-trip).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 02:39:39 +09:00
coolguy 0999d34277 Wire W&B tracking into ISMCTS trainer 2026-05-10 23:13:03 +09:00
coolguy 812dace1e3 Extend analyze.py for ISMCTS metrics
- Loss section: add ISMCTS policy/value twin-axis panel alongside the
  Deep CFR advantage/strategy panel. Both render only when their keys
  exist; the unused side shows "No data".
- Memory Size: add memory/replay alongside memory/advantage and
  memory/strategy.
- New MCTS section (analysis_09_mcts.png): visit-count entropy, value
  prediction error, policy/MCTS KL — three iter-time scalars emitted by
  IsMctsTrainer. Auto-skips on Deep CFR runs (no data).
2026-05-10 23:08:51 +09:00
coolguy 25a3fba53f Use Deep CFR diagnostics for IS-MCTS eval
Wrap AlphaZeroNet with a logits-only view so IS-MCTS training evaluation can call evaluate_strategy_network and emit the same full diagnostic metric set as Deep CFR. Adds root prior capture and per-iteration MCTS entropy, value error, and policy-vs-search KL metrics.

Tests: uv run python -m pytest tests/games/classic/ismcts/ -x; uv run python -m pytest tests/games/classic/test_deep_cfr_trainer.py -x; uv run lost-cities-ismcts train --config configs/ismcts/mini.yaml --set run.experiment_name=ismcts-metrics-smoke --set run.max_iterations=2 --set training.games_per_iter=2
2026-05-10 23:04:05 +09:00
coolguy bec59dfc3c Record §12 R3 SO-ISMCTS mini Lost Cities PoC result
Mini Lost Cities (3색 5랭크) 50 iter / 20 eval games:
- score_diff vs random +33, vs heuristic_cautious -5.7 (45% 승률)
- play_action_rate 13~31% (Deep CFR의 0~2% 대비 명확)
- trap escaped on mini, "long-horizon credit assignment" 가설 지지

Codex commit e69f316으로 ISMCTS 구현 완료. Full game scale-up이
다음 후보 (100 iter ETA 3시간 추정).
2026-05-10 22:54:03 +09:00
coolguy e69f3165b6 Add SO-ISMCTS mini trainer
Implements a proof-of-concept single-observer IS-MCTS trainer with AlphaZero-style policy/value network, determinization, replay, self-play, CLI configs, and focused tests. Mini acceptance run reaches positive random eval while keeping play_action_rate above the Deep CFR trap threshold.

Tests: uv run python -m pytest tests/games/classic/ismcts/ -x; uv run python -m pytest tests/games/classic/test_deep_cfr_trainer.py -x; uv run lost-cities-ismcts train --config configs/ismcts/mini.yaml
2026-05-10 22:46:22 +09:00
coolguy 5acda3f272 Record R2 (heuristic_balanced opponent) early termination + IS-MCTS pivot
R2 167 iter 시점 trap signature 명확히 R0/R1과 동질 (play_action_rate
≈ 0%, positive expedition 0.02/game, bad_open 0.98). 세 가지 opponent
환경(self / passive / competent) 모두에서 같은 결함 확정. opponent
dimension closed.

Doc §11에 R2 결과 + 세 런 비교표 + opponent 변경으로 풀 수 없음
결론 + 다음 방향(SO-ISMCTS 정공법) 기록.
2026-05-10 22:18:17 +09:00
coolguy 7d59398159 Add heuristic_balanced opponent_policy to interleaved scheduler
Mirrors the discard_only plumbing pattern. Uses HeuristicBot() (default
balanced params) and converts the bot's phase-local action to unified via
state.to_unified_action. Recursive (Cython) path was already supported
and is unchanged.

Tests: smoke run + accept/reject validators. All 59 tests pass.
2026-05-10 21:08:12 +09:00
coolguy 12d10fd9c8 Record full_depth past-self pool experiment from prior repo
이전 레포 commit 33e0368 (full_depth 실험)에서 past-self 풀만 (anchor
없음, current 0.5 + recent 0.3 + older 0.2)으로 322 iter 돌린 결과
selectivity emerge 실패한 이력을 doc에 추가. opened_colors 4.94-4.96
유지, 5-color opening 91-93%로 감소 없음. self-play 가족 내부 다양성은
self-mirror 평형을 시간축으로 평행이동시킬 뿐 selectivity 못 풀음.
2026-05-10 20:34:31 +09:00
coolguy 52ef321274 Record anchor_safe015 self-play league mixing experiment from prior repo
이전 레포 commit 279d726, a77464b의 self_play_league에 safe_heuristic
anchor 0.15 주입 실험(1219 iter / 4h 풀 런) 결과를 doc에 기록.
opened_colors 4.83 / 5-color 86%로 trap 못 깸. 0.15 weight으로는
self-mirror 평형 절단 불충분이라는 결론 명시.
2026-05-10 20:27:48 +09:00