- Tongits RL Champions & Persona Archetypes
Tongits RL Champions & Persona Archetypes
Reinforcement learning policies trained for Tongits.io, an online implementation of Tongits (a 3-player imperfect-information card game popular in the Philippines).
This repository contains 9 policy checkpoints: the base production champion (Carolina Reaper) and 8 fine-tuned persona archetypes trained with reward shaping to produce distinct tactical styles.
All models are exported in ONNX format for inference across Python (onnxruntime), Node.js (onnxruntime-node), and browsers (onnxruntime-web). A PyTorch state dict (policy-champion.pt) is also provided.
Model Roster & Persona Archetypes
| File | Pepper / Persona | Archetype | Win Rate vs Control | Money Delta / Hand | Characteristic Tactical Behavior | Size |
|---|---|---|---|---|---|---|
policy-champion.onnx |
Carolina Reaper (Champion) | Champion Tier | 28.3% | Baseline (Control) | Balanced baseline play | 2.84 MB |
policy-tongits-hunter.onnx |
Serrano (Tongits Hunter) | Tongits Hunter | 29.4% vs 28.3% | +0.085 ± 0.074 / hand | Fast hand clearance and early melds | 2.84 MB |
policy-aggressive-fighter.onnx |
Habanero (Aggressive Fighter) | Aggressive Fighter | 31.2% vs 28.3% | +0.103 ± 0.076 / hand | Frequent fight calls (+57% fight rate) | 2.84 MB |
policy-secret-hunter.onnx |
Sili Labuyo (Secret Hunter) | Secret Hunter | 28.9% vs 28.3% | +0.072 ± 0.071 / hand | Retains concealed melds for Secret/Sagasa wins | 2.84 MB |
policy-safe-grinder.onnx |
Poblano (Defensive Grinder) | Defensive Grinder | 28.5% vs 28.3% | +0.041 ± 0.068 / hand | Deadwood minimization and burn avoidance | 2.84 MB |
policy-wild-bluffer.onnx |
Ghost Pepper (Wild Bluffer) | Wild Bluffer | 32.6% vs 28.3% | +0.092 ± 0.075 / hand | Wide-range fight caller (+128% fight rate) | 2.84 MB |
policy-chow-fiend.onnx |
Jalapeño (Chow Fiend) | Chow Fiend | 30.5% vs 28.3% | +0.071 ± 0.071 / hand | High discard pickup and open meld frequency | 2.84 MB |
policy-deep-cover.onnx |
Cayenne (Deep-Cover Hoarder) | Deep-Cover Hoarder | 27.5% vs 28.3% | +0.124 ± 0.071 / hand | Conceals melds until endgame (644 deck-empty wins) | 2.84 MB |
policy-sapaw-saboteur.onnx |
Scotch Bonnet (Sapaw Saboteur) | Sapaw Saboteur | 27.7% vs 28.3% | -0.017 ± 0.075 / hand | High sapaw rate onto opponent melds | 2.84 MB |
Game Overview
Tongits (or Tong-its) is a 3-player rummy-type card game played with a standard 52-card deck. For detailed game rules, see Pagat:
- Imperfect Information: Players conceal hand cards while tracking discards, public melds, and opponent declarations.
- Board Mechanics:
- Chow / Meld: Forming sets (3 or 4 cards of same rank) or sequences (3 or more consecutive cards of same suit).
- Sapaw: Laying off cards from hand onto any existing exposed meld on the table.
- Fight / Showdown: Challenging table opponents when holding low deadwood points.
- Sunog (Burn): Players without any exposed meld by the end of the round forfeit stakes and pay dues.
- Instant Wins: Special instant-win conditions include Tongits (emptying hand), Secret (concealed 4-of-a-kind), Sagasa (drawing the 4th card to an exposed set), and Bisaklat (perfect initial deal).
Model Architecture & Wire Contract
All models share a multi-layer perceptron architecture:
graph LR
OBS["Observation Vector (349 floats)"] --> T1["Trunk Layer 1 (512, Mish)"]
T1 --> T2["Trunk Layer 2 (512, Mish)"]
T2 --> T3["Trunk Layer 3 (512, Mish)"]
T3 --> POL["Policy Head (75 logits)"]
T3 --> VAL["Value Head (1 float)"]
POL --> MASK["Action Mask (75 bools)"]
MASK --> ACT["Sampled / Greedy Action"]
Technical Specification
- Observation Space: 349 float features normalized to
[0.0, 1.0]or standard normal:0..51: Card locations (hand, discard, table, concealed).52..103: Discard history and opponent draw patterns.104..259: Meld compositions and sapaw attachment surfaces.260..309: Hand size counts, opponent exposed card counts, and relative deadwood estimates.310..344: Economy and betting parameters (ante, fight fee, secret bonus multipliers).345..348: Match pool streak features (streak_len,streak_won,opp_streak_len,opp_streak_won).
- Action Space: 75 discrete action slots covering Draw from Deck, Chow from Discard, Expose Meld, Sapaw on Target, Call Fight, Accept Challenge, Fold, and Discard.
- Action Masking: A companion boolean mask vector (
[75]) marks legal actions. Illegal actions are masked to-1e9prior to softmax sampling.
Quickstart: Python (onnxruntime)
import numpy as np
import onnxruntime as ort
from huggingface_hub import hf_hub_download
# 1. Download model from Hugging Face Hub
model_path = hf_hub_download(
repo_id="ricoz/tongits-rl-champions",
filename="policy-wild-bluffer.onnx"
)
# 2. Initialize ONNX Runtime session
session = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
# 3. Prepare inputs (batch_size=1)
obs = np.zeros((1, 349), dtype=np.float32) # 349-dim state vector
mask = np.ones((1, 75), dtype=np.float32) # 75-dim boolean legal action mask
# 4. Run inference
logits, value = session.run(["logits", "value"], {"obs": obs, "mask": mask})
# 5. Select action (greedy or temperature-sampled)
action = int(np.argmax(logits[0]))
print(f"Selected action index: {action}, estimated state value: {value[0][0]:.3f}")
Quickstart: Node.js / TypeScript (onnxruntime-node)
import * as ort from 'onnxruntime-node';
async function main() {
// 1. Load the ONNX model
const session = await ort.InferenceSession.create('./policy-deep-cover.onnx');
// 2. Wrap state & mask into tensors
const obsTensor = new ort.Tensor('float32', new Float32Array(349), [1, 349]);
const maskTensor = new ort.Tensor('float32', new Float32Array(75).fill(1), [1, 75]);
// 3. Execute model
const feeds = { obs: obsTensor, mask: maskTensor };
const results = await session.run(feeds);
const logits = results.logits.data as Float32Array;
const bestAction = logits.indexOf(Math.max(...Array.from(logits)));
console.log('Optimal action:', bestAction);
}
main();
Quickstart: Browser / WebAssembly (onnxruntime-web)
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js"></script>
<script>
async function run() {
const session = await ort.InferenceSession.create(
'https://huggingface.co/ricoz/tongits-rl-champions/resolve/main/policy-champion.onnx'
);
const obs = new ort.Tensor('float32', new Float32Array(349), [1, 349]);
const mask = new ort.Tensor('float32', new Float32Array(75).fill(1), [1, 75]);
const output = await session.run({ obs, mask });
console.log('Action logits:', output.logits.data);
}
run();
</script>
Training Pipeline & Reproducibility
- Warm Start via Behavioral Cloning (BC): Pretrained on 50,000 expert heuristic hands to establish rule adherence and baseline card counting.
- PPO Self-Play League: Trained over 176 checkpoint iterations (352,000 hands) with Generalized Advantage Estimation (GAE lambda = 0.95), PPO clip ratio epsilon = 0.2, and entropy schedule decay.
- Constrained Reward Shaping: Persona fine-tuning applies linear reward additives (fee units * stake) on top of the zero-sum economy payoff, steering policies into specialized tactical behaviors without destabilizing game-theoretic baseline play.
- Anti-Collusion Drift Guard: Candidates must maintain positive expected value against an anchored heuristic pool (>= 0.0 drift margin) to ensure self-play policies do not degenerate into mutually exploitative loops.
License & Attribution
All model weights, architectures, and cards in this repository are licensed under the Apache 2.0 License.
If you use these models, benchmark data, or environment specifications in research, cite:
@misc{tongits_rl_2026,
title={Tongits RL: Multi-Agent Reinforcement Learning for 3-Player Imperfect-Information Filipino Card Games},
author={Rico Zuñiga},
year={2026},
howpublished={\url{https://huggingface.co/ricoz/tongits-rl-champions}},
publisher={Hugging Face}
}