ساخت بازی Tic Tac Toe پیشرفته با پایتون

پروژه‌های بازی‌سازی پایتون

پروژه ۷ دقیقه مطالعه
فهرست مطالب
این مطلب از سایت tool.hamidvalad.ir گرفته شده است

معرفی

بازی Tic Tac Toe یک بازی کلاسیک است که در این پروژه با رابط کاربری مدرن و امکانات پیشرفته پیاده‌سازی شده است. این برنامه از کتابخانه tkinter برای ایجاد رابط گرافیکی استفاده می‌کند و دارای ویژگی‌هایی مانند انیمیشن‌های پویا، سیستم امتیازدهی، افکت‌های بصری و طراحی مدرن است.

eggcatcher

نصب و پیش‌نیازها

برای اجرای این پروژه نیاز به پایتون نسخه 3.6 یا بالاتر دارید. کتابخانه‌های مورد نیاز به صورت پیش‌فرض در پایتون موجود هستند:

  • tkinter - برای رابط گرافیکی
  • math - برای محاسبات ریاضی

معماری بازی

ساختار اصلی بازی شامل بخش‌های زیر است:

  • کلاس AnimatedButton برای دکمه‌های متحرک
  • مدیریت وضعیت بازی و نوبت بازیکنان
  • سیستم تشخیص برنده و تساوی
  • انیمیشن‌های پیروزی و تساوی
  • سیستم امتیازدهی پیشرفته
  • رابط کاربری مدرن با تم رنگی جذاب

ویژگی‌های اصلی

این بازی دارای قابلیت‌های متعددی است:

  • رابط کاربری مدرن با تم تیره
  • انیمیشن کلیک روی دکمه‌ها
  • افکت‌های پیروزی با کانفتی
  • هایلایت خانه‌های برنده
  • سیستم امتیازدهی
  • صفحه امتیازات مجزا
  • انیمیشن پالس برای خانه‌های برنده
  • پیام‌های تعاملی زیبا

کدنویسی پروژه

کد کامل پروژه

Python
from tkinter import *
from tkinter import messagebox
import random

# Game variables
Player1 = 'X'
stop_game = False
scores = {'X': 0, 'O': 0, 'Ties': 0}

class AnimatedButton(Button):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.default_bg = self.cget('bg')
        
    def animate_click(self, color):
        self.configure(bg=color)
        self.after(150, lambda: self.configure(bg=self.default_bg))

def clicked(r, c):
    global Player1, stop_game
    
    if Player1 == "X" and states[r][c] == 0 and not stop_game:
        b[r][c].configure(text="X", fg="#FF6B6B", font=("Segoe UI", 20, "bold"))
        b[r][c].animate_click("#FFE8E8")
        states[r][c] = 'X'
        Player1 = 'O'
        update_status("نوبت بازیکن O", "#4ECDC4")
        animate_status()

    elif Player1 == 'O' and states[r][c] == 0 and not stop_game:
        b[r][c].configure(text='O', fg="#4ECDC4", font=("Segoe UI", 20, "bold"))
        b[r][c].animate_click("#E8FFFE")
        states[r][c] = "O"
        Player1 = "X"
        update_status("نوبت بازیکن X", "#FF6B6B")
        animate_status()

    check_if_win()

def update_status(text, color):
    status_label.config(text=text, fg=color)

def animate_status():
    current_size = 14
    for i in range(3):
        size = current_size + (2 if i % 2 == 0 else -2)
        status_label.after(i * 100, lambda s=size: status_label.config(font=("Segoe UI", s, "bold")))

def check_if_win():
    global stop_game
    
    # Check for win conditions
    for i in range(3):
        if states[i][0] == states[i][1] == states[i][2] != 0:
            stop_game = True
            highlight_winning_cells([(i, 0), (i, 1), (i, 2)])
            scores[states[i][0]] += 1
            update_scoreboard()
            show_win_animation(states[i][0])
            return

    for i in range(3):
        if states[0][i] == states[1][i] == states[2][i] != 0:
            stop_game = True
            highlight_winning_cells([(0, i), (1, i), (2, i)])
            scores[states[0][i]] += 1
            update_scoreboard()
            show_win_animation(states[0][i])
            return

    if states[0][0] == states[1][1] == states[2][2] != 0:
        stop_game = True
        highlight_winning_cells([(0, 0), (1, 1), (2, 2)])
        scores[states[0][0]] += 1
        update_scoreboard()
        show_win_animation(states[0][0])
        return

    if states[0][2] == states[1][1] == states[2][0] != 0:
        stop_game = True
        highlight_winning_cells([(0, 2), (1, 1), (2, 0)])
        scores[states[0][2]] += 1
        update_scoreboard()
        show_win_animation(states[0][2])
        return

    # Check for tie
    if all(states[i][j] != 0 for i in range(3) for j in range(3)):
        stop_game = True
        scores['Ties'] += 1
        update_scoreboard()
        show_tie_animation()

