Reports · 2026-09-19

A one or two strategy opponent portfolio beats full KLUSS at equal budget, and still loses to expectimax

Opponent-limited subgame solving against full KLUSS by budget

Opponent-limited subgame solving against full KLUSS by budget00.10.20.30.40.50.62004006008001000budget (ms per move)effective score
  • olss2 n=1 v kluss
  • olss2 n=2 v kluss
  • olss2 n=2 v expectimax
Opponent-limited subgame solving against full KLUSS by budget
Seriesbudget (ms per move)effective score
olss2 n=1 v kluss500.61
olss2 n=2 v kluss500.605
olss2 n=2 v kluss2000.62
olss2 n=2 v kluss10000.55
olss2 n=2 v expectimax2000.38
Effective score of the olss2 player against the same-budget kluss, and against expectimax at 16 worlds and depth 2, on 200 paired games per cell (100 at the 1 s budget), with 95% intervals in the report table; built by scripts/build_portfolio_assets.py.

The searcher is the kluss crate of the previous study with mode set to olss2 (OLSS-II, Liu et al. 2023, Figure 3 right): the opponent no longer plays freely in the subgame but chooses one of N whole-game strategies at a single CFR decision node at the root, and below the root every opponent node plays the chosen strategy as a chance node. The opponent's information sets collapse into that one choice, so the tree holds only the searcher's information sets, and with N of one it is a single-agent expectimax over the sampled worlds; a test in crates/solve/tests/solve.rs checks the reduction against a reference backup on a small position, and two more pin determinism and the information boundary. The portfolio members act on the observation the opponent would have at the node: greedy, a cheap expectimax, and self, the searcher's own one-ply leaf policy at a temperature. The full parameter list is in server/docs/search.md.

Calibration and cost

Each cell pairs equal bench cost, one thread on twenty middlegame positions. Measured on the idle machine at the end of the study: kluss reads 53 ms at 2,500 expansions, 139 ms at 5,000 and 467 ms at 10,000; olss2 with the two-strategy portfolio reads 51 ms at 5,000, 160 ms at 10,000 and 770 ms at 20,000, and olss2 with the greedy member alone reads 41 ms at 5,000. The olss2 tree grows several plies deep because every opponent ply is a single-child chance node, and the deep tree makes each CFR sweep dearer as the budget grows: the cost rises from 160 ms at 10,000 expansions to 8.1 s at 40,000 under the load of the study's runs. In the arena the cells measured 116 to 133 ms per move at the 50 ms calibration, 388 to 490 ms at 200 ms, and 1,798 to 2,812 ms at 1 s for the two searchers, against expectimax's 8.4 ms; the in-game cost runs about twice the bench figure, as it did for kluss. The 1 s cell was calibrated under load (818 ms against 848 ms on the bench at the time); on the idle machine its two sides read 467 and 770 ms, so that cell's nominal budget is looser than the smaller cells' and favours the olss2 side.

What the games show

Five cells, all under the training rules, the olss2 player always contestant zero. Games end almost entirely by Flag capture (191 of 200, 195, 190, 94 of 100 and 192 respectively, the rest with a side out of moves) and last 147 to 197 plies on average: ordinary Stratego, not stalling.

cellbudgetopponentgameseffective95% Wilsoncontrast95% interval
N=150 mskluss2000.6100.541 to 0.6750.2200.066 to 0.374
N=250 mskluss2000.6050.536 to 0.6700.2100.064 to 0.356
N=2200 mskluss2000.6200.551 to 0.6840.2400.085 to 0.395
N=21 skluss1000.5500.452 to 0.6440.100minus 0.111 to 0.311
N=2200 msexpectimax2000.3800.316 to 0.449minus 0.240minus 0.390 to minus 0.090

