Building a Turn-Based Battle Game with Python Tkinter

Tkinter is Python's built-in standard GUI library that ships with every Python installation, enabling rapid development of graphical applications.

Creating the Game Interface

The following implementation creates a battle game window using grid-based layout management:

import tkinter as tk

class Fighter:
    def __init__(self, name, health, attack, defense):
        self.name = name
        self.health = health
        self.attack = attack
        self.defense = defense

    def take_damage(self, incoming_damage):
        net_damage = incoming_damage - self.defense
        if self.health > net_damage:
            self.health -= net_damage
        else:
            self.health = 0

    def is_defeated(self):
        return self.health == 0

def simulate_battle():
    p1_name = name_input_left.get()
    p2_name = name_input_right.get()
    p1_atk = int(attack_input_left.get())
    p2_atk = int(attack_input_right.get())
    p1_def = int(defense_input_left.get())
    p2_def = int(defense_input_right.get())

    fighter_a = Fighter(p1_name, 120, p1_atk, p1_def)
    fighter_b = Fighter(p2_name, 100, p2_atk, p2_def)

    result_display.delete("1.0", tk.END)
    
    for round_num in range(1, 100):
        message = f"--- Round {round_num} ---\n"
        result_display.insert(tk.END, message)

        fighter_b.take_damage(fighter_a.attack)
        if fighter_b.is_defeated():
            result_display.insert(tk.END, f"{fighter_a.name} wins!\n")
            return

        fighter_a.take_damage(fighter_b.attack)
        if fighter_a.is_defeated():
            result_display.insert(tk.END, f"{fighter_b.name} wins!\n")
            return

        status = f"{fighter_a.name} HP: {fighter_a.health} | {fighter_b.name} HP: {fighter_b.health}\n"
        result_display.insert(tk.END, status)

# Main window setup
app_window = tk.Tk()
app_window.geometry("800x600")
app_window.title("Battle Arena")
app_window.configure(bg='#505050')

title_label = tk.Label(app_window, bg='#505050', text="Battle Arena", fg='white', font=("Arial", 24, "bold"))
title_label.grid(row=0, column=0, columnspan=2, pady=15)

# Player 1 inputs (left column)
tk.Label(app_window, text="Player 1 Name:", fg='white', bg='#505050', font=("Arial", 14)).grid(row=1, column=0, sticky='w', padx=10, pady=5)
name_input_left = tk.Entry(app_window, width=18, font=("Arial", 14))
name_input_left.grid(row=2, column=0, sticky='w', padx=10, pady=5)

tk.Label(app_window, text="Attack (51-100):", fg='white', bg='#505050', font=("Arial", 14)).grid(row=3, column=0, sticky='w', padx=10, pady=5)
attack_input_left = tk.Entry(app_window, width=18, font=("Arial", 14))
attack_input_left.grid(row=4, column=0, sticky='w', padx=10, pady=5)

tk.Label(app_window, text="Defense (1-80):", fg='white', bg='#505050', font=("Arial", 14)).grid(row=5, column=0, sticky='w', padx=10, pady=5)
defense_input_left = tk.Entry(app_window, width=18, font=("Arial", 14))
defense_input_left.grid(row=6, column=0, sticky='w', padx=10, pady=5)

# Player 2 inputs (right column)
tk.Label(app_window, text="Player 2 Name:", fg='white', bg='#505050', font=("Arial", 14)).grid(row=1, column=1, sticky='w', padx=10, pady=5)
name_input_right = tk.Entry(app_window, width=18, font=("Arial", 14))
name_input_right.grid(row=2, column=1, sticky='w', padx=10, pady=5)

tk.Label(app_window, text="Attack (51-100):", fg='white', bg='#505050', font=("Arial", 14)).grid(row=3, column=1, sticky='w', padx=10, pady=5)
attack_input_right = tk.Entry(app_window, width=18, font=("Arial", 14))
attack_input_right.grid(row=4, column=1, sticky='w', padx=10, pady=5)

tk.Label(app_window, text="Defense (1-80):", fg='white', bg='#505050', font=("Arial", 14)).grid(row=5, column=1, sticky='w', padx=10, pady=5)
defense_input_right = tk.Entry(app_window, width=18, font=("Arial", 14))
defense_input_right.grid(row=6, column=1, sticky='w', padx=10, pady=5)

# Battle result display area
result_label = tk.Label(app_window, text="Combat Log", fg='white', bg='#505050', font=("Arial", 16, "bold"))
result_label.grid(row=8, column=0, columnspan=2, pady=10)

result_display = tk.Text(app_window, height=10, width=60, font=("Courier", 12))
result_display.grid(row=9, column=0, columnspan=2, pady=10)

# Start battle button
start_button = tk.Button(app_window, text="Start Battle", font=("Arial", 16, "bold"), width=18, command=simulate_battle)
start_button.grid(row=7, column=0, columnspan=2, pady=20)

app_window.mainloop()

Game Mechanics

The Fighter class encapsulates player attributes:

  • health: Starting health pool (Player 1: 120, Player 2: 100)
  • attack: Damage dealt per round before defense calculation
  • defense: Damage reduction applied against incoming attacks

The damage formula subtracts the defender's defense value from the attacker's attack value. Each round, both fighters exchange blows sequentially until one reaches zero health.

Enhancement Ideas

  • Introduce an agility/speed stat to determine turn order
  • Generate attack values as random numbers within a specified range
  • Add critical hit mechanics with multiplier bonuses
  • Implement a health regeneration system for certain rounds

Tags: python tkinter gui game-development OOP

Posted on Mon, 11 May 2026 12:38:22 +0000 by inkel