def highlight_winning_cells(cells):
    for (r, c) in cells:
        b[r][c].configure(bg="#45B7D1", fg="white")
        pulse_animation(b[r][c])

def pulse_animation(button):
    def pulse(step=0):
        colors = ["#45B7D1", "#5BC0EB", "#45B7D1"]
        button.configure(bg=colors[step % len(colors)])
        if step < 6:  # Number of pulses
            button.after(200, lambda: pulse(step + 1))
    pulse()

def show_win_animation(winner):
    confetti_colors = ["#FF6B6B", "#4ECDC4", "#FFE66D", "#FF9E6D", "#6B48FF"]
    for i in range(30):
        x = root.winfo_width() * random.random()
        y = root.winfo_height() * random.random()
        confetti = Label(root, text="✦", font=("Arial", 16), bg=BG_COLOR, fg=confetti_colors[i % len(confetti_colors)])
        confetti.place(x=x, y=y)
        root.after(i * 50, lambda c=confetti: c.destroy())
    
    update_status(f"بازیکن {winner} برنده شد!", "#FFE66D")
    messagebox.showinfo("پیروزی!", f"🎉 بازیکن {winner} برنده شد! 🎉")

def show_tie_animation():
    for i in range(3):
        for j in range(3):
            b[i][j].configure(bg="#95E1D3")
    update_status("بازی تساوی شد!", "#6A6B6E")
    messagebox.showinfo("بازی تساوی", "🤝 بازی تساوی شد! 🤝")

def reset_game():
    global Player1, stop_game, states
    
    Player1 = 'X'
    stop_game = False
    
    for i in range(3):
        for j in range(3):
            states[i][j] = 0
            b[i][j].configure(text="", bg="#FFFFFF", fg="black", font=("Segoe UI", 20, "bold"))
    
    update_status("نوبت بازیکن X", "#FF6B6B")

def update_scoreboard():
    score_x.config(text=f"بازیکن X: {scores['X']}")
    score_o.config(text=f"بازیکن O: {scores['O']}")
    score_tie.config(text=f"تساوی: {scores['Ties']}")

# Create main window
root = Tk()
root.title("تیک‌تاک‌تو")
root.resizable(0, 0)

# Modern color scheme
BG_COLOR = "#2C3E50"
CARD_COLOR = "#34495E"
ACCENT_COLOR = "#3498DB"
X_COLOR = "#FF6B6B"
O_COLOR = "#4ECDC4"
NEON_GREEN = "#00FF9D"
NEON_PINK = "#FF00FF"

root.configure(bg=BG_COLOR)

# Header with gradient effect
header_frame = Frame(root, bg=BG_COLOR, height=80)
header_frame.pack(fill=X, padx=20, pady=(10, 5))

title_label = Label(header_frame, text="بازی XO", font=("Segoe UI", 28, "bold"), 
                   bg=BG_COLOR, fg="white")
title_label.pack(pady=(6, 2))

subtitle_label = Label(header_frame, text="نسخهٔ مدرن", font=("Segoe UI", 12), 
                      bg=BG_COLOR, fg="#BDC3C7")
subtitle_label.pack(pady=(0, 4))

# Scoreboard
score_frame = Frame(root, bg=CARD_COLOR, relief="raised", bd=2)
score_frame.pack(pady=10, padx=20, fill=X)

score_title = Label(score_frame, text="امتیازها", font=("Segoe UI", 12, "bold"), 
                   bg=CARD_COLOR, fg="#ECF0F1")
score_title.pack(pady=(10, 5))

