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 plays
  • get_combination_type(cards) - Identify combination (single, pair, gang, etc.)
  • can_beat(play, trick) - Check if play legally beats trick
  • get_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:

gang-of-four-web-advisor

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 configuration
  • model.safetensors - Model weights (safetensors format)
  • modeling_gangoffour.py - Model code with from_pretrained support
  • game_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
Safetensors
Model size
946k params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support