**مهارتهای لازم:**
- تسلط به hook
- کار کاردن با state و reducer
- آشنایی با قوانین بازی مار
--------------------------------------------------
ظاهر کلی برنامه بدین صورت است:
![ظاهر برنامه](https://quera.ir/qbox/view/jN9tR8ZuMW/bazi_mar.gif)
# پروژه اولیه
پروژه اولیه را از
[این لینک](/contest/assignments/20209/download_problem_initial_project/65885/)
دانلود کنید.
ساختار فایلهای این پروژه به صورت زیر است.
```
snake-game
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── __tests__
│ │ └── sample.test.js
│ ├── assets
│ │ └── index.css
│ ├── components
│ │ ├── apple.js
│ │ ├── game.js
│ │ └── snake.js
│ ├── hooks
│ │ └── use-snake.js
│ ├── index.js
│ ├── initialState.js
│ └── logic.js
├── package-lock.json
└── package.json
```
<details class="brown">
<summary>
راهاندازی پروژه
</summary>
**برای اجرای پروژه، باید `NodeJS` و `npm` را از قبل نصب کرده باشید.**
- ابتدا پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
- در پوشهی `snake-game` ، دستور `npm install` را برای نصب نیازمندیها اجرا کنید.
- در همین پوشه، دستور `npm start` را برای راهاندازی پروژه اجرا کنید.
- پس از انجام موفق این مراحل، با مراجعه به آدرس `http://localhost:3000/`
میتوانید نتیجه را ببینید.
</details>
# جزئیات
در این سوال شما باید هوک *useSnake* که در فایل `hooks/use-snake.js` تعریف شده را کامل کنید. این هوک در کامپوننت `game.js` استفاده شده و آبجکتی شامل تمام اعضای وضعیت اولیه بازی برمیگرداند که در ابتدا برابر با آن است و سپس براساس موارد زیر تغییر میکند. شما در این سوال باید تغییرات لازم را در هر ۱۰۰ میلیثانیه (سرعت بازی) و کلیک شدن دکمههای w s a d در کیبورد روی وضعیت بازی اعمال کنید. (وضعیت اولیه بازی را حتما از فایل `initialState.js` بخوانید)
- مار و غذا در ابتدا در مکان اولیه تعریف شده هستند.
- در شروع کار مار به سمت راست شروع به حرکت میکند.
- حرکتها یک لیست است که در حالت اولیه یک آیتم دارد، با فشار دادن دکمه های w s a d اگر حرکت مخالف اولین حرکت تو این لیست نباشد به لیست اضافه میشود. وقتی حرکتها داخل لیست بیشتر از یکی باشد به ترتیب از اولین حرکت، رفته و از لیست خارج میشوند تا به یک آیتم در لیست برسد. (مسیر حرکت مار اولین ایتم لیست حرکتها است و این لیست همواره حداقل یک آیتم دارد)
- مار بعد از خوردن غذا رشد میکند.
- غذا بعد از خورده شدن تو مکان رندمی از بورد ظاهر میشود که حتما داخل بورد است.
- بازی با خوردن مار با خودش دوباره از وضعیت اولیه شروع میشود.
**وضعیت اولیه بازی**
```json
{
<mark title="تعداد ستون های بورد بازی">cols</mark>: 20,
<mark title="تعداد ردیف های بورد بازی">rows</mark>: 14,
<mark title="طول و عرض بورد بازی">board</mark>: {width: 700, height: 500},
<mark title="سرعتی که مار با ان حرکت میکند">speed</mark>: 100,
<mark title=" ارایه ای از حرکت ها، اگر یک حرکت وجود داشته باشد مار با همان حرکت برای همیشه ادامه میدهد تا وقتی که دکمه های wasd زده میشود و حرکت ها در این ارایه به صورت صف وارد میشوند و به ترتیب انجام میشوند">moves</mark>: [EAST],
<mark title="ارایه ای از مختصاتهای نقطه های مار">snake</mark>: [{x: 2, y: 2}],
<mark title="مختصات غذا که اولین بار با `initialState` پر میشود و دفعات بعد به صورت رندم است">apple</mark>: {x: 16, y: 2},
}
```
**مختصات**
مختصاتها در بازی مار به صورت یه ابجکت هستند. مبدا مخصات `{x: 0, y: 0}` گوشه بالا سمت چپ بورد بازی است.
![نمودار](https://quera.ir/qbox/view/ZrVA8RlEx5/bazi_mart_chart2.png)
مربع ابی نشان داده شده در مختصات `{x: 2, y: 1}` است.
# نکات
- شما تنها مجاز به اعمال تغییرات در فایلهای`hooks/use-snake.js` و `logic.js` هستید.
- فایل `logic.js` فقط یک فایل اضافه است که بتوانید توابعی که مربوط به *logic* بازی است را در آن تعریف کنید. این فایل میتواند خالی باشد
- تعدادی تست نمونه در پروژه اولیه وجود دارد که با استفاده از آنها میتوانید کد خود را تست کنید.
- پس از اعمال تغییرات، پروژه را _ZIP_ کرده و ارسال کنید. دقت کنید که پوشهی `node_modules` در فایل ارسالی نباشد.
# قسمت آموزشی
در این قسمت راهنماییهای سوال، به مرور اضافه میشود. مشکلاتتان در راستای حل سوال را میتوانید از بخش ["سوال بپرسید"](https://quera.ir/contest/clarification/20209/) مطرح کنید.
<details class="blue">
<summary>
راهنمایی ۱
</summary>
از `useReducer` برای مدیریت استیت بازی استفاده کنید و `INIT_STATE` را به عنوان استیت اولیه به آن بدهید.
</details>
<details class="blue">
<summary>
راهنمایی ۲
</summary>
بعداز تعریف `useReducer` از یک `useEffect` برای تعریف یک *interval* استفاده میکنیم که در هر ۱۰۰ میلی ثانیه *dispatch* را با یک اسمی مانند *move* صدا میزند این *interval* را در یک متغیر میذاریم تا هنگام `return`، بتوانیم `clearInterval` را روی آن صدا بزنیم.
از یک `useEffect` دیگر برای تعریف دکمههای w a s d استفاده میکنیم و dispatch را با اسمی مانند *change_direction* و البته جهت جدید صدا میزنیم.
بقیه برنامه، فقط منطق برنامه است که داخل *reducer* نوشته میشوند.
**توجه**: این فقط یکی از روشهای ساخت این بازی است و قطعا روشهای دیگری هم وجود دارد.
</details>
<details class="blue">
<summary>
راهنمایی۳
</summary>
**قوانین بازی مار:**
اگر *head* بعدی مار که قراره حرکت کنیم را *nextHead* در نظر بگیریم
- **برخورد**: برخورد وقتی اتفاق میفتد که مختصات *nextHead* با مختصات یکی از نقطههای مار برابر باشد.
- **حرکت مار**: اگر مار برخورد داشت به حالت اولیه برمیگردد، اگر برخورد نداشت با اضافه کردن *nextHead* به نقطههای مار به جز آخرین نقطه بدست میآید اما در صورت خوردن سیب آخرین نقطه هم اضافه میشود.
- سیب در یک نقطه رندوم یعنی *x* باید عدد صحیح رندوم بین ۰ و اندازه *cols* (که از *INIT_STATE* میاد) و *y* باید عدد صحیح رندوم بین ۰ و *rows* (که از *INIT_STATE* میاد) باشد.
</details>