scores_frame = Frame(score_frame, bg=CARD_COLOR)
scores_frame.pack(pady=(0, 10))

score_x = Label(scores_frame, text="بازیکن X: 0", font=("Segoe UI", 11, "bold"), 
               bg=CARD_COLOR, fg=X_COLOR)
score_x.pack(side=LEFT, padx=12)

score_o = Label(scores_frame, text="بازیکن O: 0", font=("Segoe UI", 11, "bold"), 
               bg=CARD_COLOR, fg=O_COLOR)
score_o.pack(side=LEFT, padx=12)

score_tie = Label(scores_frame, text="تساوی: 0", font=("Segoe UI", 11, "bold"), 
                 bg=CARD_COLOR, fg="#F39C12")
score_tie.pack(side=LEFT, padx=12)

# Status label
status_label = Label(root, text="نوبت بازیکن X", font=("Segoe UI", 12, "bold"), 
                    bg=BG_COLOR, fg=X_COLOR)
status_label.pack(pady=6)

# Game board with modern styling
board_frame = Frame(root, bg=BG_COLOR)
board_frame.pack(pady=10)

# Create glowing board effect
board_bg = Frame(board_frame, bg="#1A2530", relief="raised", bd=3)
board_bg.pack(padx=6, pady=6)

# Buttons
b = [[0, 0, 0],
     [0, 0, 0],
     [0, 0, 0]]

states = [[0, 0, 0],
          [0, 0, 0],
          [0, 0, 0]]

for i in range(3):
    for j in range(3):
        b[i][j] = AnimatedButton(
            board_bg,
            text="",
            font=("Segoe UI", 20, "bold"),
            height=1,
            width=3,
            bg="#FFFFFF",
            relief="flat",
            bd=0,
            activebackground="#F8F9FA",
            command=lambda r=i, c=j: clicked(r, c)
        )
        b[i][j].grid(row=i, column=j, padx=2, pady=2, ipady=4)

# Control buttons
control_frame = Frame(root, bg=BG_COLOR)
control_frame.pack(pady=12)

reset_btn = Button(control_frame, text="🔄 بازی جدید", font=("Segoe UI", 12, "bold"),
                  bg="#27AE60", fg="white", relief="flat", bd=0,
                  padx=20, pady=10, command=reset_game)
reset_btn.pack(side=LEFT, padx=10)

exit_btn = Button(control_frame, text="🚪 خروج", font=("Segoe UI", 12, "bold"),
                 bg="#E74C3C", fg="white", relief="flat", bd=0,
                 padx=20, pady=10, command=root.quit)
exit_btn.pack(side=LEFT, padx=10)

# Footer
footer_label = Label(root, text="ساخته شده با ❤️ | Hamidvalad.ir", font=("Segoe UI", 10),
                    bg=BG_COLOR, fg="#7F8C8D")
footer_label.pack(pady=(12, 8))

# Add some decorative elements
deco1 = Label(header_frame, text="✦", font=("Arial", 16), bg=BG_COLOR, fg=NEON_GREEN)
deco1.place(relx=0.02, rely=0.2, anchor='w')

deco2 = Label(header_frame, text="✦", font=("Arial", 16), bg=BG_COLOR, fg=NEON_PINK)
deco2.place(relx=0.98, rely=0.2, anchor='e')

# Center the window
root.update_idletasks()
width = root.winfo_width()
height = root.winfo_height()
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f"{width}x{height}+{x}+{y}")

root.mainloop()

توضیح بخش‌های مهم کد

۱. کلاس AnimatedButton - دکمه‌های متحرک

این کلاس از کلاس اصلی Button ارث‌بری می‌کند و قابلیت انیمیشن به دکمه‌ها اضافه می‌کند:

Python
class AnimatedButton(Button):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.default_bg = self.cget('bg')  # ذخیره رنگ پیش‌فرض
        
    def animate_click(self, color):
        self.configure(bg=color)  # تغییر رنگ به رنگ داده شده
        self.after(150, lambda: self.configure(bg=self.default_bg))  # بازگشت به رنگ اصلی بعد از 150ms

توضیح: این کلاس با ذخیره رنگ پیش‌فرض دکمه و استفاده از تابع after برای ایجاد تاخیر، یک افکت ساده کلیک ایجاد می‌کند. وقتی کاربر روی دکمه کلیک می‌کند، رنگ دکمه به طور موقت تغییر کرده و سپس به حالت اول برمی‌گردد.

