معرفی
بازی Tic Tac Toe یک بازی کلاسیک است که در این پروژه با رابط کاربری مدرن و
امکانات پیشرفته پیادهسازی شده است. این برنامه از کتابخانه tkinter برای
ایجاد رابط گرافیکی استفاده میکند و دارای ویژگیهایی مانند انیمیشنهای
پویا، سیستم امتیازدهی، افکتهای بصری و طراحی مدرن است.
نصب و پیشنیازها
برای اجرای این پروژه نیاز به پایتون نسخه 3.6 یا بالاتر دارید. کتابخانههای مورد نیاز به صورت پیشفرض در پایتون موجود هستند:
tkinter- برای رابط گرافیکیmath- برای محاسبات ریاضی
معماری بازی
ساختار اصلی بازی شامل بخشهای زیر است:
- کلاس AnimatedButton برای دکمههای متحرک
- مدیریت وضعیت بازی و نوبت بازیکنان
- سیستم تشخیص برنده و تساوی
- انیمیشنهای پیروزی و تساوی
- سیستم امتیازدهی پیشرفته
- رابط کاربری مدرن با تم رنگی جذاب
ویژگیهای اصلی
این بازی دارای قابلیتهای متعددی است:
- رابط کاربری مدرن با تم تیره
- انیمیشن کلیک روی دکمهها
- افکتهای پیروزی با کانفتی
- هایلایت خانههای برنده
- سیستم امتیازدهی
- صفحه امتیازات مجزا
- انیمیشن پالس برای خانههای برنده
- پیامهای تعاملی زیبا
کدنویسی پروژه
کد کامل پروژه
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 ارثبری میکند و قابلیت انیمیشن به دکمهها اضافه میکند:
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 - مدیریت کلیکهای کاربر
این تابع مسئول مدیریت کلیک کاربر روی خانههای بازی است:
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 - تشخیص برنده
این تابع تمام شرایط ممکن برای برنده شدن را بررسی میکند:
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()
توضیح: این تابع ۸ حالت ممکن برای برنده شدن (۳ سطر، ۳ ستون، ۲ قطر) را بررسی میکند. اگر هیچ برندهای پیدا نشد و همه خانهها پر باشند، بازی تساوی اعلام میشود.
۴. انیمیشنهای بازی - ایجاد تجربه کاربری جذاب
این بخش شامل انیمیشنهای مختلف برای بهبود تجربه کاربری است:
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()
توضیح: انیمیشن پیروزی با ایجاد ۳۰ تکه کانفتی در موقعیتهای تصادفی صفحه و انیمیشن پالس با تغییر متناوب رنگ خانههای برنده، تجربه بصری جذابی ایجاد میکنند.
۵. رابط کاربری و طراحی مدرن
این بخش مربوط به ایجاد رابط کاربری زیبا و مدرن است:
# طرح رنگی مدرن
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 ایجاد شده است. صفحه امتیازات و بازی در فریمهای جداگانه با استایل متمایز قرار گرفتهاند.
نحوه اجرا
برای اجرای بازی کافی است فایل پایتون را اجرا کنید:
python tic_tac_toe.py
توسعه و بهبود
ایدههایی برای توسعه بیشتر بازی:
- افزودن هوش مصنوعی برای بازی تک نفره
- افزودن سطوح مختلف دشواری
- قابلیت ذخیره امتیازات در فایل
- افزودن صدا و موسیقی متن
- ایجاد تمهای رنگی مختلف
- افزودن تایمر برای هر حرکت
منابع و مستندات
منبع :
https://docs.python.org/3/library/tkinter.html
خلاصه پروژه
این پروژه یک پیادهسازی پیشرفته از بازی کلاسیک Tic Tac Toe است که با پایتون و tkinter توسعه یافته است. ویژگیهای برجسته این بازی شامل:
- رابط کاربری مدرن با تم تیره و رنگبندی جذاب
- انیمیشنهای پویا برای کلیکها و پیروزیها
- سیستم امتیازدهی با صفحه نمایش مجزا
- افکتهای بصری شامل کانفتی و هایلایت
- مدیریت کامل وضعیت بازی و نوبتبندی
- پیامهای تعاملی و اطلاعرسانی زیبا
این پروژه نمونهای عالی از ترکیب برنامهنویسی شیگرا، مدیریت رویدادها و طراحی رابط کاربری در پایتون است.