Three readings. First, limiting the opponent pays at equal budget: N of one and N of two are indistinguishable at 50 ms (0.610 against 0.605), so the gain comes from turning the subgame single-agent, not from covering more opponent styles, and the diagnostic trace agrees: the solved opponent mixture on twenty middlegame positions at the 200 ms calibration puts 92% on greedy and 8% on self at temperature 0.3, and the purified root keeps a single action. Second, the margin narrows as the budget grows (0.220, 0.240, then 0.100 with the interval covering zero), so a crossing plausibly sits above 1 s, where both searchers' cost curves turn sharply superlinear; measuring it costs about four hours of arena time per cell and is left registered-ready as the next step. Third, the portfolio closes only part of the gap to plain search: full kluss scored 26.8% against expectimax at this budget, the portfolio player scores 38.0%, and expectimax at 8 ms per move remains the top of the measured ladder.

For the theory this splits two claims. The mechanism of T2 is confirmed here: at every budget where both were measured, the opponent-limited subgame beats the free-opponent subgame at equal cost, and one or two strategies suffice, as Liu et al. found in Mahjong. The theory as a route to a stronger player fails at these budgets all the same: the portfolio player loses to the same expectimax that beat full kluss, and the log's disproof condition (a loss to plain expectimax) is met. The binding constraint is not the opponent model but what both kluss-mode searches share, the shallow tree over 64 worlds with the hand-written leaf.

What this means for the belief study and the ladder

The opponent portfolio is the right shape for a better leaf or belief: it spends its budget on depth instead of on defending against every opponent at once, and the portfolio parameter admits the cheap expectimax member (about 25 times the greedy member per call) at budgets that can afford it. The natural next registration is olss2 with greedy, expectimax and self at the 1 s budget against expectimax itself, with the scaled run's belief sampler when it exists. OLSS-I (the non-uniform root weighting) was not implemented; with the root sample already drawn from the counting belief and the opponent restricted, the weighting is a smaller lever than the portfolio width.

Run records: N=1 at 50 ms, N=2 at 50 ms, N=2 at 200 ms, N=2 at 1 s and against expectimax.

Reproduction

Registrations experiments/2436cdbe-9447-4d1a-90d3-e85b8da1393d.toml, experiments/81e925ab-4584-4bd4-b62a-f0869ac13125.toml, experiments/092a047f-bad7-4d56-98f0-5e28094c6ec9.toml, experiments/c8ba64ba-05df-4d3b-bbca-cdd7cb9556e8.toml and experiments/4ffa3c2f-c818-4d24-828a-b3d8fc49d73f.toml. Commands, from the repository root:

cd server && cargo run --release -p armies-solve --bin bench -- --params '{"mode":"olss2","samples":64,"cfr_every":64,"portfolio":[{"policy":"greedy"},{"policy":"self","parameters":{"temperature":0.3}}]}' --expansions 5000,10000,20000
cd server && cargo run --release -p armies-solve --bin trace -- --params '{"mode":"olss2","samples":64,"cfr_every":64,"portfolio":[{"policy":"greedy"},{"policy":"self","parameters":{"temperature":0.3}}]}' --expansions 10000
cd server && cargo run --release -p armies-arena --bin matchup -- --red 'olss2 n2 200ms=kluss:{"mode":"olss2","samples":64,"expansions":10000,"cfr_every":64,"portfolio":[{"policy":"greedy"},{"policy":"self","parameters":{"temperature":0.3}}]}' --blue 'kluss 200ms=kluss:{"samples":64,"shadows":4,"expansions":5000,"cfr_every":64}' --games 200 --seed 0 --rules training --threads 8 --record-dir ../research/runs/092a047f-bad7-4d56-98f0-5e28094c6ec9/records --out ../research/runs/092a047f-bad7-4d56-98f0-5e28094c6ec9/matchup.json --tag 092a047f-bad7-4d56-98f0-5e28094c6ec9
cd research && python3 scripts/build_portfolio_assets.py

The other four cells differ only in the contestants' parameters and the game count; the exact commands are in the five run records. The matches took 593 s, 657 s, 2,220 s, 5,553 s and 1,294 s of wall time on 8 threads. Each record carries the SHA-256 of its JSON output.