A 2D stick figure fighting game built with Pygame. Play against scripted AI, train a clone that learns your style, then face an adaptive AI that exploits your patterns.
pip install -r requirements.txt
python main.py| Key | Action |
|---|---|
| A / Left Arrow | Move left |
| D / Right Arrow | Move right |
| W / Up Arrow | Jump |
| J | Punch (fast, low damage) |
| K | Kick (slower, more damage) |
| L | Uppercut (slow, high damage) |
| Space | Block |
| ESC | Quit |
| R | Restart (after game over) |
# VS scripted AI (default) — generates training data
python main.py
python main.py --p2 scripted --p2-aggression 0.8
# VS random AI
python main.py --p2 random
# VS your trained clone (imitation model)
python main_vs_clone.py
python main_vs_clone.py --temperature 0.5
python main_vs_clone.py --deterministic
# VS adaptive AI (blends imitation + counter model)
python main_vs_adaptive.py
python main_vs_adaptive.py --temperature 0.8
python main_vs_adaptive.py --start-alpha 1.0Play sessions against the scripted AI. Recordings are saved automatically to recordings/.
python main.pyTrains an LSTM to predict your actions from gameplay recordings.
python -m training.train_imitationTrains a PPO agent to exploit your predicted patterns by playing against the imitation model.
python -m training.train_ppoBlends both models with a difficulty schedule that ramps up each round.
python main_vs_adaptive.py| File | Purpose |
|---|---|
config.py |
All tunable parameters (damage, speed, screen size, etc.) |
actions.py |
Action enum — the shared language between human and AI |
fighter.py |
Fighter state machine (movement, attacks, physics) |
controllers.py |
Human keyboard input + scripted AI controllers |
game_utils.py |
Collision detection and game state building |
game_loop.py |
Shared game loop used by all game modes |
renderer.py |
Stick figure drawing and UI |
recorder.py |
Logs every frame to .jsonl for training |
env.py |
Gymnasium-style wrapper for RL training |
main.py |
VS scripted/random AI |
main_vs_clone.py |
VS your trained imitation clone |
main_vs_adaptive.py |
VS adaptive difficulty AI |
training/imitation_model.py |
LSTM model for imitation learning |
training/imitation_controller.py |
Game controller for imitation model |
training/train_imitation.py |
Imitation training script |
training/dataset.py |
Loads recordings into training sequences |
training/ppo_model.py |
Actor-Critic network for PPO |
training/train_ppo.py |
PPO counter model training script |
training/ppo_controller.py |
Game controller for PPO counter model |
training/difficulty_blender.py |
Blends imitation + counter with alpha schedule |
Each play session creates a .jsonl file in recordings/. Each line is one frame:
{
"frame": 142,
"p1_state": [0.25, 1.0, 0.85, ...],
"p2_state": [0.75, 1.0, 0.70, ...],
"distance": 0.50,
"p1_action": 4,
"p2_action": 1,
"p1_hp_delta": 0,
"p2_hp_delta": -8,
"round": 1,
"round_timer_frac": 0.35,
"p1_wins": 1,
"p2_wins": 0
}from env import FightEnv
env = FightEnv(opponent_type="scripted", render=False)
obs = env.reset()
for step in range(10000):
action = 4 # punch
obs, reward, done, info = env.step(action)
if done:
obs = env.reset()