A production-grade reinforcement learning project showcasing Deep Q-Network (DQN) training in a custom 2D game environment. This repository demonstrates the complete ML pipeline: problem formulation, state/reward design, agent training, and competitive gameplay.
Click the image to watch the trained DQN agent in action!
This video demonstrates the final trained agent interacting with the environment in real time.
You can observe decision making, food collection behavior, gravity handling, and failure cases learned through reinforcement learning.
Chubby Bird is not just a gameโit's a reinforcement learning environment where an AI agent learns optimal decision-making through self-play. The agent learns to navigate a dynamic 2D space, collect time-sensitive targets, and maximize survival time.
- Environment: A 2D scrolling game with physics-based bird dynamics
- Objective: Train an AI agent to catch falling food objects while avoiding ground collision
- Challenge: Reward shaping and exploration-exploitation balance in a continuous action environment
- โ Agent learns to actively seek food (vs. passive survival strategies)
- โ Achieves consistent food collection rates after 50 episodes
- โ Beats untrained baseline in competitive gameplay
- โ Trains in ~10 minutes on CPU
The agent observes 4-dimensional state vector at each timestep:
State = [bird_y, bird_velocity, food_dx, food_dy]
โข bird_y : Bird's vertical position (normalized 0-1, 0=top, 1=bottom)
โข bird_velocity: Current vertical velocity (normalized, range -1 to 1.5)
โข food_dx : Horizontal distance to nearest food (normalized -1 to 1)
โข food_dy : Vertical distance to nearest food (normalized -1 to 1)
Design Rationale: This minimal 4D representation captures the essential control problemโvertical positioning and proximity awarenessโwithout computational overhead.
The agent has 2 discrete actions:
action = 0: Do nothing (gravity pulls bird down)
action = 1: Flap wings (apply upward impulse)
Design Rationale: Simple binary control mimics Flappy Bird constraints while remaining Markovian and deterministic.
The reward function was carefully engineered to avoid local optima and reward hacking:
| Trigger | Reward | Purpose |
|---|---|---|
| Collect food | +10.0 | Primary objective |
| Miss food (escape) | -2.0 | Penalize inaction |
| In safe middle region | +0.1 | Exploration incentive |
| At top of screen | -2.0 | Force downward diversity |
| Near ground | -0.5 | Discourage reckless play |
| Within 100px of food | +0.05 | Proximity guidance |
| Per step | -0.005 | Efficiency penalty |
Critical Engineering Decisions:
- No inflated proximity bonus - Removed +0.5/step bonus (agent hovered near food without catching it)
- Only reward actual catches - Not generic "moving toward" (eliminated false positives)
- Random spawn positions - Prevents overfitting to starting in middle
- Score-based model saving - Save on food caught, not total reward (prevents reward gaming)
Deep Q-Network (DQN) Architecture:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Input Layer: 4 neurons (state dimensions)
Hidden Layer 1: 128 neurons + ReLU activation
Hidden Layer 2: 128 neurons + ReLU activation
Output Layer: 2 neurons (Q-values per action)
Episodes: 50
Max steps/episode: 3000
Learning rate: 0.001 (Adam optimizer)
Discount factor (ฮณ): 0.99
Epsilon decay: 0.98 per episode (slower exploration decay)
Epsilon min: 0.10 (maintain 10% exploration)
Batch size: 64
Memory buffer: 5000 experiences
Target update freq: 200 steps- Target Network Freezing: Separate frozen target network updated every 200 steps (prevents feedback loops)
- Gradient Clipping:
clip_grad_norm_(max_norm=1.0)(prevents exploding gradients) - Experience Replay: Mini-batch SGD from randomized memory buffer (breaks temporal correlations)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ GAME LOOP (60 FPS) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโ
โ Get Game State โ
โ [y,v,dx,dy] โ
โโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DQN Agent (Inference Mode) โ
โ โข Forward pass through net โ
โ โข Q(s,a) = [Q_nothing,Q_flap]โ
โ โข action = argmax(Q-values) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโ
โ Execute Action โ
โ Physics update โ
โโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Observe: Reward + Next State โ
โ โข Food collision? +10 โ
โ โข Food escaped? -2 โ
โ โข New state: [y',v',dx',dy'] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Store Transition in Memory โ
โ (state, action, reward, next_state) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Train on Mini-Batch (when ready) โ
โ โข Sample 64 transitions โ
โ โข Forward pass on current net โ
โ โข Compute target Q with frozen net โ
โ โข MSE loss + backprop โ
โ โข Gradient clip + Adam step โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The agent shows clear learning progression. Early episodes incur heavy exploration penalties (-5000 range), stabilizing as the agent learns efficient strategies.
Agent learns to catch 3-5 food items by episode 10 and maintains consistent performance by episode 25, demonstrating stable convergence.
| Challenge | Root Cause | Fix | Result |
|---|---|---|---|
| Reward Hacking | +0.5 proximity bonus caused hovering | Removed proximity bonus, reward only catching | Agent actively catches food |
| Stuck in Local Optima | ฮต decay 0.999/ep โ 0.01 in 7 eps | Changed to 0.98/ep, maintains 0.10 floor | Agent explores all regions |
| Training Instability | No gradient clipping, freq updates | Added clip_grad_norm(1.0), 200-step targets | Stable convergence |
| Poor Generalization | Always spawn at middle (y=0.5) | Random spawn [50px, height-100px] | Robust at any position |
| Distribution Shift | Model assumes middle-start position | Randomize bird Y during training | Handles diverse initial states |
Reward Shaping Pitfall: Initial +0.5/step bonus for being "close to food" caused reward hackingโagent learned to hover near targets for points without actually catching them. Solution: Only reward terminal actions (food catch +10, food escape -2).
Exploration Decay: Epsilon 0.999/step converged too fast, locking into "stay at top" strategy. Slower decay (0.98/episode) with 0.10 floor maintained 10% exploration throughout training, enabling discovery of diverse strategies.
Gradient Stability: No clipping caused exploding gradients during food-rich episodes. Clip_grad_norm(max_norm=1.0) prevented divergence while maintaining learning speed.
# 1. Install dependencies
pip install -r requirements.txt
# 2. Play
python main.pypython train.py
# Trains for 50 episodes, saves assets/model/best_model.pth
# Shows live rendering during training (~15 min)
# Edit train.py config for faster headless training- Control with SPACEBAR to flap
- Collect falling food for points
- Game ends on ground collision
- Purpose: Understand game mechanics
- Watch the trained agent play
- No user input
- Shows AI's decision-making in action
- Purpose: Verify training effectiveness
- Alternating turns: Player then AI
- Each player tries to catch as much food as possible
- First to 10 points wins
- Purpose: Competitive benchmark
Chubby Bird/
โโโ main.py # Entry point
โโโ launcher.py # Game mode router
โโโ train.py # DQN training script
โโโ requirements.txt # Dependencies
โโโ assets/model/best_model.pth # Trained agent weights
โ
โโโ src/
โ โโโ settings.py # Game constants
โ โโโ agent.py # DQN model + training logic
โ โโโ env.py # Training environment
โ โโโ game.py # Base game loop
โ โโโ vs_game.py # Competitive mode
โ โโโ menu_simple.py # Menu UI
โ โโโ bird.py # Physics + rendering
โ โโโ food.py # Food spawning
โ
โโโ assets/
โโโ images/ # Sprites & backgrounds
โโโ sounds/ # Audio files
| Parameter | Value | Purpose |
|---|---|---|
| Episodes | 50 | Total training runs |
| Max steps/episode | 3000 | Timeout per episode |
| Learning rate | 0.001 | Adam optimizer |
| Discount factor (ฮณ) | 0.99 | Future reward weight |
| Epsilon decay | 0.98/ep | Exploration schedule |
| Epsilon min | 0.10 | Min exploration rate |
| Batch size | 64 | SGD mini-batch |
| Memory buffer | 5000 | Experience replay size |
| Target update | 200 steps | Frozen network sync |
- Manual Play: Control with SPACEBAR
- AI Play: Watch trained agent
- You vs AI: Competitive mode (first to 10 wins)
โ
Deep Q-Learning (DQN)
โ
Experience replay & target networks
โ
Reward shaping in practice
โ
Hyperparameter tuning
โ
Agent evaluation metrics
โ
Competitive benchmarking
Mansoor Bukhari
- GitHub: @cyberfantics
- LinkedIn: linkedin.com/in/mansoor-bukhari
MIT License - Use freely for learning and development.
- Inspired by Flappy Bird and DQN paper (Human-level control through deep RL)
- Thanks to Pygame community for excellent documentation
- PyTorch team for intuitive deep learning APIs


