در این مقاله، قصد داریم تا با استفاده از زبان برنامهنویسی پایتون، فرآیند ساخت بازی منچ با پایتون را بررسی کنیم. بازی منچ، یکی از محبوبترین و نوستالژیکترین بازیهای تختهای در سراسر جهان است که به دلیل سادگی در قوانین و جذابیت در رقابت، همواره محبوبیت خود را حفظ کرده است. امروزه با گسترش زبانهای برنامهنویسی و ابزارهای متنوع، امکان پیادهسازی بازیهای سنتی در قالب برنامههای دیجیتال فراهم شده است.
پایتون با داشتن کتابخانههای قدرتمند و کاربردی، محیط مناسبی برای پروژههای بازیسازی است و اجازه میدهد تا توسعهدهندگان به راحتی فازهای مختلف پیادهسازی، از طراحی رابط کاربری گرفته تا منطق بازی، را به اجرا درآورند. پیادهسازی بازی منچ فرصتی مناسب برای یادگیری مفاهیم برنامهنویسی و آشنایی با تکنیکهای بازیسازی در پایتون ارائه میدهد.
بازی منچ چیست؟
بازی منچ یک بازی تختهای محبوب و هیجانانگیز است که برای دو تا چهار بازیکن طراحی شده است. در این بازی، هر بازیکن دارای چهار مهره است که باید آنها را از نقطه شروع به نقطه پایان منتقل کند. نحوه حرکت مهرهها با استفاده از تاس تعیین میشود، به طوری که هر بازیکن در نوبت خود تاس میاندازد و بر اساس عددی که میآید، مهرههای خود را روی صفحهبازی حرکت میدهد.
نوبتها به صورت چرخشی، یعنی به ترتیب بین بازیکنان، گرفته میشود و هر بازیکن به نوبت مهرههای خود را جلو میبرد. هدف اصلی این است که هر چهار مهره خود را زودتر از دیگران به خانه مقصد برسانید. این بازی علاوه بر ایجاد رقابت، نیاز به برنامهریزی دقیق و استراتژی دارد تا بتوانید مسیر حرکت مهرههای خود را به بهترین شکل مدیریت کنید و برنده بازی شوید.
آموزش ساخت بازی منچ با پایتون
برای ساخت بازی منچ با پایتون، از ماژولهای PyGame و Random استفاده خواهیم کرد. ماژول PyGame به ما امکان میدهد تا پنجره اصلی بازی را ایجاد کرده و کنترلهای لازم برای بازی را پیادهسازی کنیم. ماژول Random نیز برای شبیهسازی تاس و به دست آوردن عددی بین ۱ تا ۶ به کار میرود.
پیشنیازهای لازم برای ساخت بازی منچ با پایتون
برای ساخت این بازی، توسعهدهنده باید با زبان پایتون و ماژول Pygame آشنا باشد. برای دانلود ماژولهای Pygame و Random میتوانید از دستورات زیر استفاده کنید:
pip install random2 pip install pygame
مراحل ساخت بازی منچ با پایتون
برای ساخت بازی منچ با پایتون، مراحل زیر را دنبال خواهیم کرد:
- وارد کردن ماژولهای مورد نیاز
- ایجاد کلاس سازنده بازی منچ در پایتون
- تعریف متغیرهای سراسری (global)
- ایجاد تمام مهره ها و اشیاء شبکه
- نوشتن تابعی برای رسم جدول
- نوشتن توابعی برای حرکت توکنها
- نوشتن توابعی برای بررسی شناسه بازیکن، بازیکن فعلی و برخورد بین دو بازیکن
- نوشتن تابعی برای حرکت خودکار توکن کامپیوتر
- نوشتن تابعی برای تعیین نوبت بازیکن بعدی
- نوشتن بخش اصلی بازی
- ایجاد پنجره اصلی بازی
مرحله اول: وارد کردن ماژول های ضروری
در این مرحله از کدنویسی مربوط به ساخت بازی منچ با پایتون باید ماژولها و کتابخانههای ضروری پایتون برای اجرای پروژه را به محیط برنامهنویسی وارد کنیم.
import pygame import random from pygame.locals import * import sys from pygame import font import time
تمام ماژولهایی که در بالا به آنها اشاره شده است، برای ساخت برنامه بسیار مهم و حیاتی هستند. در ادامه، اطلاعات مربوط به کدهای فوق را ارائه میدهیم.
توضیح کد:
- ماژول Pygame به ما در ساخت پنجره و کنترلها برای بازی منچ کمک میکند.
- ماژول Random برای بهدست آوردن یک عدد هنگام پرتاب تاس استفاده میشود.
- Pygame.locals تمام ثابتها (constants) مورد نیاز در Pygame را وارد میکند.
مرحله دوم: ایجاد کلاس سازنده بازی منچ در پایتون
در این مرحله از فرایند ساخت بازی منچ با پایتون، کلاس اصلی با نام Munch game تعریف شده است که به عنوان مرجع اصلی تمامی توابع و کلاسهای دیگر عمل میکند. در این کلاس، ما ساختارهای اصلی و ویژگیهای کلیدی بازی را پیادهسازی میکنیم. این شامل تعریف متغیرهای مربوط به بازیکنان، مهرهها و قوانین بازی است.
پس از نوشتن کدهای اولیه کلاس، میتوانیم به بررسی جزئیات آن بپردازیم. این بررسی شامل تحلیل متدهای مختلفی است که در بازی مورد استفاده قرار میگیرند، از جمله متدهای آغاز بازی، حرکت مهرهها، بررسی وضعیت برنده و انجام نوبتها. با این رویکرد، میتوانیم اطمینان حاصل کنیم که ساختار کلاس به گونهای طراحی شده که تمامی جنبههای بازی را به درستی مدیریت کند و قابلیت توسعه و نگهداری آسانتر را فراهم آورد.
class Munch game: def __init__(self, bgColor, playerList, safe, coordinate): self.bgColor = bgColor self.playerList = playerList self.safe = safe self.coordinate = coordinate class Token: def __init__(self, Id, color, state, coordinate,size): self.Id = Id self.color = color self.state = state self.coordinate = coordinate self.size=size self.original_coordinate = coordinate
توضیح کد:
- () MunchGame: این کلاس به عنوان مدل اصلی بازی “Munch” عمل میکند.
- def __init__: در این قسمت سازنده کلاس را تعریف کرده و سپس پارامترهای ورودی و مورد نیاز را نیز برای آن تعریف کردهایم.
- متدسازنده (Constructor): این متد به عنوان سازنده کلاس عمل میکند و هنگام ساخت یک شی جدید از کلاس MunchGame، مقادیر اولیه را دریافت میکند.
- bgColor: رنگ پسزمینه بازی.
- playerList: لیستی از بازیکنان حاضر در بازی.
- safe: این متغیر نشان میدهد که آیا مکان خاصی ایمن است یا خیر.
- coordinate: مختصات یا موقعیت در پنجره بازی.
- تعریف کلاس Token: این کلاس نمایانگر یک توکن (مهره) برای هر بازیکن در بازی است.
متد سازنده (Constructor): این متد هنگام ایجاد یک شی جدید از کلاس Token، مقادیر اولیه را دریافت میکند. - Id: شناسه منحصر به فرد مهره.
- color: رنگ مهره.
- state: وضعیت مهره(مثلاً فعال یا غیرفعال).
- coordinate: مختصات یا موقعیت مهره در پنجره بازی.
- size: اندازه مهره.
- انتساب مختصات اصلی مهره: مختصات اولیه مهره به ویژگی self.original_coordinate اختصاص داده میشود. این باعث میشود که بتوان مختصات اصلی مهره را ذخیره کرد تا در صورت نیاز به آن مراجعه کرد.
مرحله سوم: ایجاد متغیرهای سراسری
در این مرحله متغیرهای سراسری ایجاد میشوند که میتوانند در حین ایجاد و اجرای بازی استفاده شوند. در ادامه به توضیحات کدهای زیر میپردازیم:
cList = [(1, 6), (2, 6), (3, 6), (4, 6), (5, 6), (6, 5), (6, 4), (6, 3), (6, 2), (6, 1), (6, 0), (7, 0), (8, 0), (8, 1), (۸, ۲), (۸, ۳), (۸, ۴), (۸, ۵), (۹, ۶), (۱۰, ۶), (۱۱, ۶), (۱۲, ۶), (۱۳, ۶), (۱۴, ۶), (۱۴, ۷),(۱۴, ۸), (۱۳, ۸), (۱۲, ۸), (۱۱, ۸), (۱۰, ۸), (۹, ۸), (۸, ۹), (۸, ۱۰), (۸, ۱۱), (۸, ۱۲), (۸, ۱۳), (۸, ۱۴), (۷, ۱۴),(۶, ۱۴), (۶, ۱۳), (۶, ۱۲), (۶, ۱۱), (۶, ۱۰), (۶, ۹), (۵, ۸), (۴, ۸), (۳, ۸), (۲, ۸), (۱, ۸), (۰, ۸), (۰, ۷),(۰, ۶)] #RGBY initPos=[[[1,1],[1,4],[4,1],[4,4]],[[10,1],[13,1],[10,4],[13,4]],[[1,10],[4,10],[1,13],[4,13]],[[10,10],[10,13],[13,10],[13,13]]] pnames=['R','G','B','Y'] height = 1000 width = 800 initx = 0 inity = 0 currentPlayer = 'R' compTokensLoc=[[1,1],[1,4],[4,1],[4,4]] n=2 withComputer=False dice_clicked = False move_list = [] diceValue = 6 Game_grid = [[-1 for _ in range(15)] for _ in range(15)] colors=['white','red','green','yellow','blue','black'] colorMatrix = [[-1, -1, -1, -1, -1, -1, 0, 0, 0, -1, -1, -1, -1, -1, -1], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۲, ۲, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۲, ۲, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۲, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۲, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۲, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [۰, ۱, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۴, ۰, ۰], [۰, ۱, ۱, ۱, ۱, ۱, ۰, ۵, ۰, ۴, ۴, ۴, ۴, ۴, ۰], [۰, ۰, ۱, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۰, ۴, ۰], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۳, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۳, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۳, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۳, ۳, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۳, ۳, ۰, -۱, -۱, -۱, -۱, -۱, -۱], [-۱, -۱, -۱, -۱, -۱, -۱, ۰, ۰, ۰, -۱, -۱, -۱, -۱, -۱, -۱]] coordinateMatrix = [[[initx + i * 50, inity + j * 50] for i in range(0, 15)] for j in range(0, 15)] safeLocs=[[6,2],[8,1],[12,6],[13,8],[8,12],[6,13],[2,8],[1,6]] safeMatrix = [[0 for i in range(15)] for j in range(15)] for i in safeLocs: safeMatrix[i[0]][i[1]] = 1 diceFaces = {1: [[0, 0, 0], [0, 1, 0], [0, 0, 0]], ۲: [[۰, ۱, ۰], [۰, ۰, ۰], [۰, ۱, ۰]], ۳: [[۰, ۱, ۰], [۰, ۱, ۰], [۰, ۱, ۰]], ۴: [[۱, ۰, ۱], [۰, ۰, ۰], [۱, ۰, ۱]], ۵: [[۱, ۰, ۱], [۰, ۱, ۰], [۱, ۰, ۱]], ۶: [[۱, ۰, ۱], [۱, ۰, ۱], [۱, ۰, ۱]], }
توضیح کد:
- متغیر cList مکانهای مرکزی شبکه را نگه میدارد و متغیر initPos موقعیتهای اولیه توکنها را ذخیره میکند.
- متغیر pnames نامهای توکنها را دارد، و متغیرهای height و width اندازه پنجره بازی را تعیین میکنند. همچنین، متغیرهای initx و inity مختصات اولیه x و y هستند.
- متغیر compTokensLoc مکانهای چهار توکن را در صورتی که حالت بازی با کامپیوتر باشد، ذخیره میکند. این کار به ردیابی موقعیت توکنهای کامپیوتر برای حرکت خودکار آنها کمک میکند.
- متغیر currentPlayer نام بازیکنی که در حال بازی است را نگه میدارد، و dice_clicked یک متغیر بولی است که بررسی میکند آیا کاربر دکمه ماوس را فشار داده است یا نه. همچنین، diceValue مقدار نشان داده شده روی تاس را در حین پرتاب ذخیره میکند.
- متغیر ‘n’ تعداد بازیکنانی را که بر اساس انتخاب کاربر در حال بازی هستند، نگه میدارد و متغیر ‘withComputer’ در صورتی که بازی با کامپیوتر انجام شود، برابر با True است و در غیر این صورت False خواهد بود.
- Game_grid پنجره اصلی بازی است و colorMatrix رنگها را به مکانهای شبکه براساس رنگهای موجود در لیست colors نسبت میدهد.
- لیست safeLocs مکانهایی است که چندین توکن میتوانند در آنجا بمانند. این مکانها در متغیر safeMatrix با مقدار ۱ تعیین شدهاند.
- در نهایت، متغیر diceFaces به نمایش مقادیر تاس بر روی صفحه کمک میکند. به عنوان مثال، برای نمایش عدد ۱ نیاز به یک نقطه در وسط داریم. بنابراین، ماتریس ۳×۳ مربوطه به شکل [[۰, ۰, ۰], [۰, ۱, ۰], [۰, ۰, ۰]] است که در وسط آن ۱ قرار دارد.
مرحله چهارم: ایجاد تمام مهره ها و اشیاء شبکه
در این مرحله تمام مهره و و اشیا شبکه که برای ساخت بازی منچ نیاز داریم را خواهیم نوشت.
for i in range(15): for j in range(15): ob = Munch game(colors[colorMatrix[i][j]], [], safeMatrix[i][j], coordinateMatrix[i][j]) Game_grid[i][j] = ob for j in range(4): for i in range(1,5): p=initPos[j][i-1] R= Token(pnames[j]+str(i), colors[j+1], 0, (50 *p[0] , 50 * p[1]), 20) Game_grid[p[0]][p[1]].playerList.append(R)
توضیح کد:
- اکنون ما با استفاده از حلقه، اشیاء شبکه ۱۵×۱۵ را برای پنجره ایجاد خواهیم کرد. این اشیاء شامل رنگ و ویژگیهای دیگری هستند که توسط متغیرهای سراسری بالا تعیین شدهاند.
- سپس ۱۶ مهره ایجاد میکنیم که از هر رنگ ۴ مهره خواهد بود. این مهره ها دارای ویژگیهایی مانند نام، رنگ، اندازه و موقعیت هستند.
مرحله پنجم: نوشتن تابع برای رسم جدول
نوشتن تابع برای رسم جدول بازی منچ به ایجاد نمای بصری و منظم از محیط بازی کمک میکند. این تابع میتواند با استفاده از مختصات مشخص، سلولهای جدول را ترسیم کند و همچنین خطوط افقی و عمودی را برای مشخص کردن فضای بازی طراحی نماید. با تنظیم اندازه هر سلول و استفاده از رنگها برای تمایز نوبتها، این تابع تجربه بصری جذابی را برای بازیکنان فراهم میآورد و به راحتی قابل فهم و تعامل میباشد. در ادامه به توضیح کدهای زیر خواهیم پرداخت.
def drawGrid(): global Game_grid newSurface = pygame.display.set_mode((height, width)) newSurface.fill('bisque') for i in range(15): for j in range(15): pygame.draw.rect(newSurface, Game_grid[i][j].bgColor, tuple(Game_grid[i][j].coordinate + [50, 50])) pygame.draw.rect(newSurface, (0, 0, 0), tuple(Game_grid[i][j].coordinate + [50, 50]), 1) # always constant pygame.draw.rect(newSurface, colors[1], (initx, inity, 300, 300)) pygame.draw.rect(newSurface, colors[0], (initx + 50, inity + 50, 200, 200)) pygame.draw.rect(newSurface, colors[2], (initx + 450, inity, 300, 300)) pygame.draw.rect(newSurface, colors[0], (initx + 500, inity + 50, 200, 200)) pygame.draw.rect(newSurface, colors[3], (initx, inity + 450, 300, 300)) pygame.draw.rect(newSurface, colors[0], (initx + 50, inity + 500, 200, 200)) pygame.draw.rect(newSurface, colors[4], (initx + 450, inity + 450, 300, 300)) pygame.draw.rect(newSurface, colors[0], (initx + 500, inity + 500, 200, 200)) for i in range(15): for j in range(15): relativeToken(Game_grid[i][j].playerList, i * 50, j * 50) for k in Game_grid[i][j].playerList: c = k.coordinates pygame.draw.circle(newSurface, k.color, (c[0] + 25, c[1] + 25), k.size) pygame.draw.circle(newSurface, colors[-1], (c[0] + 25, c[1] + 25), k.size, 1) # highlight if k.Id[0] == currentPlayer: pygame.draw.circle(newSurface, colors[0], (c[0] + 25, c[1] + 25), k.size - 2, 2) # chess_faces face = diceFaces[diceValue] for i in range(3): for j in range(3): pygame.draw.rect(newSurface, 'black', ((0 + 800) + (50 * j), (0 + 300) + (50 * i), 50, 50)) if face[i][j] == 1: cIndex=pnames.index(currentPlayer)+1 pygame.draw.circle(newSurface, colors[cIndex], ((0 + 800) + (50 * j) + 25, (0 + 300) + (50 * i) + 25), ۱۰) pygame.draw.rect(newSurface, colors[pnames.index(currentPlayer)+1], ((0 + 798), (0 + 298), 150, 150), 4) return newSurface
توضیح کد:
- def drawGrid(): یک تابع به نام drawGrid تعریف میکند که برای رسم شبکه استفاده میشود.
- global Game_grid: با استفاده از global مشخص میکند که متغیر Game_grid که در خارج از این تابع تعریف شده، در این تابع نیز قابل دسترسی است.
- newSurface = pygame.display.set_mode((height, width)): این خط یک سطح جدید برای نمایش (پنجره بازی) با ارتفاع و عرض مشخص ایجاد میکند.
- newSurface.fill(‘bisque’): با این خط رنگ پسزمینه سطح جدید به رنگ «بیسک» پر میشود.
- face = diceFaces[diceValue]: شکل تاس بر اساس مقدار diceValue رسم میشود.
- return newSurface: در نهایت، سطح جدید رسمشده به تابع بازگردانده میشود.
مرحله ششم: نوشتن توابع برای جابجایی مهره ها
نوشتن توابع برای جابجایی مهرهها یکی از مراحل کلیدی در طراحی بازی است که نقش مهمی در اجرای منطق بازی ایفا میکند. این توابع مسئول مدیریت حرکات مهرهها از یک محل به محل دیگر بر اساس قواعد بازی هستند. با تعریف ورودیهای مناسب، مانند موقعیت اولیه و نهایی مهره، میتوانیم اطمینان حاصل کنیم که حرکات به درستی انجام میشوند و قوانین بازی رعایت میگردند.
def move(initPos, value, current): i = 0 j = -1 flag = 0 while True: if cList[i] == initPos or j >= 0: if current == 'R' and i == 50: flag = 1 if current == 'G' and i == 11: flag = 2 if current == 'B' and i == 37: flag = 3 if current == 'Y' and i == 24: flag = 4 j += 1 if j == value: break i = (i + 1) % len(cList) if flag == 1: return (cList[i][0] + 1, cList[i][1] + 1) elif flag == 2: return (cList[i][0] + 1, cList[i][1] + 1) elif flag == 3: return (cList[i][0] + 1, cList[i][1] - 1) elif flag == 4: return (cList[i][0] - 1, cList[i][1] - 1) else: return (cList[i][0], cList[i][1]) def relativeToken(pList, x, y): l = len(pList) relRad = int((2 / (l + 1)) * 20) relpt = [] j = 0 if l % 2 == 0: l1 = [i + 1 for i in range((l // 2))] l2 = [i - 1 for i in range((l // 2))] relpt = l2[::-1] + l1 else: l1 = [i + 1 for i in range((l // 2))] l2 = [i - 1 for i in range((l // 2))] relpt = l2[::-1] + [0] + l1 for p in pList: p.size = relRad p.coordinates = ((x) + (relpt[j] * (relRad // 2)), (y)) j += 1
توضیح کد:
- در تابع move(), حرکت بر اساس مقدار تاس، موقعیت فعلی مهره مربوطه و مجموعهای از قوانین کنترل میشود.
- تابع relativeToken() اندازه و موقعیت نسبی را بر اساس مختصات برمیگرداند.
این توضیحات به شما کمک میکند عملکرد و هدف هر تابع را بهتر درک کنید.
مرحله هفتم: نوشتن توابعی برای بررسی شناسه بازیکن و برخورد بین دو بازیکن
در این مرحله به نوشتن تابع برای بررسی و شناسه بازیکن و نحوه تشخیص برخورد بین بازیکن ها را مشخص میکنیم. در ادامه به توضیح تمامی کد های زیر میپرداریم:
def check(pos): if pos in cList: return True else: return False def gridlocation(pos): x = pos[0] y = pos[1] return (x // 50, y // 50) def checkCollision(pList): global currentPlayer global Game_grid new_list=[] for p in pList: if p.Id[0] == currentPlayer: new_list.append(p) else: p.coordinates=p.original_coordinate i=p.coordinates[0]//50 j=p.coordinates[1]//50 Game_grid[i][j].playerList.append(p) return new_list def checkId(pList): global currentPlayer for i in pList: if i.Id[0] == currentPlayer: return True return False
توضیح کد:
- تابع check() بررسی میکند که آیا موقعیت مهره در فهرست cList قرار دارد یا خیر.
- تابع gridlocation() اطمینان حاصل میکند که مهره درون شبکه حرکت میکند و موقعیت متناظر را ارائه میدهد.
- تابع checkCollision() بررسی میکند که آیا دو مهره در نهایت در یک موقعیت از شبکه قرار میگیرند یا خیر.
- تابع checkId() تعیین میکند که کدام مهره از بازیکن فعلی باید حرکت کند.
مرحله هشتم: نوشتن تابع حرکت خودکار توکن کامپیوتر
برای نوشتن تابعی که توکن کامپیوتر را بهصورت خودکار در بازی منچ حرکت دهد، ابتدا باید مکان کنونی توکن را مشخص کنیم و سپس با توجه به قوانین بازی، تعداد واحدهایی که باید حرکت کند را تعیین کنیم. این تابع میتواند از یک عدد تصادفی برای انتخاب تعداد خانههایی که توکن باید جابجا شود، استفاده کند و سپس با بررسی موقعیت فعلی، توکن را به جلو ببرد. همچنین، باید الزامات خاصی نظیر برخورد با بازی و شرایط پیروزی را در نظر بگیریم تا حرکت توکن به درستی انجام شود. به این ترتیب، این تابع نه تنها تجربه بازی را برای کاربران جذابتر میکند، بلکه به ارائه هوش مصنوعی سادهای برای رقبای کامپیوتری کمک مینماید.
def compLoc(diceValue): global compTokensLoc saveLocs=[(1, 7),(2, 7),(3, 7),(4, 7),(5, 7),(6, 7),(7, 7)] inits=[] players=[] if(compTokensLoc[0]==[1,1]): inits.append(0) else: players.append(0) if(compTokensLoc[1]==[1,4]): inits.append(1) else: players.append(1) if(compTokensLoc[2]==[4,1]): inits.append(2) else: players.append(2) if(compTokensLoc[3]==[4,4]): inits.append(3) else: players.append(3) if(diceValue==6 and len(inits)>0): tkn=random.randint(1,len(inits)) compTokensLoc[tkn-1]=cList[0] return compTokensLoc[tkn-1] cnt=len(compTokensLoc)-len(inits) print(inits) if(cnt<=0): return (1,1) if(cnt>0): tkn=random.randint(1,cnt) tkn=players[tkn-1] print(tkn) ind=cList.index(compTokensLoc[tkn]) if(compTokensLoc[tkn] in saveLocs): ind=saveLocs.index(compTokensLoc[tkn]) if((ind+diceValue) <=(len(saveLocs-1))): compTokensLoc[tkn]=saveLocs[ind+diceValue] return compTokensLoc[tkn] else: compLoc(diceValue) elif(ind+diceValue<len(cList)-1): compTokensLoc[tkn]=cList[ind+diceValue] else: stepsLeft= diceValue-(len(cList)-1) compTokensLoc[tkn]=saveLocs[stepsLeft-1] return compTokensLoc[tkn]
توضیح کد:
- def compLoc(diceValue): این تابع به نام compLoc تعریف شده است و یک پارامتر به نام diceValue (مقدار تاس) را دریافت میکند.
- global compTokensLoc: در اینجا متغیر compTokensLoc به عنوان یک متغیر سراسری (global) مشخص شده است، که به این معنی است که تابع به این متغیر خارج از تابع نیز دسترسی دارد.
- []=inits=[],players: دو لیست خالی به نامهای inits و players برای ذخیره مهرههای در حالت اولیه و مهرههای در حال بازی، به ترتیب ایجاد میشود.
- if(diceValue==6 and len(inits)>0): اگر مقدار تاس ۶ باشد و حداقل یک مهره در حالت اولیه (inits) وجود داشته باشد.
- tkn=random.randint(1,len(inits)): یک عدد تصادفی به نام tkn از میان مهرههای اولیه انتخاب میشود.
- return compTokensLoc[tkn-1]: موقعیت جدید مهره را باز میگرداند.
- tkn=random.randint(1,cnt): یک عدد تصادفی به نام tkn از میان مهرههای در حال بازی انتخاب میشود.
- tkn=players[tkn-1]: مهره انتخاب شده در لیست players بازیابی میشود.
- if(compTokensLoc[tkn] in saveLocs): بررسی میشود که آیا موقعیت مهره در saveLocs وجود دارد یا خیر.
- ind=saveLocs.index(compTokensLoc[tkn]): اگر مهره در saveLocs باشد، اندیس آن در saveLocs مشخص میشود.
- if((ind+diceValue) <=(len(saveLocs-1))): اگر مجموع اندیس و مقدار تاس از طول لیست saveLocs بیشتر نباشد
- compTokensLoc[tkn]=saveLocs[stepsLeft-1]: مهره به موقعیت جدید در saveLocs منتقل میشود.
- return compTokensLoc[tkn]: در نهایت، موقعیت جدید مهره بازگردانده میشود.
مرحله نهم: نوشتن تابع تعیین نوبت بازیکن بعدی
برای نوشتن تابعی که نوبت بازیکن بعدی را تعیین کند، ابتدا لازم است که تعداد بازیکنان را مشخص کنیم و وضعیت نوبت کنونی هر یک را در نظر بگیریم. این تابع میتواند با استفاده از باقیمانده تقسیم شمارش نوبت جاری بر تعداد بازیکنان، بازیکن بعدی را شناسایی کند. به عنوان مثال، اگر نوبت جاری بازیکن ۲ باشد و تعداد بازیکنان ۴ نفر باشد، نوبت بعدی بازیکن ۳ خواهد بود. با این کار، بازی بهطور منظم و بدون خلل پیش میرود و هر بازیکن به نوبت فرصت حرکت و انجام اقدامهای خود را خواهد داشت. در ادامه، به توضیح کدها و جزئیات پیادهسازی این تابع خواهیم پرداخت.
def nextPlayer(): global n,currentPlayer if(n==2): if currentPlayer == 'R': currentPlayer = 'B' elif currentPlayer == 'B': currentPlayer = 'R' elif(n==3): if currentPlayer == 'R': currentPlayer = 'G' elif currentPlayer == 'G': currentPlayer = 'Y' elif currentPlayer == 'Y': currentPlayer = 'R' elif(n==4): if currentPlayer == 'R': currentPlayer = 'G' elif currentPlayer == 'G': currentPlayer = 'Y' elif currentPlayer == 'Y': currentPlayer = 'R' elif currentPlayer == 'B': currentPlayer = 'R'
توضیح کد:
- در اینجا، بر اساس تعداد بازیکنان، بین بازیکنان اصلی جا به جا میشویم.
- اگر تعداد بازیکنان ۲ باشد، جا به جایی بین بازیکنهای ‘R’ و ‘B’ انجام میشود. اگر ۳ بازیکن وجود داشته باشد، این جا به جایی بین ‘R’، ‘G’ و ‘B’ خواهد بود. و اگر چهار بازیکن در حال بازی باشند، بین تمامی ۴ بازیکن جابهجایی صورت میگیرد.
مرحله دهم: نوشتن بخش اصلی ساخت بازی منچ با پایتون
نوشتن بخش اصلی بازی شامل پیادهسازی منطق کلی و کنترل نوبتها و حرکات بازیکنان است. در این مرحله، باید قواعد بازی و شرایط برنده شدن را مشخص کنیم، تا تعاملات بین بازیکنان بهخوبی مدیریت شود. با استفاده از متغیرها و توابع، میتوانیم جریان بازی را بهخوبی کنترل کنیم. در ادامه، به توضیح کدها و جزئیات پیادهسازی این بخش خواهیم پرداخت.
global DISPLAYSURF def mainGame(): height = 1000 width = 800 pygame.init() DISPLAYSURF = pygame.display.set_mode((height, width)) pygame.display.set_caption('PythonGeeks Ludo Game') global cList, initPos,pnames,initx,inity,currentPlayer,n,dice_clicked,move_list,diceValue,Game_grid,colors,colorMatrix, coordinateMatrix,safeMatrix,safeLocs,diceFaces font = pygame.font.SysFont("Calibri", 30,'bold') label = font.render("Munch game", 1, 'black') while (True): for event in pygame.event.get(): if(withComputer and currentPlayer=='R') or event.type == MOUSEBUTTONDOWN: if(withComputer and currentPlayer=='R'): loc=compLoc(random.randint(1, 6)) dice_clicked == True elif event.type == MOUSEBUTTONDOWN: loc = gridlocation(event.pos) if (loc[0] >= 16 and loc[0] <= 18 and loc[1] >= 6 and loc[1] <= 8 and dice_clicked == False): # dice_value = 6 diceValue = random.randint(1, 6) print("dice clicked", currentPlayer) dice_clicked = True if diceValue != 6 and dice_clicked == True: print(1) flag = 0 for i in cList: for p in Game_grid[i[0]][i[1]].playerList: if p.Id[0] == currentPlayer: flag = 1 if flag == 0: nextPlayer() dice_clicked = False if currentPlayer == 'R' and diceValue == 6 and (loc in [(1, 1), (4, 1), (4, 4), (1, 4)]) and dice_clicked == True: print(2) print(Game_grid[1][6].playerList) Game_grid[1][6].playerList.append(Game_grid[loc[0]][loc[1]].playerList[0]) Game_grid[1][6].playerList[-1].coordinates = (50 * 1, 50 * 6) for p in Game_grid[1][6].playerList: print(p.coordinates) Game_grid[loc[0]][loc[1]].playerList = [] dice_clicked = False elif currentPlayer == 'G' and diceValue == 6 and (loc in [(10, 1), (13, 1), (13, 4), (10, 4)]) and dice_clicked == True: print(3) print(Game_grid[8][1].playerList) Game_grid[8][1].playerList.append(Game_grid[loc[0]][loc[1]].playerList[0]) Game_grid[8][1].playerList[-1].coordinates = (50 * 8, 50 * 1) Game_grid[loc[0]][loc[1]].playerList = [] print(Game_grid[8][1].playerList[0].Id) dice_clicked = False elif currentPlayer == 'B' and diceValue == 6 and (loc in [(1, 10), (4, 10), (4, 13), (1, 13)]) and dice_clicked == True: print(5) print(Game_grid[6][13].playerList) Game_grid[6][13].playerList.append(Game_grid[loc[0]][loc[1]].playerList[0]) Game_grid[6][13].playerList[-1].coordinates = (50 * 6, 50 * 13) Game_grid[loc[0]][loc[1]].playerList = [] dice_clicked = False elif currentPlayer == 'Y' and diceValue == 6 and (loc in [(10, 10), (13, 10), (13, 13), (10, 13)]) and dice_clicked == True: print(4) print(Game_grid[13][8].playerList) Game_grid[13][8].playerList.append(Game_grid[loc[0]][loc[1]].playerList[0]) Game_grid[13][8].playerList[-1].coordinates = (50 * 13, 50 * 8) Game_grid[loc[0]][loc[1]].playerList = [] dice_clicked = False elif currentPlayer == 'R' and (loc in [(1, 7), (2, 7), (3, 7), (4, 7), (5, 7)]) and len( Game_grid[loc[0]][loc[1]].playerList) > 0 and dice_clicked == True: if loc[0] + diceValue <= 6: Game_grid[loc[0] + diceValue][loc[1]].playerList.append(Game_grid[loc[0]][loc[1]].playerList[-1]) Game_grid[loc[0] + diceValue][loc[1]].playerList[-1].coordinates = ( ۵۰ * (loc[0] + diceValue), 50 * (loc[1])) Game_grid[loc[0]][loc[1]].playerList = Game_grid[loc[0]][loc[1]].playerList[:-1] dice_clicked = False elif currentPlayer == 'G' and (loc in [(7, 1), (7, 2), (7, 3), (7, 4), (7, 5)]) and len( Game_grid[loc[0]][loc[1]].playerList) > 0 and dice_clicked == True: if loc[1] + diceValue <= 6: Game_grid[loc[0]][loc[1] + diceValue].playerList.append(Game_grid[loc[0]][loc[1]].playerList[-1]) Game_grid[loc[0]][loc[1] + diceValue].playerList[-1].coordinates = ( ۵۰ * (loc[0]), 50 * (loc[1] + diceValue)) Game_grid[loc[0]][loc[1]].playerList = Game_grid[loc[0]][loc[1]].playerList[:-1] dice_clicked = False elif currentPlayer == 'B' and (loc in [(7, 9), (7, 10), (7, 11), (7, 12), (7, 13)]) and len( Game_grid[loc[0]][loc[1]].playerList) > 0 and dice_clicked == True: if loc[1] + diceValue >= 8: Game_grid[loc[0]][loc[1] + diceValue].playerList.append(Game_grid[loc[0]][loc[1]].playerList[-1]) Game_grid[loc[0]][loc[1] + diceValue].playerList[-1].coordinates = ( ۵۰ * (loc[0]), 50 * (loc[1] + diceValue)) Game_grid[loc[0]][loc[1]].playerList = Game_grid[loc[0]][loc[1]].playerList[:-1] dice_clicked = False elif currentPlayer == 'Y' and (loc in [(9, 7), (10, 7), (11, 7), (12, 7), (13, 7)]) and len( Game_grid[loc[0]][loc[1]].playerList) > 0 and dice_clicked == True: if loc[0] - diceValue >=8: Game_grid[loc[0] - diceValue][loc[1]].playerList.append(Game_grid[loc[0]][loc[1]].playerList[-1]) Game_grid[loc[0] - diceValue][loc[1]].playerList[-1].coordinates = ( ۵۰ * (loc[0] - diceValue), 50 * (loc[1])) Game_grid[loc[0]][loc[1]].playerList = Game_grid[loc[0]][loc[1]].playerList[:-1] dice_clicked = False elif (check(loc)) and checkId(Game_grid[loc[0]][loc[1]].playerList) and dice_clicked == True: print(6) newpos = move(loc, diceValue, currentPlayer) new_list = [] flg = 0 for i in Game_grid[loc[0]][loc[1]].playerList: if i.Id[0] == currentPlayer and flg == 0: Game_grid[newpos[0]][newpos[1]].playerList.append(i) Game_grid[newpos[0]][newpos[1]].playerList[-1].coordinates = (50 * newpos[0], 50 * newpos[1]) #eating pieces Game_grid[newpos[0]][newpos[1]].playerList=checkCollision(Game_grid[newpos[0]][newpos[1]].playerList) flg = 1 else: new_list.append(i) Game_grid[loc[0]][loc[1]].playerList = new_list dice_clicked = False if diceValue != 6: nextPlayer() # DISPLAYSURF.blit(drawGrid(), (0, 0)) # pygame.display.update() font1 = pygame.font.SysFont("Calibri", 50) label1 = font1.render(str(diceValue), 1, 'black') DISPLAYSURF.blit(drawGrid(), (0, 0)) DISPLAYSURF.blit(label, (800, 10)) DISPLAYSURF.blit(label1, (850, 500)) pygame.display.update() if event.type == QUIT: pygame.quit() sys.exit()
توضیح کد:
- global DISPLAYSURF: متغیر سراسری DISPLAYSURF اعلام میشود که برای نمایش سطح بازی در Pygame استفاده میشود.
- def mainGame(): تعریف تابع اصلی mainGame که تمام منطق بازی در آن قرار دارد.
- pygame.init(): کتابخانه Pygame راهاندازی میشود.
- DISPLAYSURF = pygame.display.set_mode((height, width)): یک پنجره با ابعاد مشخصشده (ارتفاع و عرض) ایجاد میشود.
- pygame.display.set_caption(‘PythonGeeks Ludo Game’): عنوان پنجره بازی به “PythonGeeks Munch game” تنظیم میشود.
- font = pygame.font.SysFont(“Calibri”, ۳۰, ‘bold’): یک فونت با اندازه ۳۰ و نوع “bold” از نوع “Calibri” ایجاد میشود.
- label = font.render(“Munch game”, ۱, ‘black’): متن “Munch game” با رنگ سیاه آماده میشود تا روی صفحه نمایش داده شود.
- for event in pygame.event.get(): تمام رویدادهای ورودی از Pygame گرفته میشوند.
- if(withComputer and currentPlayer==’R’) or event.type == MOUSEBUTTONDOWN: اگر بازی با کامپیوتر انجام شود و نوبت بازیکن ‘R’ باشد یا کاربر بر روی صفحه کلیک کند.
- if(withComputer and currentPlayer==’R’): اگر بازی با کامپیوتر باشد و نوبت ‘R’ باشد.
- loc=compLoc(random.randint(1, 6)): مکان جدید بازیکن با استفاده از تابع compLoc و یک عدد تصادفی بین ۱ تا ۶ مشخص میشود.
- dice_clicked == True: در این نقطه به نظر میرسد که باید از عملگر ‘=’ به جای ‘==’ استفاده شود.
- elif event.type == MOUSEBUTTONDOWN: و اگر این رویداد یک کلیک ماوس باشد.
- loc = gridlocation(event.pos): مکان کلیکشده در شبکه بازی مشخص میشود.
- diceValue = random.randint(1, 6): یک عدد تصادفی برای مقدار تاس انتخاب میشود.
- print(“dice clicked”, currentPlayer): پیامی برای نمایش اینکه تاس کلیک شده و نوبت بازیکن فعلی چاپ میشود.
- for i in cList: برای هر بازیکن در لیست بازیکنان
- for p in Game_grid[i[0]][i[1]].playerList: برای هر بازیکن در لیست بازیکنهای در موقعیت مشخص کنید.
- font1 = pygame.font.SysFont(“Calibri”, ۵۰): یک فونت جدید با اندازه ۵۰ ایجاد میشود.
- label1 = font1.render(str(diceValue), 1, ‘black’): مقدار تاس به عنوان یک رشته تبدیل و به رنگ سیاه آماده میشود.
- DISPLAYSURF.blit(drawGrid(), (0, 0)): شبکه بازی روی سطح نمایش قرار داده میشود.
- DISPLAYSURF.blit(label, (800, 10)): متن “Munch game” روی صفحه نمایش میشود.
- DISPLAYSURF.blit(label1, (850, 500)): مقدار تاس روی صفحه نمایش داده میشود.
- pygame.display.update(): صفحه نمایش بهروزرسانی میشود تا تغییرات را به کاربر نشان دهد.
- if event.type == QUIT: اگر رویداد خروج باشد.
- ()sys.exit: برنامه به طور کامل خاتمه مییابد.
مرحله یازدهم: ایجاد پنجره اصلی بازی
ایجاد پنجره اصلی بازی اولین گام برای طراحی رابط کاربری است که به بازیکنان امکان تعامل با محیط بازی را میدهد. در این مرحله، با استفاده از کتابخانههای گرافیکی، اندازه و ظاهر پنجره، عنوان آن، و المانهای ضروری مانند دکمهها و برچسبها را تعیین میکنیم. این پنجره باید به گونهای طراحی شود که فضایی جذاب و کاربرپسند را فراهم کند. در ادامه، کدهای این بخش را توضیح خواهیم داد.
pygame.init() screen = pygame.display.set_mode((1000, 600)) pygame.display.set_caption('PythonGeeks Ludo Game') clock = pygame.time.Clock() font = pygame.font.SysFont("Arial", 20) class Button: """Create a button, then blit the surface in the while loop""" def __init__(self, text,num,comp,pos, font, bg="blue", feedback=""): self.x, self.y = pos self.font = pygame.font.SysFont("Arial", font) self.no=num self.text=text self.text = self.font.render(self.text, 1, pygame.Color("White")) self.cmp=comp self.change_text(bg) def change_text(self, bg="blue"): self.size = self.text.get_size() self.surface = pygame.Surface(self.size) self.surface.fill(bg) self.surface.blit(self.text, (0, 0)) self.rect = pygame.Rect(self.x, self.y, self.size[0], self.size[1]) def show(self): screen.blit(self.text , (self.x, self.y)) def click(self, event): x, y = pygame.mouse.get_pos() font1 = pygame.font.SysFont("Calibri", 50) label1 = font1.render("PythonGeeks Ludo Game", 1, 'yellow') screen.blit(label1, (230, 30)) if event.type == pygame.MOUSEBUTTONDOWN: if pygame.mouse.get_pressed()[0]: if self.rect.collidepoint(x, y): global n,withComputer n=self.no withComputer=self.cmp print(n,",",withComputer) pygame.quit() mainGame() def mainloop(): """ The infinite loop where things happen """ while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() button1.click(event) button2.click(event) button3.click(event) button4.click(event) button1.show() button2.show() button3.show() button4.show() clock.tick(30) pygame.display.update() button1 = Button( "Play with Computer",2,True, (۴۰۰, ۱۵۰), font=30, bg="navy") button2 = Button( "۲ Players",2,False, (۴۰۰, ۲۵۰), font=30, bg="navy") button3 = Button( "۳ Players",3,False, (۴۰۰, ۳۵۰), font=30, bg="navy",) button4 = Button( "۴ Players",4,False, (۴۰۰, ۴۵۰), font=30, bg="navy",) mainloop()
توضیحات کد:
- در اینجا یک پنجره ایجاد میشود و دکمهها به آن افزوده میشوند.
- یک کلاس به نام Button ساخته میشود تا نام دکمهها را تعیین کرده و آنها را بر اساس موقعیت مشخص شده روی صفحه قرار دهد. سپس بسته به دکمهای که کاربر کلیک میکند، تابع مربوطه اجرا میشود و متغیرهای n و withComputer اصلاح میشوند و تابع mainGame() اجرا میشود.
- کلیک روی هر یک از دکمهها بهطور مداوم با استفاده از یک حلقه while بررسی میشود.
خروجی بازی منچ
نتیجهگیری
در این مقاله، ما به بررسی نحوه ساخت بازی منچ با استفاده از زبان برنامهنویسی پایتون پرداختیم. ساختن این بازی به ما کمک کرد تا با مفاهیم اصلی برنامهنویسی، طراحی رابط کاربری و مدیریت وضعیتهای مختلف در یک بازی آشنا شویم. همچنین یاد گرفتیم که چگونه میتوانیم با استفاده از کلاسها و توابع، کد خود را سازماندهی کنیم و آن را کارآمدتر کنیم.
در نهایت، توسعه یک بازی مانند منچ نه تنها یک تجربه سرگرمکننده است، بلکه فرصتی عالی برای یادگیری اصول برنامهنویسی و تقویت مهارتهای حل مسئله میباشد. هر برنامهنویسی میتواند با ساختن چنین پروژههای ساده، به درک بهتری از مفاهیم پیچیدهتر دست یابد و به تدریج مهارتهای خود را ارتقا دهد. با گوشهچشمی به پروژههای آینده، میتوانید ایجاد بازیهای پیچیدهتر و جذابتر را نیز مد نظر قرار دهید.