بازی دوز یا Tic Tac Toe یکی از بازیهای قدیمی و محبوب در دنیا است که به سادگی و با استفاده از یک صفحه ۳×۳ انجام میشود. این بازی که به عنوان یک بازی دو نفره شناخته میشود، نیاز به استراتژیهای ساده و تصمیمگیریهای هوشمندانه دارد. در این مقاله قصد داریم به بررسی برنامه نویسی بازی دوز و نحوه پیادهسازی آن در زبانهای برنامهنویسی مختلف بپردازیم. همچنین قوانین بازی دوز و نحوه برنده شدن در این بازی را بررسی خواهیم کرد. این مقاله برای کسانی که میخواهند در زمینه ساخت بازی دوز و پیادهسازی آن در محیطهای مختلف برنامهنویسی، چه برای شروع یادگیری و چه برای پروژههای پیچیدهتر، اطلاعاتی جامع کسب کنند، مفید خواهد بود.
معرفی بازی دوز
بازی دوز یک بازی دو نفره است که به کمک یک صفحه ۳×۳ انجام میشود. هر بازیکن یک علامت مخصوص (X یا O) را برای خود برمیدارد و نوبت به نوبت، یکی از خانههای خالی صفحه را با علامت خود پر میکند. هدف از بازی دوز این است که بازیکن اولین فردی باشد که سه علامت خود را بهصورت افقی، عمودی یا قطری در یک خط قرار دهد. این بازی به دلیل سادگی قوانین و ساختار گرافیکی بسیار ابتدایی آن، برای شروع یادگیری برنامهنویسی و ساخت بازیهای ساده انتخاب خوبی است.
قوانین بازی دوز:
- بازی با یک صفحه ۳×۳ انجام میشود.
- دو بازیکن وجود دارند که به نوبت علامتهای خود را روی صفحه قرار میدهند.
- هر بازیکن فقط میتواند یک خانه را در هر نوبت پر کند.
- اولین بازیکنی که سه علامت خود را در یک خط افقی، عمودی یا قطری قرار دهد، برنده است.
اگر تمامی خانهها پر شوند و هیچ بازیکنی سه علامت خود را در یک خط قرار ندهد، بازی به پایان میرسد و نتیجه مساوی میشود.
نحوه برنده شدن در بازی دوز
برای برنده شدن در بازی دوز، بازیکن باید سه علامت خود را بهصورت پشتسرهم در یکی از خطوط افقی، عمودی یا قطری قرار دهد. بهطور خاص، یک بازیکن باید یکی از ترکیبهای زیر را بسازد:
- سه خانه متوالی افقی در یک ردیف
- سه خانه متوالی عمودی در یک ستون
- سه خانه متوالی در یکی از قطرهای صفحه
نحوه پیادهسازی الگوریتمها در بازی دوز
برای ساخت بازی دوز بهطور بهینه و طراحی یک هوش مصنوعی قوی، استفاده از الگوریتمهای مختلف برای تحلیل وضعیت بازی و اتخاذ تصمیمات استراتژیک بسیار حائز اهمیت است. در این بخش به توضیح دو الگوریتم اصلی برای پیادهسازی هوش مصنوعی در بازی دوز پرداخته میشود: الگوریتم Minimax و الگوریتمهای یادگیری ماشین مانند Q-Learning.
۱. الگوریتم Minimax
الگوریتم Minimax یکی از الگوریتمهای جستجوی تصمیمگیری است که بهطور ویژه برای بازیهایی مانند دوز (Tic Tac Toe)، شطرنج و داما طراحی شده است. هدف این الگوریتم این است که بهترین حرکت را برای بازیکن محاسبه کند با در نظر گرفتن اینکه حریف نیز بازی را بهطور بهینه انجام میدهد.
نحوه عملکرد الگوریتم Minimax
الگوریتم Minimax به صورت درختی از همهٔ حرکات ممکن بازی را بررسی میکند. این الگوریتم بهصورت بازگشتی، تمامی حالات ممکن از بازی را از موقعیت فعلی تا وضعیت پایان بازی ارزیابی میکند و به دنبال بهترین نتیجه برای بازیکن میگردد.
درخت جستجو: درخت جستجوی Minimax بهطور کامل تمامی حرکات ممکن را از وضعیت فعلی تا موقعیتهای پایانی مانند برد، باخت یا مساوی بررسی میکند. این درخت میتواند تا حد زیادی بزرگ باشد، اما با استفاده از الگوریتمهای بهینهسازی مانند برش آلفا-بتا میتوان آن را بهطور چشمگیری کوچکتر کرد.
ارزیابی وضعیتها: در این الگوریتم، در هر مرحله از بازی دو نوع وضعیت داریم:
- وضعیت ماکزیمم (Max): این وضعیت متعلق به بازیکنی است که در حال تلاش برای بردن است.
- وضعیت مینیمم (Min): این وضعیت متعلق به حریف است که در تلاش برای جلوگیری از برد بازیکن و برد خود است.
تابع ارزیابی: در هر گره از درخت، یک تابع ارزیابی برای تعیین ارزش وضعیت انجام میشود. این تابع معمولاً بر اساس اینکه آیا بازیکن برنده، بازنده یا بازی مساوی است، مقداردهی میشود.
پیشبینی حرکتها: الگوریتم Minimax از دو گره برگ برگشت میزند: اگر بازیکن در وضعیت Max باشد، الگوریتم بیشترین مقدار را از بین حالتهای ممکن انتخاب میکند. در صورتی که وضعیت Min باشد، الگوریتم کمترین مقدار را انتخاب میکند.
مثال ساده الگوریتم Minimax برای بازی دوز
در بازی دوز، درخت جستجو میتواند شامل تمامی حرکتهای ممکن باشد. برای هر حرکت، Minimax ارزیابی میکند که آیا حرکت به بازیکن برتری میدهد یا خیر. بهطور خلاصه، این الگوریتم میتواند مانند یک «هوش مصنوعی بیطرف» عمل کند که با دیدی استراتژیک حرکتهای بهترین گزینه را انجام میدهد.
در حالت ساده، Minimax با ارزیابی نتایج تمام حرکات، انتخاب میکند که چه حرکتهایی به بازیکن کمک میکنند تا برنده شود یا جلوی برد حریف را بگیرد.
محدودیتهای الگوریتم Minimax
الگوریتم Minimax برای بازیهای کوچک مانند دوز مناسب است، زیرا تعداد حالتهای ممکن کم است. اما برای بازیهای پیچیدهتر مانند شطرنج، این الگوریتم به دلیل تعداد زیاد حالتها (که درخت جستجو به سرعت افزایش مییابد) زمانبر و پردازشبر خواهد بود.
۲. الگوریتمهای یادگیری ماشین (Q-Learning)
الگوریتمهای یادگیری ماشین میتوانند رویکرد پیشرفتهتری برای ساخت هوش مصنوعی بازی دوز ارائه دهند. یکی از الگوریتمهای معروف در این زمینه، الگوریتم Q-Learning است که برای یادگیری تصمیمات بهینه از طریق تجربه و تعامل با محیط طراحی شده است.
نحوه عملکرد Q-Learning
Q-Learning یک الگوریتم یادگیری تقویتی است که به سیستم این امکان را میدهد که از تجربیات قبلی خود برای یادگیری نحوه اتخاذ بهترین تصمیمها استفاده کند. این الگوریتم بر اساس یک ماتریس به نام Q-Table عمل میکند که در آن هر وضعیت بازی و اقدام ممکن دارای یک ارزش Q است. این ارزشها نشاندهندهٔ بهترین حرکت ممکن در یک وضعیت خاص است.
وضعیت (State): در هر گام از بازی، الگوریتم یک وضعیت بازی را ارزیابی میکند. برای بازی دوز، وضعیت میتواند موقعیت فعلی صفحه بازی باشد (اینکه کدام خانهها پر شده و کدام خالی هستند).
عملیات (Action): در هر وضعیت، الگوریتم باید انتخاب کند که کدام خانه را برای حرکت بعدی انتخاب کند. این انتخاب بهطور تصادفی یا با توجه به تجربیات قبلی انجام میشود.
مقدار Q : Q-Learning از مقدار Q(s, a) استفاده میکند که نشاندهندهٔ کیفیت اقدام a در وضعیت s است. به مرور زمان و پس از انجام تعداد زیادی از بازیها، این مقادیر بهطور خودکار بهینه میشوند.
بهروزرسانی Q-Table: پس از هر حرکت و ارزیابی نتیجه، الگوریتم مقدار Q را برای آن وضعیت و اقدام بهروزرسانی میکند.
پاداشها: در بازی دوز، پاداشها میتوانند بر اساس نتایج بازی تنظیم شوند:
- +۱ برای برد
- ۰ برای مساوی
- -۱ برای باخت
مزایای Q-Learning
- یادگیری خودکار: هوش مصنوعی قادر است با تعامل و تجربه، تصمیمات بهینه بگیرد.
- انعطافپذیری: این الگوریتم میتواند در بازیهای پیچیدهتر نیز اعمال شود.
- غیرنیاز به برنامهنویسی دقیق استراتژی: برخلاف Minimax که نیاز به ارزیابی دستی حرکات دارد، Q-Learning میتواند بهطور خودکار استراتژیها را یاد بگیرد.
معایب Q-Learning
- نیاز به دادههای زیاد: Q-Learning به مدت زمان طولانی برای یادگیری بهینه نیاز دارد.
- کند بودن در یادگیری: برای بازیهایی که تعداد زیادی حالت دارند، فرایند یادگیری ممکن است کند باشد.
کد برنامه نویسی بازی دوز با زبانهای مختلف
در این بخش از مقاله، کدهای برنامه نویسی بازی دوز را با استفاده از زبانهای مختلف بررسی خواهیم کرد. هر زبان برنامهنویسی دارای ویژگیهای خاص خود است که میتواند در نحوه پیادهسازی بازی دوز تأثیرگذار باشد.
کدنویسی بازی دوز در زبان پایتون
قطعه کد زیر پیادهسازی بازی دوز (Tic Tac Toe) در زبان پایتون است. در کد زیر، از دو توابع اصلی print_board و check_win برای نمایش صفحه بازی و بررسی پیروزی استفاده شده است. کاربران بهصورت نوبتی خانهها را پر میکنند تا زمانی که یک بازیکن برنده شود یا بازی مساوی شود.
# برنامه نویسی بازی دوز با زبان پایتون def print_board(board): for row in board: print(" | ".join(row)) print("-" * 5) def check_win(board): # بررسی ردیفها و ستونها for i in range(3): if board[i][0] == board[i][1] == board[i][2] != " ": return True if board[0][i] == board[1][i] == board[2][i] != " ": return True # بررسی قطرها if board[0][0] == board[1][1] == board[2][2] != " ": return True if board[0][2] == board[1][1] == board[2][0] != " ": return True return False def tic_tac_toe(): board = [[" " for _ in range(3)] for _ in range(3)] player = "X" while True: print_board(board) row = int(input(f"بازیکن {player}, ردیف را وارد کنید (۰-۲): ")) col = int(input(f"بازیکن {player}, ستون را وارد کنید (۰-۲): ")) if board[row][col] == " ": board[row][col] = player if check_win(board): print_board(board) print(f"بازیکن {player} برنده شد!") break player = "O" if player == "X" else "X" else: print("این خانه پر است، خانه دیگری انتخاب کنید.") tic_tac_toe()
کدنویسی بازی دوز در زبان جاوا
کد زیر پیادهسازی بازی دوز (Tic Tac Toe) با زبان جاوا «Java» است.
در کد زیر، مشابه پایتون از یک صفحه ۳×۳ برای بازی استفاده شده است. بازیکنان نوبتی حرکت میکنند و پس از هر حرکت، بررسی میشود که آیا بازیکنی برنده شده است یا خیر.
import java.util.Scanner; public class TicTacToe { static char[][] board = new char[3][3]; static char currentPlayer = 'X'; public static void main(String[] args) { initializeBoard(); Scanner scanner = new Scanner(System.in); while (true) { printBoard(); System.out.println("بازیکن " + currentPlayer + "، ردیف را وارد کنید (۰-۲): "); int row = scanner.nextInt(); System.out.println("بازیکن " + currentPlayer + "، ستون را وارد کنید (۰-۲): "); int col = scanner.nextInt(); if (board[row][col] == ' ') { board[row][col] = currentPlayer; if (checkWin()) { printBoard(); System.out.println("بازیکن " + currentPlayer + " برنده شد!"); break; } currentPlayer = (currentPlayer == 'X') ? 'O' : 'X'; } else { System.out.println("این خانه پر است، خانه دیگری انتخاب کنید."); } } } static void initializeBoard() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { board[i][j] = ' '; } } } static void printBoard() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { System.out.print(board[i][j] + " | "); } System.out.println(); System.out.println("----------"); } } static boolean checkWin() { for (int i = 0; i < 3; i++) { if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') { return true; } if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') { return true; } } if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') { return true; } if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ') { return true; } return false; } }
در کد بالا، تابع ()tic_tac_toe بازی اصلی را اجرا میکند. در ابتدا یک تخته بازی ۳x۳ با خانههای خالی ایجاد میشود. سپس در یک حلقه بینهایت، بازی ادامه مییابد.
کدنویسی بازی دوز در زبان سیشارپ (C#)
در این کد سیشارپ، مشابه پایتون و جاوا، بازی دوز پیادهسازی شده است. برای هر حرکت، صفحه چاپ میشود و پس از هر نوبت، بررسی میشود که آیا بازیکنی برنده شده است یا نه.
using System; public class TicTacToe { static char[,] board = new char[3, 3]; static char currentPlayer = 'X'; public static void Main() { InitializeBoard(); while (true) { PrintBoard(); Console.WriteLine($"بازیکن {currentPlayer}، ردیف را وارد کنید (۰-۲): "); int row = int.Parse(Console.ReadLine()); Console.WriteLine($"بازیکن {currentPlayer}، ستون را وارد کنید (۰-۲): "); int col = int.Parse(Console.ReadLine()); if (board[row, col] == ' ') { board[row, col] = currentPlayer; if (CheckWin()) { PrintBoard(); Console.WriteLine($"بازیکن {currentPlayer} برنده شد!"); break; } currentPlayer = (currentPlayer == 'X') ? 'O' : 'X'; } else { Console.WriteLine("این خانه پر است، خانه دیگری انتخاب کنید."); } } } static void InitializeBoard() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { board[i, j] = ' '; } } } static void PrintBoard() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { Console.Write(board[i, j] + " | "); } Console.WriteLine(); Console.WriteLine("----------"); } } static bool CheckWin() { for (int i = 0; i < 3; i++) { if (board[i, 0] == board[i, 1] && board[i, 1] == board[i, 2] && board[i, 0] != ' ') { return true; } if (board[0, i] == board[1, i] && board[1, i] == board[2, i] && board[0, i] != ' ') { return true; } } if (board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2] && board[0, 0] != ' ') { return true; } if (board[0, 2] == board[1, 1] && board[1, 1] == board[2, 0] && board[0, 2] != ' ') { return true; } return false; } }
در کد بالا، در داخل متد Main، یک حلقه while (true) برای اجرای بازی ادامه پیدا میکند:
- ابتدا وضعیت تخته با استفاده از تابع ()PrintBoard نمایش داده میشود.
- از بازیکن خواسته میشود که ردیف و ستون مورد نظر خود را وارد کند.
- اگر خانه انتخابشده خالی باشد، علامت بازیکن در آن خانه قرار میگیرد.
- سپس با استفاده از تابع ()CheckWin بررسی میشود که آیا بازیکن برنده شده است یا خیر.
- اگر یکی از بازیکنان برنده شود، پیام برنده بودن او چاپ میشود و بازی تمام میشود.
- در غیر این صورت نوبت به بازیکن دیگر میرسد.
- اگر خانه انتخابشده پر باشد، پیام خطا چاپ میشود و بازیکن باید خانه دیگری انتخاب کند.
زبان برنامهنویسی سیشارپ همراه با Windows Forms گزینهای مناسب برای طراحی بازیهای گرافیکی ساده محسوب میشود و تجربهای کاربرپسند را فراهم میکند. اگر علاقهمند به ساخت بازی دوز (XO) تحت شبکه هستید، میتوانید از سورسکد آماده این بازی که در مجموعه پیاستور ارائه شده، بهرهمند شوید.
خروجی
ما هریک از کدهای بالا را جرا کردیم و خروجی به شکل زیر است:
| | ---------- | | ---------- | | بازیکن X، ردیف را وارد کنید (۰-۲): ۰ بازیکن X، ستون را وارد کنید (۰-۲): ۰ X | | ---------- | | ---------- | | بازیکن O، ردیف را وارد کنید (۰-۲): ۱ بازیکن O، ستون را وارد کنید (۰-۲): ۱ X | | ---------- | O | ---------- | | بازیکن X، ردیف را وارد کنید (۰-۲): ۰ بازیکن X، ستون را وارد کنید (۰-۲): ۱ X | X | ---------- | O | ---------- | | بازیکن O، ردیف را وارد کنید (۰-۲): ۲ بازیکن O، ستون را وارد کنید (۰-۲): ۲ X | X | ---------- | O | ---------- | | O بازیکن X، ردیف را وارد کنید (۰-۲): ۰ بازیکن X، ستون را وارد کنید (۰-۲): ۲ X | X | X ---------- | O | ---------- | | بازیکن X برنده شد!
در این مثال، بازیکن X با قرار دادن سه علامت مشابه در ردیف اول برنده میشود.
اگر این مطلب برای شما مفید واقع شد و توانست به سؤالات یا نیازهای شما پاسخ دهد، پیشنهاد میکنیم سایر آموزشهای مرتبط ما را نیز مطالعه کنید.
نتیجهگیری
در این مقاله، بهطور کامل به بررسی دو الگوریتم قدرتمند برای پیادهسازی برنامه نویسی بازی دوز پرداختهایم: الگوریتم Minimax که بهطور سیستماتیک تمامی حرکتها را بررسی میکند و بهترین انتخاب را برای بازیکن محاسبه میکند، و الگوریتم Q-Learning که بهطور خودکار و از طریق تجربههای بازی، تصمیمات بهینه را یاد میگیرد. همچنین به بررسی نحوه برنامه نویسی بازی دوز در زبانهای مختلف مانند پایتون، جاوا و سیشارپ پرداختیم.
برای ساخت بازی دوز با هوش مصنوعی، انتخاب هر یک از این الگوریتمها بستگی به پیچیدگی پروژه و منابع مورد نظر دارد. اگر هدف شما ایجاد یک بازی ساده و سریع است، Minimax گزینه مناسبی خواهد بود. اما اگر قصد دارید هوش مصنوعی بازی را بهطور پیشرفتهتری پیادهسازی کنید، Q-Learning میتواند عملکرد بهتری ارائه دهد.
امیدواریم که این مقاله به شما در درک بهتر نحوه پیادهسازی این بازی در زبانهای مختلف کمک کرده باشد و شما را به ادامه یادگیری و ایجاد بازیهای پیچیدهتر ترغیب کند.