دومین بازی که با pygame می سازیم بازی دوز یا tictactoe است.در این بازی به جای X و O از تصویر گربه و موش استفاده می کنیم تا به نوعی هم با تصویرها کار کرده باشیم و هم در ساخت بازی تنوع به خرج داده باشیم.
بازی دوز یک بازی دو نفره است. این بازی در یک صفحه با ۳ سطر و ۳ ستون انجام میشود. یکی از بازیکن ها علامت X و دیگری O را برمی گزیند.برنده کسی است که بتواند هر سه علامت یا نشانه خود را در یک سطر یا ستون قرار بدهد. بازی که در این قسمت ساخت آن یاد خواهیم گرفت به جای X و O از تصویر گربه و موش استفاده می کند. تصویر بازی در زیر آمده است.
کد کامل بازی در زیر آمده است:
import pygame import math pygame.init() WIDTH = 480 HEIGHT=480 ROWS = 3 SCREEN = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("TicTacToe") WHITE = (255, 255, 255) BLACK = (0, 0, 0) GRAY = (200, 200, 200) RED = (255, 0, 0) BLUE = (0, 0, 255) CAT_IMAGE = pygame.transform.scale(pygame.image.load("images/cat.png"), (150, 150)) MOUSE_IMAGE = pygame.transform.scale(pygame.image.load("images/mouse.png"), (150, 150)) END_FONT = pygame.font.SysFont('arial', 40) def draw_grid(): gap = WIDTH // ROWS x = 0 y = 0 for i in range(ROWS): x = i * gap pygame.draw.line(SCREEN, GRAY, (x, 0), (x, WIDTH), 3) pygame.draw.line(SCREEN, GRAY, (0, x), (WIDTH, x), 3) def initialize_grid(): dis_to_center = WIDTH // ROWS // 2 game_array = [[None, None, None], [None, None, None], [None, None, None]] for i in range(len(game_array)): for j in range(len(game_array[i])): x = dis_to_center * (2 * j + 1) y = dis_to_center * (2 * i + 1) game_array[i][j] = (x, y,"", True) return game_array def click(game_array): global cat_turn, mouse_turn, images mouse_x, mouse_y = pygame.mouse.get_pos() for i in range(len(game_array)): for j in range(len(game_array[i])): x, y, char, can_play = game_array[i][j] dis = math.sqrt((x - mouse_x) ** 2 + (y - mouse_y) ** 2) if dis < WIDTH // ROWS // 2 and can_play: if cat_turn: images.append((x, y, CAT_IMAGE)) cat_turn = False mouse_turn = True game_array[i][j] = (x, y, 'cat', False) elif mouse_turn: images.append((x, y, MOUSE_IMAGE)) cat_turn = True mouse_turn = False game_array[i][j] = (x, y, 'mouse', False) def has_won(game_array): for row in range(len(game_array)): if (game_array[row][0][2] == game_array[row][1][2] == game_array[row][2][2]) and game_array[row][0][2] != "": display_message(game_array[row][0][2].upper() + " Win") return True for col in range(len(game_array)): if (game_array[0][col][2] == game_array[1][col][2] == game_array[2][col][2]) and game_array[0][col][2] != "": display_message(game_array[0][col][2].upper() + " Win") return True if (game_array[0][0][2] == game_array[1][1][2] == game_array[2][2][2]) and game_array[0][0][2] != "": display_message(game_array[0][0][2].upper() + " Won") return True if (game_array[0][2][2] == game_array[1][1][2] == game_array[2][0][2]) and game_array[0][2][2] != "": display_message(game_array[0][2][2].upper() + " Won") return True return False def has_drawn(game_array): for i in range(len(game_array)): for j in range(len(game_array[i])): if game_array[i][j][2] == "": return False display_message("It's a tie!") return True def display_message(content): pygame.time.delay(500) SCREEN.fill(WHITE) end_text = END_FONT.render(content, 1, BLACK) SCREEN.blit(end_text, ((WIDTH - end_text.get_width()) // 2, (WIDTH - end_text.get_height()) // 2)) pygame.display.update() pygame.time.delay(3000) def render(): SCREEN.fill(WHITE) draw_grid() for image in images: x, y, IMAGE = image SCREEN.blit(IMAGE, (x - IMAGE.get_width() // 2, y - IMAGE.get_height() // 2)) pygame.display.update() def main(): global cat_turn, mouse_turn, images, draw images = [] draw = False run = True cat_turn = True mouse_turn = False game_array = initialize_grid() while run: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() if event.type == pygame.MOUSEBUTTONDOWN: click(game_array) render() if has_won(game_array) or has_drawn(game_array): run = False while True: if __name__ == '__main__': main()
برای استفاده از pygame آن را وارد برنامه می کنیم. در این برنامه از ماژول math هم استفاده می کنیم. در خط بعدی pygame را initialize می کنیم.اگر این کار را نکنیم pygame کار نخواهد کرد.
import pygame import math pygame.init()
ثابت ها و متغیر های خود را به ترتیب زیر تعریف خواهیم کرد. رنگ ها را نیز به شکل ثابت تعریف می کنیم.
WIDTH = 480 HEIGHT=480 ROWS = 3 SCREEN = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("TicTacToe") WHITE = (255, 255, 255) BLACK = (0, 0, 0) GRAY = (200, 200, 200) RED = (255, 0, 0) BLUE = (0, 0, 255)
همان طور که از پیش گفتم در این بازی به جای X و O از تصویر گربه و موش استفاده می کنیم. این تصویر ها را در پوشه ای به نام images قرار می دهیم. سپس با کد زیر آن ها را وارد برنامه می کنیم. تصویر گربه را در ثابت CAT_IMAGE و تصویر موش را در MOUSE_IMAGE ذخیره می کنیم. برای بارگذاری یک عکس در برنامه از ماژول pygame.image.load استفاده می کنیم.این ماژول مسیر عکس را به عنوان ورودی می گیرد.
پس با دو دستور زیر عکس موش و گربه را وارد برنامه می کنیم:
pygame.image.load("images/cat.png") pygame.image.load("images/mouse.png")
می خواهیم که عکس موش و گربه دارای طول و عرض 150 پیکسل باشند. برای تغییر اندازه یک تصویر ماژول pygame.transform.scale را در pygame داریم. این ماژول دو پارامتر می گیرد. پارامتر نخست برای تصویر و پارامتر دوم یک تاپل دوتایی است. در این تاپل طول و عرض تصویر مشخص می شود. مانند کد زیر:
CAT_IMAGE = pygame.transform.scale(pygame.image.load("images/cat.png"), (150, 150)) MOUSE_IMAGE = pygame.transform.scale(pygame.image.load("images/mouse.png"), (150, 150))
اگر بازیکن برنده شود یا مساوی شود می خواهیم پیغام مناسب را به او نشان دهیم برای همین باید از متن ها استفاده کنیم. پس در زیر شی فونت را تعریف می کنیم. برای ساخت این شی از فونت سیستم استفاده می کنیم:
END_FONT = pygame.font.SysFont('arial', 40)
در این بازی انجام هر کاری را به یک تابع واگذار می کنیم مانند رسم صفحه، نمایش پیام ها و غیره.مهم ترین تابعی که در این بازی داریم تابع main است. سایر توابع در تابع main فراخوانی و استفاده می شوند. حلقه بازی که قلب بازی است در این تابع قرار دارد. این تابع را به صورت زیر تعریف می کنیم:
def main(): global cat_turn, mouse_turn, images, draw images = [] draw = False run = True cat_turn = True mouse_turn = False game_array = initialize_grid() while run: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() if event.type == pygame.MOUSEBUTTONDOWN: click(game_array) render() if has_won(game_array) or has_drawn(game_array): run = False
در خط اول متغیرهای global را که cat_turn, mouse_turn, images, draw هستند، تعریف می کنیم. در ابتدای هیچ تصویر موش یا گربه نمایش داده نمی شود و تنها با کلیک کردن روی خانه ها است که تصویر آن ها نمایش داده می شود.پس آرایه images در ابتدا باید خالی باشد و تا وقتی کلیک نکنیم کشیدن تصویر انجام نمی شود:
images = [] draw = False
برای کنترل کردن اجرای حلقه کد زیر را داریم:
run = True
تا هنگامی که متغیر run مقدار True را دارد حلقه اجرا می شود. اولین کسی که بازی را آغاز می کند گربه است. برای پیاده سازی این ویژگی دو متغیر زیر را که برای تعیین نوبت بازیکن است تعریف می کنیم :
cat_turn = True mouse_turn = False
سپس برای رسم خانه ها کد زیر را می نویسیم:
game_array = initialize_grid()
متد initialize_grid یک آرایه سه در سه را برمی گرداند که در متغیر game_array ذخیره می شود.زیرا سه سطر و سه ستون داریم. شرح این متد را در پایین خواهیم دید. برای رسم خانه های بازی که نه عدد هستند به این متد نیاز داریم.سپس به سراغ حلقه بازی می رویم:
while run: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() if event.type == pygame.MOUSEBUTTONDOWN: click(game_array) render() if has_won(game_array) or has_drawn(game_array): run = False
در حلقه بالا که به آن حلقه رویداد هم می گوییم دو نوع رویداد بررسی می شوند.رویداد نسخت خروج یا QUIT است و رویداد دوم فشرده شدن موس یا MOUSEBUTTONDOWN است.اگر رویداد QUIT رخ دهد از بازی خارج می شویم. اگر رویداد MOUSEBUTTONDOWN رخ دهد متد click را فراخوانی می کنیم و game_array را به آن می فرستیم. این متد نیز در پایین توضیح داده شده است. کاری که این متد می کند، تعیین نوبت بازیکن ها و نمایش تصویر ها روی صفحه است. پس از آن متد render را فراخوانی می کنیم تا صفحه با رنگ پوشیده شود و صفحه بازی رسم شود.
خط بعدی یک دستور شرطی است. اگر یکی از بازیکن ها برنده شده باشند یا مساوی کرده باشند بازی باید تمام شود. کد زیر این بررسی را انجام می دهد و اگر برد یا تساوی داشتیم بازی را پایان می دهد.برای این کار باید از حلقه رویداد خارج شویم. برای خروج از این حلقه run را مساوی False قرار می دهیم. متد hasWon برای بررسی وجود برنده و متد has_drawn برای بررسی تساوی است. این دو متد نیز در پایین توضیح داده شده اند.
if has_won(game_array) or has_drawn(game_array): run = False
def draw_grid(): gap = WIDTH // ROWS x = 0 y = 0 for i in range(ROWS): x = i * gap pygame.draw.line(SCREEN, GRAY, (x, 0), (x, WIDTH), 3) pygame.draw.line(SCREEN, GRAY, (0, x), (WIDTH, x), 3)
بازی در آغاز یک صفحه جدول مانند به سه سطر و سه ستون است. اگر بخواهیم همین تصویر را روی کاغذ بکشیم ابتدا یک مربع بزرگ می کشیم و سپس با سه خط سه سطر و با سه خط دیگر سه ستون می کشیم. پس به شش خط برای شبکه بندی صفحه بازی نیاز داریم.
def initialize_grid(): dis_to_center = WIDTH // ROWS // 2 game_array = [[None, None, None], [None, None, None], [None, None, None]] for i in range(len(game_array)): for j in range(len(game_array[i])): x = dis_to_center * (2 * j + 1) y = dis_to_center * (2 * i + 1) game_array[i][j] = (x, y, "", True) return game_array
از تابع بالا برای ذخیره مقدار تک تک خانه های بازی استفاده می کنیم.مقدار اولیه خانه ها در ابتدای بازی None است.اگر روی خانه ای کلیک شود آن گاه مقدار مناسب برای آن خانه در آرایه game_array ذخیره می شود.
def click(game_array): global x_turn, o_turn, images # Mouse position m_x, m_y = pygame.mouse.get_pos() for i in range(len(game_array)): for j in range(len(game_array[i])): x, y, char, can_play = game_array[i][j] # Distance between mouse and the centre of the square dis = math.sqrt((x - m_x) ** 2 + (y - m_y) ** 2) # If it's inside the square if dis < WIDTH // ROWS // 2 and can_play: if x_turn: # If it's X's turn images.append((x, y, X_IMAGE)) x_turn = False o_turn = True game_array[i][j] = (x, y, 'x', False) elif o_turn: # If it's O's turn images.append((x, y, O_IMAGE)) x_turn = True o_turn = False game_array[i][j] = (x, y, 'o', False)
تابع بالا برای مدیریت کلیک خانه ها به کار می رود.برای تعیین نوبت بازیکن از متغیرهای سراسری cat_turn و mouse_turn استفاده می کنیم. جایی که موس کلیک می شود دارای یک مختصات خواهد بود.این مختصات را در mouse_x و mouse_y ذخیره می کنیم.
global cat_turn, mouse_turn, images mouse_x, mouse_y = pygame.mouse.get_pos()
def has_won(game_array): for row in range(len(game_array)): if (game_array[row][0][2] == game_array[row][1][2] == game_array[row][2][2]) and game_array[row][0][2] != "": display_message(game_array[row][0][2].upper() + " Win") return True for col in range(len(game_array)): if (game_array[0][col][2] == game_array[1][col][2] == game_array[2][col][2]) and game_array[0][col][2] != "": display_message(game_array[0][col][2].upper() + " Win") return True if (game_array[0][0][2] == game_array[1][1][2] == game_array[2][2][2]) and game_array[0][0][2] != "": display_message(game_array[0][0][2].upper() + " Won") return True if (game_array[0][2][2] == game_array[1][1][2] == game_array[2][0][2]) and game_array[0][2][2] != "": display_message(game_array[0][2][2].upper() + " Won") return True return False
کسی برنده است که همه شکل های خود را در یک سطر یا یک ستون یا قطر های اصلی و فرعی صفحه قرار دهد. اگر حالت های برنده شدن را بشماریم هشت حالت به دست می آید.به شکل زیر نگاه کنید:
شماره خانه ها در زیر آمده است:
باید حرکت های بازیکنان را برررسی کنیم تا ببینیم آیا هیچ یک از هشت حالت بالا به وجود آمده است یا نه. برای این کار سطرها را جدا و ستون ها را جدا بررسی می کنیم. کد زیر سطرها را بررسی می کند:
for row in range(len(game_array)): if (game_array[row][0][2] == game_array[row][1][2] == game_array[row][2][2]) and game_array[row][0][2] != "": display_message(game_array[row][0][2].upper() + " Win") return True
کد زیر ستون ها را بررسی می کند:
for col in range(len(game_array)): if (game_array[0][col][2] == game_array[1][col][2] == game_array[2][col][2]) and game_array[0][col][2] != "": display_message(game_array[0][col][2].upper() + " Win") return True
کد زیر قطر اصلی را بررسی می کند:
if (game_array[0][0][2] == game_array[1][1][2] == game_array[2][2][2]) and game_array[0][0][2] != "": display_message(game_array[0][0][2].upper() + " Won") return True
کد زیر قطر فرعی را بررسی می کند. اگر هیچ یک از هشت حال بالا رخ ندهد در خط آخر False برگردانده می شود:
if (game_array[0][2][2] == game_array[1][1][2] == game_array[2][0][2]) and game_array[0][2][2] != "": display_message(game_array[0][2][2].upper() + " Won") return True return False
def has_drawn(game_array): for i in range(len(game_array)): for j in range(len(game_array[i])): if game_array[i][j][2] == "": return False display_message("It's a tie!") return True
اگر هیچ یک از هشت حال بالا رخ ندهد و در صفحه جایی برای ادامه بازی نباشد آن گاه دو بازیکن مساوی می شوند. پس باید خانه ها را باید سطر به سطر و ستون به ستون بررسی کنیم تا ببینیم آیا جایی برای ادامه بازی هست یا نه. آرایه game_array به عنوان پارامتر به این تابع فرستاده می شود. تابع با استفاده از این آرایه سطرها و ستون ها را بررسی می کند. اگر هنوز جایی برای بازی باشد False را برمی گرداند در غیر این صورت تابع display_message("It's a tie!") با پیام "It's a tie!" اجرا می شود و مقدار True برگردانده می شود.
def display_message(content): pygame.time.delay(500) SCREEN.fill(WHITE) end_text = END_FONT.render(content, 1, BLACK) SCREEN.blit(end_text, ((WIDTH - end_text.get_width()) // 2, (WIDTH - end_text.get_height()) // 2)) pygame.display.update() pygame.time.delay(3000)
اگر بازیکنی برنده یا بازیکنان با هم مساوی کنند باید پیام مناسب به آن ها نمایش داده شود.پس از معلوم شدن نتیجه پیروزی یا تساوی 500 میلی ثانیه درنگ می کنیم سپس صفحه را با رنگ سفید پاک می کنیم.
pygame.time.delay(500) SCREEN.fill(WHITE)
تابع display_message پارامتر content را به عنوان ورودی می گیرد.این پارامتر همان پیامی است که می خواهیم به بازیکن نمایش دهیم. رنگ آن را مشکی قرار می دهیم و دوست داریم که محل نمایش آن مرکز صفحه باشد.
end_text = END_FONT.render(content, 1, BLACK) SCREEN.blit(end_text, ((WIDTH - end_text.get_width()) // 2, (WIDTH - end_text.get_height()) // 2))
در آخر تغییرها را به روزآوری می کنیم و نتیجه را در صفحه نمایش می دهیم. برای این که بازیکنان پیام را با دقت بخوانند آن را به مدت سه ثانیه نمایش می دهیم.
pygame.display.update() pygame.time.delay(3000)
def render(): SCREEN.fill(WHITE) draw_grid() for image in images: x, y, IMAGE = image SCREEN.blit(IMAGE, (x - IMAGE.get_width() // 2, y - IMAGE.get_height() // 2)) pygame.display.update()
در این تابع نمایش نهایی تصویر ها و رسم نهایی شبکه انجام می شود. در آخر تغییرها به روزآوری می شوند و نتیجه در صفحه نمایش داده می شود.
کد کامل بازی را می توانید از نشانی دانلود کنید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.