۲. تابع clicked - مدیریت کلیک‌های کاربر

این تابع مسئول مدیریت کلیک کاربر روی خانه‌های بازی است:

Python
def clicked(r, c):
    global Player1, stop_game
    
    if Player1 == "X" and states[r][c] == 0 and not stop_game:
        b[r][c].configure(text="X", fg="#FF6B6B", font=("Segoe UI", 36, "bold"))
        b[r][c].animate_click("#FFE8E8")  # انیمیشن کلیک با رنگ صورتی
        states[r][c] = 'X'  # ذخیره وضعیت در آرایه states
        Player1 = 'O'  # تغییر نوبت به بازیکن O
        update_status("Player O's Turn", "#4ECDC4")  # به‌روزرسانی وضعیت
        animate_status()  # اجرای انیمیشن متن وضعیت

توضیح: این تابع ابتدا بررسی می‌کند که آیا خانه خالی است و بازی متوقف نشده است. سپس نماد بازیکن فعلی را نمایش داده، انیمیشن کلیک را اجرا می‌کند، وضعیت را در آرایه states ذخیره کرده و نوبت را به بازیکن بعدی تغییر می‌دهد.

۳. تابع check_if_win - تشخیص برنده

این تابع تمام شرایط ممکن برای برنده شدن را بررسی می‌کند:

Python
def check_if_win():
    global stop_game
    
    # بررسی برنده شدن در سطرها
    for i in range(3):
        if states[i][0] == states[i][1] == states[i][2] != 0:
            stop_game = True  # توقف بازی
            highlight_winning_cells([(i, 0), (i, 1), (i, 2)])  هایلایت خانه‌های برنده
            scores[states[i][0]] += 1  # افزایش امتیاز بازیکن برنده
            update_scoreboard()  # به‌روزرسانی صفحه امتیازات
            show_win_animation(states[i][0])  # نمایش انیمیشن پیروزی
            return

    # بررسی برنده شدن در ستون‌ها
    for i in range(3):
        if states[0][i] == states[1][i] == states[2][i] != 0:
            stop_game = True
            highlight_winning_cells([(0, i), (1, i), (2, i)])
            scores[states[0][i]] += 1
            update_scoreboard()
            show_win_animation(states[0][i])
            return

    # بررسی قطر اصلی
    if states[0][0] == states[1][1] == states[2][2] != 0:
        stop_game = True
        highlight_winning_cells([(0, 0), (1, 1), (2, 2)])
        scores[states[0][0]] += 1
        update_scoreboard()
        show_win_animation(states[0][0])
        return

    # بررسی قطر فرعی
    if states[0][2] == states[1][1] == states[2][0] != 0:
        stop_game = True
        highlight_winning_cells([(0, 2), (1, 1), (2, 0)])
        scores[states[0][2]] += 1
        update_scoreboard()
        show_win_animation(states[0][2])
        return

    # بررسی تساوی
    if all(states[i][j] != 0 for i in range(3) for j in range(3)):
        stop_game = True
        scores['Ties'] += 1
        update_scoreboard()
        show_tie_animation()

توضیح: این تابع ۸ حالت ممکن برای برنده شدن (۳ سطر، ۳ ستون، ۲ قطر) را بررسی می‌کند. اگر هیچ برنده‌ای پیدا نشد و همه خانه‌ها پر باشند، بازی تساوی اعلام می‌شود.

۴. انیمیشن‌های بازی - ایجاد تجربه کاربری جذاب

این بخش شامل انیمیشن‌های مختلف برای بهبود تجربه کاربری است:

Python
def show_win_animation(winner):
    confetti_colors = ["#FF6B6B", "#4ECDC4", "#FFE66D", "#FF9E6D", "#6B48FF"]
    for i in range(30):  # ایجاد 30 تکه کانفتی
        x = root.winfo_width() * math.random()  # موقعیت تصادفی X
        y = root.winfo_height() * math.random()  # موقعیت تصادفی Y
        confetti = Label(root, text="✦", font=("Arial", 16), bg=BG_COLOR, fg=confetti_colors[i % len(confetti_colors)])
        confetti.place(x=x, y=y)  # قرار دادن کانفتی در موقعیت تصادفی
        root.after(i * 50, lambda c=confetti: c.destroy())  # حذف کانفتی بعد از تاخیر
    
    update_status(f"Player {winner} Wins!", "#FFE66D")
    messagebox.showinfo("Victory!", f"🎉 Player {winner} Wins! 🎉")

def pulse_animation(button):
    def pulse(step=0):
        colors = ["#45B7D1", "#5BC0EB", "#45B7D1"]  # رنگ‌های پالس
        button.configure(bg=colors[step % len(colors)])  # تغییر رنگ دکمه
        if step < 6:  # تعداد پالس‌ها
            button.after(200, lambda: pulse(step + 1))  # پالس بعدی بعد از 200ms
    pulse()

توضیح: انیمیشن پیروزی با ایجاد ۳۰ تکه کانفتی در موقعیت‌های تصادفی صفحه و انیمیشن پالس با تغییر متناوب رنگ خانه‌های برنده، تجربه بصری جذابی ایجاد می‌کنند.

۵. رابط کاربری و طراحی مدرن

این بخش مربوط به ایجاد رابط کاربری زیبا و مدرن است:

Python
# طرح رنگی مدرن
BG_COLOR = "#2C3E50"  # رنگ پس‌زمینه تیره
CARD_COLOR = "#34495E"  # رنگ کارت‌ها
X_COLOR = "#FF6B6B"  # رنگ بازیکن X
O_COLOR = "#4ECDC4"  # رنگ بازیکن O

# ایجاد صفحه امتیازات
score_frame = Frame(root, bg=CARD_COLOR, relief="raised", bd=2)
score_frame.pack(pady=10, padx=20, fill=X)

# ایجاد صفحه بازی
board_bg = Frame(board_frame, bg="#1A2530", relief="raised", bd=3)
board_bg.pack(padx=10, pady=10)

# ایجاد دکمه‌های بازی
for i in range(3):
    for j in range(3):
        b[i][j] = AnimatedButton(
            board_bg,
            text="",
            font=("Segoe UI", 36, "bold"),
            height=2,
            width=5,
            bg="#FFFFFF",  # رنگ سفید برای خانه‌ها
            relief="flat",  # حذف حاشیه استاندارد
            command=lambda r=i, c=j: clicked(r, c)  # اتصال تابع کلیک
        )
        b[i][j].grid(row=i, column=j, padx=4, pady=4, ipady=10)

توضیح: رابط کاربری با استفاده از رنگ‌های مدرن و المان‌های flat design ایجاد شده است. صفحه امتیازات و بازی در فریم‌های جداگانه با استایل متمایز قرار گرفته‌اند.

نحوه اجرا

برای اجرای بازی کافی است فایل پایتون را اجرا کنید:

Terminal
python tic_tac_toe.py

توسعه و بهبود

ایده‌هایی برای توسعه بیشتر بازی:

  • افزودن هوش مصنوعی برای بازی تک نفره
  • افزودن سطوح مختلف دشواری
  • قابلیت ذخیره امتیازات در فایل
  • افزودن صدا و موسیقی متن
  • ایجاد تم‌های رنگی مختلف
  • افزودن تایمر برای هر حرکت

منابع و مستندات

منبع : https://docs.python.org/3/library/tkinter.html

منبع : https://docs.python.org/3/tutorial/

منبع : https://docs.python.org/3/library/math.html

خلاصه پروژه

این پروژه یک پیاده‌سازی پیشرفته از بازی کلاسیک Tic Tac Toe است که با پایتون و tkinter توسعه یافته است. ویژگی‌های برجسته این بازی شامل:

  • رابط کاربری مدرن با تم تیره و رنگ‌بندی جذاب
  • انیمیشن‌های پویا برای کلیک‌ها و پیروزی‌ها
  • سیستم امتیازدهی با صفحه نمایش مجزا
  • افکت‌های بصری شامل کانفتی و هایلایت
  • مدیریت کامل وضعیت بازی و نوبت‌بندی
  • پیام‌های تعاملی و اطلاع‌رسانی زیبا

این پروژه نمونه‌ای عالی از ترکیب برنامه‌نویسی شی‌گرا، مدیریت رویدادها و طراحی رابط کاربری در پایتون است.