Skip to content

Repository files navigation

πŸ₯‹ Zoorkhane Fighter β€” AI Agent Project

Course: Artificial Intelligence & Expert Systems
Instructor: Dr. Salimi-Badr

Overview

This project implements an AI agent for a two-player fighting game called Pixel Warrior / Zoorkhane Fighter. Two agents compete in real-time combat, each starting with 100 HP. The first agent to reduce the opponent's HP to zero wins. Each agent is an independent program that receives the current game state as JSON input and returns an action as JSON output every frame.

The project explores adversarial AI techniques, specifically Minimax search with alpha-beta pruning and heuristic evaluation functions, to build a competitive fighting game agent.


Project Structure

.
β”œβ”€β”€ GAMECODE-python.py   # Main game loop and rendering (Pygame)
β”œβ”€β”€ fighter.py           # Fighter class β€” handles animation, physics, agent I/O
β”œβ”€β”€ agent.py             # Primary AI agent (Minimax + heuristic)
β”œβ”€β”€ random-agent.py      # Secondary agent (Markov model + strategy selection)
β”œβ”€β”€ health_bar.png       # HUD asset
β”œβ”€β”€ p1.png               # Player 1 victory screen
β”œβ”€β”€ p2.png               # Player 2 victory screen
└── README.md

Additional required assets (backgrounds, character spritesheets, fonts, sounds) must be present in their respective folders.


How It Works

Game Loop

The main game (GAMECODE-python.py) runs at 60 FPS using Pygame. Each frame, it calls Fighter.move() for both fighters. For AI-controlled fighters, the fighter class serializes the current game state to JSON, pipes it to the agent script via subprocess, and reads back the chosen action.

The game supports three modes, configurable via game_mode in GAMECODE-python.py:

Mode Description
ai_vs_ai Both players controlled by AI agents
player_vs_ai Player 1 is human, Player 2 is AI
player_vs_player Both players are human

Agent Interface

Each agent reads a JSON object from stdin and writes a JSON action to stdout.

Input:

{
  "fighter": {
    "x": int,
    "y": int,
    "health": int,
    "attacking": bool,
    "attack_cooldown": [int, int],
    "jump": bool,
    "dash_cooldown": int
  },
  "opponent": {
    "x": int,
    "y": int,
    "health": int,
    "attacking": bool
  },
  "saved_data": {}
}

Output:

{
  "move": "left" | "right" | null,
  "attack": 1 | 2 | null,
  "jump": bool,
  "dash": "left" | "right" | null,
  "debug": any,
  "saved_data": {}
}

saved_data persists across frames and can be used to store any state your agent needs between calls.


AI Agents

agent.py β€” Minimax Agent

The primary agent uses Minimax search with alpha-beta pruning (depth 3) to select the best action each frame.

Key components:

  • assess_position(fighter, opponent) β€” Heuristic evaluation function. Scores a game state based on:

    • Health differential (highest weight)
    • Distance to opponent (attack range pressure)
    • Attack cooldown readiness
    • Vertical alignment (both on ground vs. one in air)
    • Position on screen (penalizes corner positioning)
  • simulate_action(fighter, opponent, action, is_fighter_turn) β€” Simulates the effect of a single action without modifying actual game state.

  • minimax(fighter, opponent, depth, is_maximizing, alpha, beta) β€” Standard minimax with alpha-beta pruning. The maximizing player is the agent; the minimizing player is the opponent.

  • choose_best_move(fighter_info, opponent_info, saved_data) β€” Entry point. Evaluates all legal actions and returns the one with the highest minimax score.

Available actions considered:
MOVE_FORWARD, MOVE_BACKWARD, ATTACK, HEAVY_ATTACK, DASH_FORWARD, DASH_BACKWARD, JUMP_IN_PLACE


random-agent.py β€” Adaptive Strategy Agent

The secondary agent uses a Markov model to predict opponent behavior and adapts its strategy dynamically.

Key components:

  • Opponent modeling: Tracks action transition probabilities to predict the opponent's next move.
  • Strategy selection: Chooses between aggressive, defensive, counter, press, or balanced based on health differential and opponent aggression score.
  • Dodge logic: With 80% probability, attempts to dodge predicted attacks via dash or jump.
  • Optimal positioning: Calculates ideal distance from the opponent based on current strategy.

Game Mechanics Reference

Mechanic Details
Screen size 1000 Γ— 540 px
Ground Y 380
Jump height 170 (minimum Y)
Hitbox 120 wide Γ— 180 tall, centered on fighter
Attack range < 180 px horizontal distance to connect
Movement speed 5 px/frame
Light attack 10 damage, 25-frame cooldown
Heavy attack 20 damage, 100-frame cooldown
Dash 300 px over 10 frames, 40-frame cooldown
Time limit 3600 frames (60 seconds)

Running the Project

Requirements

pip install pygame numpy

Run

python GAMECODE-python.py

Switching Agents

To change which agent file is used, edit the agent1_info or agent2_info dictionaries in GAMECODE-python.py:

agent1_info = {
    'enabled': True,
    'language': 'python',
    'path': os.path.join(os.path.dirname(__file__), 'agent.py')
}

Changing Game Mode

game_mode = "ai_vs_ai"       # AI vs AI
# game_mode = "player_vs_ai" # Human vs AI
# game_mode = "pvp"          # Human vs Human

Design Notes

Heuristic Function

A good heuristic balances multiple factors. The general form used in agent.py:

score = (health_diff Γ— w1) + (range_advantage Γ— w2) + (cooldown_bonus Γ— w3) βˆ’ (corner_penalty Γ— w4) βˆ’ ...

Weights were tuned empirically. Key insights:

  • Health difference is the dominant term.
  • Being in attack range with a ready cooldown is highly rewarded.
  • Being cornered is strongly penalized.
  • Vertical misalignment (one fighter in air, one on ground) is penalized as attacks miss.

Alpha-Beta Pruning

Minimax at depth 3 with alpha-beta pruning significantly reduces the search space. The opponent is modeled conservatively (assumes it plays optimally from a subset of moves), while the agent considers its full action space.


Notes & Limitations

  • Each agent call must complete within 0.4 seconds or the frame is skipped.
  • If the agent crashes, the fighter loses by default.
  • saved_data must be JSON-serializable.
  • Agents do not share state β€” each runs as a separate subprocess.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages