Gang of Four Neural AI v3
A neural network model for playing the Gang of Four card game (Chinese climbing game).
Model Description
This model predicts optimal card plays through imitation learning from expert strategy demonstrations. It uses a custom architecture with card attention mechanisms to capture relationships between different card regions.
Architecture
- Card Attention: Multi-head self-attention over 4 card regions (hand, played, trick, opponent estimates)
- Residual Blocks: 3 residual MLP blocks with LayerNorm and GELU activation
- Dual Heads: Separate policy head (40 actions) and declaration head (binary)
- Parameters: ~920K trainable parameters
Input Encoding (328 features)
| Range | Description |
|---|---|
| 0-63 | Player's hand cards (64 card slots) |
| 64-127 | Cards played this game |
| 128-191 | Current trick cards to beat |
| 192-255 | Opponent card estimates |
| 256-295 | Action mask (40 valid actions) |
| 296-327 | Context features (scores, positions, etc.) |
Output
- action_logits: (batch, 40) logits for each action (action 0 = pass)
- declare_prob: (batch, 1) probability to declare last card ("Carte!")
Usage
Quick Start (Complete Example)
from huggingface_hub import hf_hub_download
import torch
import sys
# Download all necessary files
for filename in ["modeling_gangoffour.py", "game_utils.py", "rules.py", "config.json", "model.safetensors"]:
hf_hub_download(repo_id="quintana42/gang-of-four-neural", filename=filename, local_dir="./model")
sys.path.insert(0, "./model")
from modeling_gangoffour import GangOfFourNet
from game_utils import Card, GameEncoder, decode_action
# Load model
model = GangOfFourNet.from_pretrained("./model")
model.eval()
# Parse your hand from string notation
hand = Card.parse_hand("1G 3R 5Y 7G 10R Dragon")
print(f"Hand: {[str(c) for c in hand]}")
# Define valid plays (in a real game, comes from game rules)
valid_plays = [
[], # Pass
[hand[0]], # Play 1G
[hand[1]], # Play 3R
[hand[2]], # Play 5Y
]
# Encode the game state
encoder = GameEncoder()
state, ordered_plays = encoder.encode_simple(
hand=hand,
valid_plays=valid_plays,
is_leading=True, # We're leading (no trick to beat)
)
# Run inference
state_tensor = torch.tensor(state).unsqueeze(0)
mask_tensor = torch.tensor(state[256:296]).unsqueeze(0)
with torch.no_grad():
logits, declare_prob = model(state_tensor, mask_tensor)
# Decode the result
action_idx = logits.argmax(dim=1).item()
chosen_play = decode_action(action_idx, ordered_plays)
if chosen_play is None:
print("Model chose: PASS")
else:
print(f"Model chose: {[str(c) for c in chosen_play]}")
print(f"Declare last card probability: {declare_prob.item():.3f}")
Card Notation
Parse cards using simple string notation:
from game_utils import Card
# Single cards
card = Card.parse("5G") # 5 Green
card = Card.parse("10R") # 10 Red
card = Card.parse("1M") # Multi-colored 1
card = Card.parse("Dragon") # Dragon
card = Card.parse("PhoenixG") # Phoenix Green
# Multiple cards
hand = Card.parse_hand("1G 3R 5Y Dragon PhoenixY")
Card Index Mapping
The 64 cards are mapped to indices 0-63:
| Index | Card |
|---|---|
| 0-1 | 1 Green (2 copies) |
| 2-3 | 1 Yellow (2 copies) |
| 4-5 | 1 Red (2 copies) |
| 6-7 | 2 Green (2 copies) |
| ... | ... |
| 58-59 | 10 Red (2 copies) |
| 60 | Multi-colored 1 |
| 61 | Phoenix Green |
| 62 | Phoenix Yellow |
| 63 | Dragon |
Formula for numbered cards: (rank - 1) * 6 + color_idx * 2 + copy
where color_idx: GREEN=0, YELLOW=1, RED=2
Action Encoding
Actions are encoded dynamically based on valid plays:
- Action 0: Always PASS
- Actions 1-39: Valid plays sorted by (length, sum of ranks, sum of colors)
The ordered_plays list returned by the encoder maps action indices to actual plays.
Generating Valid Plays
Use rules.py to generate valid plays according to game rules:
from game_utils import Card
from rules import get_valid_plays, get_combination_type, can_beat
# Your hand and the trick to beat
hand = Card.parse_hand("4G 4Y 4R 4G 7R 7Y 10G")
trick = Card.parse_hand("6G 6R") # Pair of 6s
# Get all legal plays
valid_plays = get_valid_plays(hand, trick_to_beat=trick)
for play in valid_plays:
if play:
combo_type = get_combination_type(play)
print(f"{combo_type}: {[str(c) for c in play]}")
else:
print("PASS")
# Output:
# pair: ['7R', '7Y']
# gang_of_four: ['4G', '4Y', '4R', '4G'] # Gang beats anything!
# PASS
# Check if a specific play beats a trick
play = Card.parse_hand("8G 8Y")
print(can_beat(play, trick)) # True
Key functions in rules.py:
get_valid_plays(hand, trick_to_beat)- Get all legal playsget_combination_type(cards)- Identify combination (single, pair, gang, etc.)can_beat(play, trick)- Check if play legally beats trickget_all_combinations(hand)- Get all possible combinations from hand
Using from_pretrained
from modeling_gangoffour import GangOfFourNet
# Load from Hugging Face Hub
model = GangOfFourNet.from_pretrained("quintana42/gang-of-four-neural")
# Or load from local directory
model = GangOfFourNet.from_pretrained("./my_local_model")
# Use GPU
model = GangOfFourNet.from_pretrained("quintana42/gang-of-four-neural", device="cuda")
Save Your Own Model
# After training
model.save_pretrained("./my_trained_model")
Examples
Web Advisor (WASM)
A complete browser-based example using ONNX Runtime Web:
Features:
- 100% client-side (runs offline after initial load)
- Uses ONNX model with WebAssembly inference
- Complete rules implementation in JavaScript
- No frameworks, vanilla JS
Training
The model was trained using imitation learning from an expert heuristic strategy:
- Dataset: ~500K game state-action pairs
- Training: Cross-entropy loss for actions, BCE for declarations
- Optimizer: AdamW (lr=1e-3, weight_decay=0.01)
- Epochs: 50 with early stopping (patience=10)
Game Rules
Gang of Four is a Chinese climbing card game similar to Big Two/Tichu:
- Deck: 64 cards (numbers 1-10 in 3 colors x 2 copies, plus Dragon and 2 Phoenix)
- Goal: Be first to empty your hand
- Combinations: Single, Pair, Triple, Straight, Flush, Full House, Straight Flush, Gang (4+ of a kind)
- Scoring: Penalty points for cards remaining; first to 100 loses
Files
config.json- Model configurationmodel.safetensors- Model weights (safetensors format)modeling_gangoffour.py- Model code withfrom_pretrainedsupportgame_utils.py- Encoding/decoding utilities (Card, GameEncoder, decode_action)rules.py- Game rules (get_valid_plays, can_beat, get_combination_type)
Requirements
torch>=2.0.0
safetensors>=0.4.0
huggingface_hub>=0.20.0
Citation
@misc{gangoffour-neural,
author = {quintana42},
title = {Gang of Four Neural AI},
year = {2026},
publisher = {Hugging Face},
url = {https://huggingface.co/quintana42/gang-of-four-neural}
}
License
MIT License
- Downloads last month
- 3