Saihai (ιι ) β a classical game-AI agent for gateball
πͺπΈ Leer en espaΓ±ol
Saihai is the AI "kantoku" (team manager) of a gateball game. It is not a language model and not a neural network. It is a classical game-AI agent:
- Planning by simulation. For every stroke it generates candidate strokes (gates, touches on rival balls, sparks, re-entries, waiting playsβ¦), simulates each one with the real rules engine and physics, including the execution error of the player, and keeps the best few.
- Evaluation with learned weights. Each simulated outcome is scored by a hand-built evaluation function (points, risk of being hit, touches, sparks, positioningβ¦) whose 31 numbers were learned by self-play with sep-CMA-ES (a derivative-free evolution strategy).
- Kantoku rules. On top of that sit explicit, readable rules a manager would apply: when to take a ball to the pole (agari), how to bring a far ball closer by chaining touches (tsunagi), how to treat the last turns of the match, and how to avoid leaving balls exposed.
The code (rules engine, physics, planner, training and measurement tools) is in the GitHub repository; this Hugging Face repo hosts the weights and this card.
What is in this repo
| File | What it is |
|---|---|
saihai-v1.4.json |
The weights of model saihai-v1.4 (identical to src/game/cpuWeights.json at tag v1.4.2). 45 values in total: 31 learned genes (sep-CMA-ES) = 21 base weights + 8 phase multipliers (4 key terms Γ opening and endgame) + the 2 balance slopes leadSlope and clockSlope (the latter learned at 0, i.e. neutral). The other 14 are fixed values or configuration: 5 rule constants in rules (contAttack, heapRisk, heapValue, heapSparkP, rivalSupport; genes in v3, kept fixed in v4), 5 riskWeights (all 1), 2 planner settings (sims), the advanced switch and version. |
attackTable.json |
A precomputed lookup table, not learned weights: how often a rival's touch attempt on one of my balls ends in each outcome (valid touch and spark, my ball knocked out, both outβ¦), simulated with the real physics for a grid of distances to the court line, shot angles and shot distances. One table for a human-like rival and one for the CPU. Generated by npm run lineplay -- --write. |
The files are plain JSON, read by the TypeScript package; they are not loadable with transformers or any ML framework.
How it was trained
- Algorithm: sep-CMA-ES (diagonal-covariance CMA-ES), chosen because the fitness is noisy (each match depends on luck on the grass and on execution errors) and there are only ~30 parameters. The released weights are the mean of the final generation, not the best candidate.
- Population: 24 candidates per generation, starting from hand-set weights.
- Fitness: wins (a draw counts Β½) + 0.02 Γ point difference per match, plus a small term from a battery of behaviour checks (
behavior). - Each generation: matches in color-swapped pairs with shared random draws against hand-set weights and against the rest of the population; β40 % of the pairs start from end-of-match positions taken from a bank of generated positions.
- Execution error: a realistic human-level error (aim 0.056 m, power 0.022, direction 0.022), scaled per player and per distance by randomly drawn "certainty" profiles, so it learns a general rule and not a fixed team.
- Lineage:
saihai-v1.4= the weights of the v4 training run (24 generations of a population of 24 candidates) + fixed kantoku rules, added both before and after the v4 training, up to v1.4. The weights were not retrained after the later additions. The rules are fixed and were defined by the author's own judgement as a gateball player and kantoku, not learned. A brief list: attacking after a gate, defending the line, openings, taking a ball to the pole (agari) at the right time, tsunagi, double angle, touches on teammates, re-entries, the last turn, spark that avoids own balls, and clustering/advancing depending on who plays again. Some rule constants are candidates to become learned genes in a future retraining.
Full history and honest notes on every change, including those that did not help: TRAINING.md (Spanish).
Results
All figures are for saihai-v1.4 with the default seeds of the repository tools, a goal pole of 0.025 m and the certainty of the "Kantoku IA" level of the demo game. Matches are played in color-swapped pairs. "Β±" is the 95 % interval reported by the repository's tools. The reference baseline and commands are in the repository's CLAUDE.md.
| Measurement | Condition | Result |
|---|---|---|
h2h β head to head against the weights of the previous model, saihai-v1.3 |
200 matches, --seed 8300000 |
46.5 % (85 wins / 16 draws / 99 losses), β0.38 points per match. Statistically even (interval β Β±6.9): v1.4 did not become measurably stronger than v1.3 in self-play; it changed how it plays in specific documented situations. |
humanduel β against a human-like rival (plays well, but with the execution error of a good human and extra power on touch attempts) |
200 matches, default seed | 84.5 % score (draws count Β½), +7.00 points per match |
behavior β battery of situations taken from real matches |
4 seeds per scenario | 98 % of the checks pass |
selfplay --games 8 --levels 4,1 --seed 1 |
small sanity run | 5 wins / 2 draws / 1 loss, 20.1β16.4 points |
Time per stroke (bench:plan --kantoku) |
Node, one core | β 55β60 ms |
Note: the first measurements of v1.4 used a 0.05 m goal pole (h2h 49.8 %, humanduel 87.3 %); after the pole was set to 0.025 m the same weights give the figures above. The weights did not change.
Limitations
- Calibrated against a model of a human rival, not against real people at scale.
opponent: 'human'makes Saihai assume the execution error of a good human player; thehumanduelrival is a simulated human (a planner executing with human error). It has been validated against real players only in matches played by the author, who is both the player and the gateball kantoku. - Tied to this engine. The weights make sense only with this rules engine, physics and the two-bar execution mechanic. If the rules, the physics or the error model (
CONFIGvalues) change, the weights should be re-validated or retrained. - Known weak spots are documented, not hidden. Situations taken from the author's real matches (named after the turn in which they appeared, e.g. T12, a re-entering ball that stayed glued to a rival ball) were fixed with explicit rules and are covered by the
behaviorbattery and the tests. Other situations of the same kind may exist that nobody has seen yet. - Self-play strength has plateaued. The latest versions improved specific behaviours but did not beat the previous one head to head.
- Game design choices (e.g. a match always ends after a White ball has played) are listed in RULES.md.
How to use it
Install the package from GitHub (the weights are bundled in it; you do not need to download these files to use Saihai):
npm install github:ckomiya/saihai-gateball#v1.4.2
import { createMatch, Saihai, playShot } from 'saihai-gateball';
const match = createMatch({ seed: 42 }); // new match (seeded randomness)
const saihai = new Saihai({ opponent: 'human' }); // judges its rival as a person would
while (match.stage !== 'done') {
const shot = saihai.chooseShot(match); // { startX, aim, power, accuracy, plan }
playShot(match, shot); // applies the stroke and lets the balls roll
match.proceed(); // next stroke or turn
}
console.log(match.teamScore(0), match.teamScore(1));
The full API, the events table and the glossary of gateball terms are in the GitHub README. Texts can be Spanish or English (lang: 'es' | 'en').
Links
- Code: https://github.com/ckomiya/saihai-gateball
- Play against Saihai in your browser (Hugging Face Space): https://huggingface.co/spaces/ckomiya/saihai-gateball-demo
- Same demo on GitHub Pages: https://ckomiya.github.io/gateball-retro/demo/
Author and license
Christian Komiya. Code and weights under the MIT license. No assets, code or text from any commercial game are used; the project is an original implementation.