> معین که پس از شکست در طراحی سوالات خوب برای مسابقات برنامه نویسی *#المپیکفناوری* از کار خود در کوئرا، برکنار شده است و در حال حاضر مشغول دوز *(Tic-tac-toe یا همان بازی ایکس-او خودمان)* بازی است! معین که آدم **پر دوز و کلکی** است و هیچ کاری را مثل سایر افراد انجام نمیدهد، در **دوز-بازی** هم **دوز و کلک جدیدی** میزند. او بازی دوز را به این شکل تغییر میدهد که **پس از انجام حرکت چهارم**، **اولین** حرکتی که از هر کدام از بازیکنها **باقی مانده** بود از بازی **پاک** میشود. معین برای معرفی این بازی به جهانیان قصد دارد تا نسخهی برخطی از آن را با استفاده از **لاراول** پیاده سازی کند.
# جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/71158/download_problem_initial_project/252155/) دانلود کنید.
<details class="yellow">
<summary>
**ساختار فایلها**
</summary>
```plaintext
moeine-dooz-o-kalak
├── app
│ ├── Http
│ │ ├── Livewire
│ │ │ ├── <mark class="orange" title="این فایل باید پیادهسازی شود">XOGame.php</mark>
│ ├── Models
│ └── Providers
├── bootstrap
├── config
├── database
├── public
├── resources
│ ├── views
│ │ ├── livewire
│ │ │ └── <mark class="orange" title="این فایل باید پیادهسازی شود">x-o-game.blade.php</mark>
│ │ └── <mark class="orange" title="این فایل باید پیادهسازی شود">dooz.blade.php</mark>
│ └── css
├── routes
│ └── web.php
├── storage
├── tests
├── README.md
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
└── vite.config.js
```
</details>
<details class="grey">
<summary>
**راهاندازی پروژه**
</summary>
**برای اجرای پروژه، باید `php` و `composer` را از قبل نصب کرده باشید.**
+ ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
+ دستور `composer install` را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید.
+ دستور `npm install` را در پوشهی اصلی پروژه برای نصب نیازمندیها اجرا کنید. *(توجه کنید که این پروژه از* [*Tailwindcss*](https://tailwindcss.com/) *استفاده میکند)*
+ دستور `npm run dev` را در مسیر پوشه اصلی پروژه اجرا کنید. در صورتی که این دستور را اجرا نکنید نمیتوانید ویوهای ساخته شده با *Tailwindcss* را مشاهده کنید.
+ برای اجرای تستهای نمونه، میتوانید از دستور `php artisan test` استفاده کنید.
</details>
# پیادهسازی پروژه
در این سوال قرار است شما به سراغ پیاده سازی این بازی هیجانانگیز با استفاده از [*Livewire3*](https://livewire.laravel.com) بروید!
<details class="green">
<summary>
**جزئیات دقیقتر و معرفی بازی جدید** _معینِ دوزُ کلک_
</summary>
# جزئیات دقیقتر و معرفی بازی جدید _معینِ دوزُ کلک_
همانطور که در بخش ابتدایی این سوال گفته شد، بازی معینِ دوزُ کلک یک نسخه خاصی از بازی دوز است که در آن پس از انجام حرکت چهارم، قدیمیترین *(ابتداییترین)* حرکتی که همان فرد انجام داده است باید از صفحه بازی حذف شود! به مثال زیر توجه کنید که در آن پس از حرکت چهارم `X`، حرکت اول او از بازی حذف میشود و به این ترتیب جا برای حرکتهای جدید باز میشود. همین شرایط برای بازیکن `O` و همچنین سایر حرکتها هم صادق است.
- برد بازی **مانند یک دوز کلاسیک**، زمانی اتفاق خواهد افتاد که یکی از بازکینان `X` یا `O` بتواند یک سطر، ستون و یا یکی از قطرهای اصلی را با `X` یا `O` تماما پر کند.
![پیش نمایش بازی](https://quera.org/qbox/view/zGeUQDuWCB/contest_game-demo-main.gif)
**نکته قابل توجه در این نسخه از بازی این است که ممکن است پس از انجام تعدادی حرکت، فرد موقعیت این را داشته باشد که یک سطر، ستون و یا قطر را کامل کند، اما پس از انجام مرحله مورد نظر برای تکمیل خانهها، به جای اینکه یکی از حرکتهای خارج از آن سطر، ستون و یا قطر جایگزین در تنها خانه باقی مانده برای تکمیلشان شود، به دلیل رعایت در ترتیب، یکی از حرکتهایی که در همان راستا قرار دارد در این خانه جایگزین شود. در این صورت فرد باید با طی کردن تعدادی مراحل بازی را به وضعیتی برساند که با رعایت ترتیب درست؛ یک راستا کامل شده و فرد بازی را برنده شود.**
![دموی بخش چلنجینگ بازی](https://quera.org/qbox/view/Pi2MMR7YOH/demo-game-challenge.gif)
در واقع این نسخه از بازی دوز، یک نسخه **دارای حافظه** میباشد! پس ترتیب حرکتهای انجام شده هم علاوه بر ماهیت و مکان حرکت انجام شده اهمیت خواهد داشت زیرا این مورد در ترتیب حذف شدن حرکتهای `X` یا `O` از صفحه و برد بازیکن نقش خواهد داشت.
</details>
<details class="blue">
<summary>
**جزئیات و ساختار `Route` و `View` بازی**
</summary>
برای دسترسی به این بازی در پروژه اولیه یک `Route` با آدرس `/dooz` به صورت پیشفرض تعریف شده است که به ویو `dooz` متصل شده است. در این مسیردهی و ویو تعریف شده نباید تغییری داده شود.
در فایل مربوط به ویو `dooz` شما باید **کامپوننتی** *(Component)* که از قبل در ساختار پروژه اولیه با نام `x-o-game` تعریف شده است را **استفاده** *(Include)* کنید. این کامپوننت همان بخش اصلی بازی دوز است که قرار است تا اجزای مختلف کامپوننتش را در بخشهای بعدی این سوال پیادهسازی کنید تا در نهایت بازی به درستی اجرا شود.
```php laravel web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/dooz', function () {
return view('dooz');
});
```
```php laravel dooz.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>❌ Moeine Dooz O Kalak ⭕</title>
@vite('resources/css/app.css')
@livewireStyles
</head>
<body>
{{--TODO: Include Laravel game component--}}
@livewireScripts
</body>
</html>
```
و در نهایت ساختار `View` مربوط به کامپوننت بازی که در مسیر `resources/views/livewire/x-o-game.blade.php` قرار گرفته است و باید در ویو `dooz.blade.php` از آن استفاده شود به شکل زیر است:
```php laravel x-o-game.blade.php
<div class="flex flex-col items-center justify-center min-h-screen bg-teal-700">
<h1 class="text-6xl font-bold text-white mb-8">❌ Moeine Dooz O Kalak ⭕</h1>
<!-- Input form for player names -->
<div class="flex flex-col items-center space-y-4">
<input type="text" placeholder="🏅First Player's name"
class="px-4 py-2 rounded bg-white text-black text-xl focus:outline-none">
<input type="text" placeholder="🏅Second Player's name"
class="px-4 py-2 rounded bg-white text-black text-xl focus:outline-none">
<button class="bg-cyan-500 text-white font-bold py-2 px-4 rounded hover:bg-cyan-600">
Start Game 🎮
</button>
</div>
<!-- Input form for player names -->
<!-- Winner animation section -->
<div class="absolute inset-0 flex items-start justify-center">
<div class="confetti-container">
@for ($i = 0; $i < 18; $i++)
<div class="confetti-piece"></div>
@endfor
</div>
<div class="text-white text-6xl font-bold animate-bounce scale-125 mt-12 z-50">
🎉 Winner: [WinnerPlayerName]! 🎉
</div>
</div>
<!-- Winner animation section -->
<!-- Game board section -->
<div class="grid grid-cols-3 gap-4 w-64">
<button class="bg-teal-700 border-4 border-teal-500 aspect-square text-5xl font-bold flex items-center justify-center
transition-transform transform hover:scale-105 hover:bg-teal-600 focus:outline-none focus:ring-2 focus:ring-yellow-300">
<span class="text-gray-300">X</span>
<span class="text-yellow-300">O</span>
</button>
</div>
<!-- Game board section -->
<!-- Current turn section -->
<div class="mt-6 text-xl text-white">
🕹 Current Turn:
<span class="font-bold animate-pulse">[X] - [PlayerName]</span>
</div>
<!-- Current turn section -->
<!-- Leaderboard to track wins -->
<div class="mt-6 text-xl text-white">
<div class="font-bold">📊 Leaderboard</div>
<div>[Player1Name]: [NumberOfWinsPlayer1] win(s)</div>
<div>[Player2Name]: [NumberOfWinsPlayer2] win(s)</div>
</div>
<!-- Leaderboard to track wins -->
</div>
```
- این ساختار صرفا ساختار آماده مربوط به رابط گرافیکی بازی میباشد و منطق نمایش المانهای مختلف آن باید توسط شما در این کامپوننت پیادهسازی شود.
</details>
<details class="yellow">
<summary>
**ساختار بازی معینِ دوزُ کلک**
</summary>
## ورود به بازی
پس از اجرای مسیر `/dooz` در ابتدا شما با ساختاری مشابه ساختار زیر مواجه میشوید، که در آن دو Input از شما دو رشته را به عنوان نام بازیکنان دریافت میکنند. سپس پس از فشردن دکمه `Start Game 🎮` باید برسی شود که در صورتی که هر دو نام وارد شده بودند بازی آغاز شود. نیازی به انجام هیچ اعتبارسنجیای برای نامهای ورودی نیست؛ صرفا این دو رشته نباید مقادیر خالی باشند.
![تصویر اول سوال معین دوز و کلک](https://quera.org/qbox/view/Sk7NVygJN3/moeindooz3.png)
پس از وارد کردن نامها باید با ساختاری مشابه ساختار زیر مواجه شوید، که در آن علاوه بر جدول $3 * 3$ بازی، یک جدول امتیاز برای دو نامی که وارد شده وجود خواهد داشت که مجموع امتیازات هر فرد را مشخص میکند.
![تصویر دوم سوال معین دوز و کلک](https://quera.org/qbox/view/NCDdOu62eu/moeindooz2.png)
![دموی بازی اول](https://quera.org/qbox/view/6PP691s2O9/final-demo-game-main.gif)
پس از انجام بازی با هر تعداد مرحله، با توجه به موارد نوشته شده در بخش جزئیات دقیقتر و معرفی بازی ممکن است بازی برندهای داشته باشد. در صورتی که فردی با انجام حرکتی موجب پیروزی شد، باید مطابق تصویر زیر، ` 🎉 Winner: [PlayerName]! 🎉 ` در بالای صفحه همراه با انیمیشن مورد نظر نمایش داده شده، جدول و وضعیت بازی ریست شده و همچنین به تعداد بردهای فردی که برنده بازی شده **یکی اضافه** شود.
![تصویر سوم سوال معین دوز و کلک](https://quera.org/qbox/view/khXuiNxfVO/moeindooz1.png)
**مورد قابل توجه این است که بازی را همیشه** `X` **شروع میکند اما بازیکنان هر دفعه به صورت یکی در میان نقش** `X` و `O` **را بر عهده میگیرند تا بازی عدالت بیشتری داشته باشد.** همچنین بازی را فردی شروع میکند که نامش در ابتدای ورود به بازی به عنوان نام نفر اول وارد شده است. برای مثال، اگر دوین نام اول و معین نام دوم باشد، در **بازی اول** دوین `X` و معین `O` و در **بازی دوم**، معین `X` و دوین `O` خواهد بود و **ترتیب چرخشی** به همین صورت برای باقی بازیها ادامه خواهد داشت. اما در **تمامی حالات** شروع کننده بازی `X` است و صرفا **فرد شروعکننده** تغییر خواهد کرد.
</details>
با توجه به موارد گفته شده در بخشهای بالا، شما باید ساختار *کامپوننت Livewire ای* `XOGame` را که در ساختار پروژه اولیه در مسیر `app/Livewire/XOGame.php` قرار دارد را مطابق با ساختار زیر پیاده سازی کنید:
```php laravel app/Livewire/XOGame.php
<?php
namespace App\Livewire;
use Livewire\Component;
class XOGame extends Component
{
public $board = [];
public $current_turn = 'X';
public $last_winner = null;
public $player1_name;
public $player2_name;
public $player1_wins = 0;
public $player2_wins = 0;
public $name_submitted = false;
public function mount()
{
// TODO: Implement
}
public function submitNames()
{
// TODO: Implement
}
public function resetBoard()
{
// TODO: Implement
}
public function makeMove($index)
{
// TODO: Implement
}
public function render()
{
// TODO: Implement
}
}
```
توضیحات ساختار و ویژگیهای این کامپوننت به شکل زیر است:
- **متغیر** `board`**:** این متغیر همانطور که از اسمش پیداست ساختار جدول را در خود ذخیره میکند. هر کدام از خانههای این جدول میتوانند یکی از مقادیر `X`، `O` و یا `null` باشند. این متغیر یک آرایه یکبعدی میباشد که اندیسهای شماره `0` تا `8` اش نشان دهنده خانههای جدول بازی هستند.
- **متغیر** `current_turn`**:** این متغیر نشاندهنده نوبت فعلی میباشد که میتواند یکی از مقادیر `X` و یا `O` را داشته باشد.
- **متغیر** `last_winner`**:** این متغیر مشخص میکند که آخرین فردی که برنده بازی شده است چه کسی بوده است. این متغیر در ابتدا مقداری برابر با `null` دارد اما بلافاصله پس از بردن بازی توسط یکی از `X` و یا `O` مقدار آن را ذخیرهسازی میکند.
- **متغیرهای** `player1_name` **و** `player2_name`**:** این دو متغیر نام دو بازیکن را دریافت و ذخیرهسازی میکنند.
- **متغیرهای** `player1_wins` **و** `player2_wins`**:** این دو متغیر تعداد برد هر کدام از بازیکنها را مشخص میکنند. این مقدار باید پس از برد هر فرد بروزرسانی شود و به صورت زنده در صفحه بازی نمایش داده شود.
- **متغیر** `name_submitted`**:** این متغیر یک متغیر بولی است که پس از وارد شدن نام بازیکنان مقدارش از `false` ابتدایی به `true` تغییر پیدا میکند.
توابعی که شما باید در این سوال پیادهسازی کنید به شکل زیر هستند:
- **تابع** `mount`**:** این تابع در واقع همان تابع سازنده کامپوننت خواهد بود.
- **تابع** `submitNames`**:** این تابع باید برسی کند که در صورتی که نام بازیکنان وارد شده بود، مقدار متغیر `name_submitted` را به `true` تغییر دهد. در ویو کامپوننت این مورد برسی شده و در صورتی که نام بازیکنان وارد شده بود دیگر بخش وارد کردن نام و شروع بازی نباید نمایش داده شود.
- **تابع** `resetBoard`**:** این تابع وضعیت بازی را ریست میکند. توجه کنید که با ریست وضعیت بازی صرفا جدول و نوبت بازی `X` یا `O` ریست میشود و تغییری در امتیاز بازیکنان داده نمیشود. در واقع این تابع پس از مشخص شدن هر وضعیت برد اجرا خواهد شد تا بتوان بازی را به تعداد دلخواهی بار با شرایط گفته شده انجام داد.
- **تابع** `makeMove`**:** این تابع در هر مرحله با فشردن بر روی یکی از خانههای جدول اجرا میشود. هر خانه که یکی از اندیسها `0` تا `8` را داراست *(خانههای جدول هر بار از سمت چپ به راست اندیسدهی میشوند، یعنی اندیسهای سطر اول برابر با* `0 1 2` *و اندیسهای سطر دوم برابر با* `3 4 5` *خواهند بود)* مهره تعیین شده در `current_turn` در خانه مشخص شده از جدول جایگزین میشود.
- **تابع** `render`**:** این تابع کامپوننت بازی را رندر میکند.
# آنچه باید آپلود کنید
+ **توجه**: شما میتوانید هر تعداد تابع یا متغیر کمکی را در ساختار کامپوننت بازی (یا همان کلاس `XOGame`) پیادهسازی کنید، اما توابع و متغیرهایی که در مورد آنها در سوال توضیح داده شده است **نباید** تغییری داشته باشند. تغییر در ساختار ورودی توابع یا نام توابع و متغیرهای تعریف شده باعث **عدم** دریافت نمره از سمت داور خودکار خواهد شد.
+ **توجه**: تمامی موارد مربوط به استایلهای بازی در پروژه اولیه از قبل قرار گرفته اند و شما نیازی به پیادهسازی هیچگونه استایلی ندارید. در این سوال که از Tailwindcss برای طراحی رابط کاربری استفاده شده است، موارد مورد نیاز مربوط به کامپوننت بازی در مسیر `View` این کامپوننت در مسیر `resources/views/livewire/x-o-game.blade.php` قرار گرفته اند، اما شما نیاز پیدا خواهید کرد تا مانند ساختار گفته شده منطق نمایش آنها را پیادهسازی کنید.
+ **توجه**: پیادهسازی منطق نمایش در ویوی کامپوننت `resources/views/livewire/x-o-game.blade.php` در سیستم داوری مورد تست قرار خواهد گرفت، لذا از تغییر نام یا افرودن فواصل کمتر یا بیشتر در متن المانهایی که در اختیار شما قرار داده شده مانند دکمهها و یا متنهای نمایش داده شده **اکیدا خودداری کنید**. ایجاد تغییرات منجر به **عدم** دریافت نمره از سیستم داوری خواهد شد.
+ **توجه**: پس از اعمال تغییرات، کل پروژه به غیر از پوشهی `vendor`و `node_modules` را _Zip_ کرده و آپلود کنید.
+ **توجه**: نام فایل _ZIP_ اهمیتی ندارد.
معینِ دوز و کلک (Laravel)