بناء لعبة الثعبان الكلاسيكية باستخدام JavaScript: دليلك الشامل
هيكل اللعبة الأساسي: HTML
نبدأ بتعريف العناصر الأساسية للعبتنا باستخدام لغة ترميز النص التشعبي (HTML). على الرغم من بساطته، فإن هذا الهيكل يضع الأساس لكل تفاعل بصري ووظيفي في اللعبة:
< h1 > Nokia 3310 snake </ h1 >
< div class = "scoreDisplay" > </ div >
< div class = "grid" > </ div >
< div class = "button" >
< button class = "top" > top </ button >
< button class = "bottom" > bottom </ button >
< button class = "left" > left </ button >
< button class = "right" > right </ button >
</ div >
< div class = "popup" >
< button class = "playAgain" > play Again </ button >
</ div >
يحتوي هذا الكود على عدة عناصر <div> أساسية تؤدي وظائف محددة:
- العنصر
<h1>لعرض عنوان اللعبة. - العنصر
<div>ذو الفئةscoreDisplay: سيُستخدم لعرض نقاط اللاعب أثناء اللعب. - العنصر
<div>ذو الفئةgrid: هذا هو لوح لعبتنا الرئيسي، حيث سيتحرك الثعبان وتظهر التفاحات. سنقوم بإنشائه كشبكة 10×10 باستخدام JavaScript. - العنصر
<div>ذو الفئةbutton: يحتوي على أزرار التحكم (أعلى، أسفل، يسار، يمين) المخصصة بشكل أساسي لمستخدمي الهواتف المحمولة. سنتيح التحكم عبر لوحة المفاتيح لمستخدمي أجهزة الكمبيوتر المكتبية. - العنصر
<div>ذو الفئةpopup: سيحتوي على زر “العب مرة أخرى” (playAgain) الذي يظهر عند انتهاء اللعبة.
تنسيق اللعبة: CSS
الآن، لنضف بعض الأناقة والترتيب للعبتنا باستخدام أوراق الأنماط المتتالية (CSS). هذه الأنماط ستحدد مظهر لوحة اللعب، الثعبان، التفاحة، وعناصر الواجهة الأخرى:
body {
background : rgb ( 212 , 211 , 211 );
}
.grid {
width : 200px ;
height : 200px ;
border : 1px solid red;
margin : 0 auto;
display : flex;
flex-wrap : wrap;
}
.grid div {
width : 20px ;
height : 20px ;
/*border:1px black solid; box-sizing:border-box*/
}
.snake {
background : blue;
}
.apple {
background : yellow;
border-radius : 20px ;
}
.popup {
background : rgb ( 32 , 31 , 31 );
width : 100px ;
height : 100px ;
position : fixed;
top : 100px ;
left : 100px ;
display : flex;
justify-content : center;
align-items : center;
}
في هذا الجزء من CSS، نركز على عدة نقاط:
- الفئة
.grid: تمثل لوحة اللعب. قمنا بتحديد أبعادها (widthوheight) وجعلها تستخدم خاصيةdisplay: flexمعflex-wrap: wrap. يتيح لنا هذا الأسلوب ترتيب عناصر<div>الداخلية التي سننشئها ديناميكياً باستخدام JavaScript، بحيث تصطف أفقياً وتنتقل إلى سطر جديد تلقائياً عند الحاجة، مما يحافظ على حدود الشبكة. - الفئة
.grid div: تحدد الأبعاد لكل خلية فردية داخل الشبكة (20 بكسل × 20 بكسل)، والتي ستمثل جزءاً من الثعبان أو التفاحة. - الفئتان
.snakeو.apple: تُستخدمان لتلوين الخلايا التي تحتوي على أجزاء الثعبان (باللون الأزرق) والتفاحة (باللون الأصفر مع حدود دائرية). - الفئة
.popup: هي نافذة منبثقة ثابتة (position: fixed) ستظهر عند انتهاء اللعبة، وتحتوي على زر إعادة اللعب.

الآن بعد أن وضعنا الهيكل والأسلوب، حان الوقت لإضفاء الحياة على لعبتنا باستخدام JavaScript.
منطق اللعبة: JavaScript
تعريف المتغيرات الأساسية
نبدأ بتعريف المتغيرات التي سنستخدمها للوصول إلى عناصر HTML، وتخزين حالة اللعبة، وتحديد سلوك الثعبان والتفاح. هذه المتغيرات هي العمود الفقري لمنطق لعبتنا:
let grid = document .querySelector( ".grid" );
let popup = document .querySelector( ".popup" );
let playAgain = document .querySelector( ".playAgain" );
let scoreDisplay = document .querySelector( ".scoreDisplay" );
let left = document .querySelector( ".left" );
let bottom = document .querySelector( ".bottom" );
let right = document .querySelector( ".right" );
let up = document .querySelector( ".top" );
let width = 10 ;
let currentIndex = 0 ;
let appleIndex = 0 ;
let currentSnake = [ 2 , 1 , 0 ];
let direction = 1 ;
let score = 0 ;
let speed = 0.8 ;
let intervalTime = 0 ;
let interval = 0 ;
شرح موجز للمتغيرات الهامة:
grid،popup،playAgain،scoreDisplay،left،bottom،right،up: هذه المتغيرات تقوم بالوصول إلى عناصر HTML المقابلة لها باستخدامdocument.querySelector()، مما يتيح لنا التفاعل معها برمجياً.width: يمثل عرض الشبكة (عدد الخلايا في الصف الواحد)، وهو 10 في حالتنا.currentIndex: يُستخدم لتتبع موقع رأس الثعبان الحالي.appleIndex: يُستخدم لتتبع موقع التفاحة.currentSnake: مصفوفة تمثل جسم الثعبان. كل رقم في المصفوفة يمثل فهرس خلية في الشبكة، حيث يكون العنصر الأول (currentSnake[0]) هو رأس الثعبان.direction: يحدد اتجاه حركة الثعبان (1 لليمين، -1 لليسار،widthللأسفل،-widthللأعلى).score: لتخزين نقاط اللاعب.speed: عامل يحدد مدى سرعة زيادة سرعة اللعبة بعد كل تفاحة.intervalTime: المدة الزمنية بين كل خطوة حركة للثعبان بالمللي ثانية.interval: متغير لتخزين معرّف الدالةsetInterval()للتحكم في إيقاف وتشغيل حركة الثعبان.
تهيئة اللعبة عند التحميل
نحرص على أن تبدأ اللعبة بالتهيئة الصحيحة بمجرد تحميل محتوى HTML بالكامل. لهذا الغرض، نستخدم معالج الحدث DOMContentLoaded:
document .addEventListener( "DOMContentLoaded" , function ( ) {
document .addEventListener( "keyup" , control);
createBoard();
startGame();
playAgain.addEventListener( "click" , replay);
});
عندما يتم تحميل محتوى DOM بالكامل، يتم تشغيل هذا الحدث، ويقوم بالمهام التالية:
- تعيين مستمع حدث (
eventListener) على الكائنdocumentلرصد ضغطات المفاتيح (keyup) والتعامل معها بواسطة الدالةcontrol()، والتي ستُستخدم لتحريك الثعبان. - استدعاء الدالة
createBoard()لإنشاء لوحة اللعب. - استدعاء الدالة
startGame()لبدء اللعبة وتهيئة حالتها الأولية. - تعيين مستمع حدث لزر “العب مرة أخرى” (
playAgain) بحيث عند النقر عليه، يتم استدعاء الدالةreplay()لإعادة تشغيل اللعبة.
الدالة createBoard(): بناء لوحة اللعب
تتولى هذه الدالة مسؤولية إنشاء الشبكة التي ستقام عليها اللعبة. بما أننا نصمم شبكة 10×10، فإننا نحتاج إلى 100 عنصر <div> لتمثيل الخلايا الفردية:
function createBoard ( ) {
popup.style.display = "none" ;
for ( let i = 0 ; i < 100 ; i++) {
let div = document .createElement( "div" );
grid.appendChild(div);
}
}
آلية عمل الدالة:
- أولاً، تقوم بإخفاء النافذة المنبثقة (
popup) للتأكد من أنها غير مرئية عند بدء اللعبة. - ثم، تستخدم حلقة تكرارية (
forloop) لإنشاء 100 عنصر<div>جديد. - كل عنصر
<div>يتم إنشاؤه، يُضاف كابن للعنصرgridالذي يمثل لوحة اللعب.
هذه العناصر <div> المضافة حديثاً ستكتسب تلقائياً الأنماط المحددة مسبقاً في CSS للفئة .grid div، مما يمنحها الأبعاد والشكل الصحيحين.
الدالة startGame(): إطلاق اللعبة
تُعد الدالة startGame() نقطة الانطلاق الفعلية للعبة. تقوم بتهيئة جميع المتغيرات الضرورية وتضع الثعبان والتفاحة في مواضعهما الأولية، ثم تبدأ دورة حركة الثعبان:
function startGame ( ) {
let squares = document .querySelectorAll( ".grid div" );
randomApple(squares); // تحديد موقع عشوائي للتفاحة
direction = 1 ;
scoreDisplay.innerHTML = score;
intervalTime = 1000 ;
currentSnake = [ 2 , 1 , 0 ];
currentIndex = 0 ;
currentSnake.forEach( ( index ) => squares[index].classList.add( "snake" ));
interval = setInterval (moveOutcome, intervalTime);
}
خطوات عمل الدالة:
- الحصول على الخلايا: بما أن عناصر
<div>التي تشكل الشبكة تُنشأ ديناميكياً، فإن أول خطوة هي الحصول على جميع هذه العناصر وتخزينها في المتغيرsquaresباستخدامdocument.querySelectorAll(".grid div"). - وضع التفاحة: تستدعي الدالة
randomApple(squares)لتحديد موقع عشوائي للتفاحة على لوحة اللعب. - تهيئة حالة اللعبة:
- يتم تعيين الاتجاه الأولي للثعبان (
direction = 1، أي لليمين). - يتم تحديث عرض النقاط (
scoreDisplay.innerHTML = score) إلى الصفر. - يتم تعيين الوقت الأولي بين الحركات (
intervalTime = 1000مللي ثانية، أي ثانية واحدة). - يتم إعادة تعيين الثعبان إلى موضعه الأولي (
currentSnake = [2, 1, 0]). - يتم تعيين الفهرس الحالي (
currentIndex = 0).
- يتم تعيين الاتجاه الأولي للثعبان (
- عرض الثعبان: يتم المرور على عناصر مصفوفة
currentSnakeباستخدامforEach()، ولكل فهرس، يتم إضافة الفئة"snake"إلى العنصر<div>المقابل فيsquares، مما يجعل الثعبان مرئياً. - بدء الحركة: يتم استخدام
setInterval()لاستدعاء الدالةmoveOutcome()بشكل متكرر كلintervalTimeمللي ثانية. يتم تخزين معرّف هذا الفاصل الزمني في المتغيرintervalللتحكم فيه لاحقاً (مثل إيقافه باستخدامclearInterval()). الدالةmoveOutcome()هي التي تحدد ما يحدث في كل خطوة حركة للثعبان.
الدالة moveOutcome(): تحديد نتيجة الحركة
تُنفذ هذه الدالة بشكل متكرر بواسطة setInterval()، وهي المسؤولة عن تحديد ما إذا كانت حركة الثعبان ستؤدي إلى انتهاء اللعبة أو استمرارها:
function moveOutcome ( ) {
let squares = document .querySelectorAll( ".grid div" );
if (checkForHits(squares)) {
alert( "you hit something" );
popup.style.display = "flex" ;
return clearInterval (interval);
} else {
moveSnake(squares);
}
}
آلية عمل الدالة:
- الحصول على الخلايا: مثل دالة
startGame()، تحصل هذه الدالة أولاً على جميع عناصر<div>التي تشكل الشبكة وتخزنها في المتغيرsquares. - التحقق من الاصطدامات: تستدعي الدالة
checkForHits(squares)للتحقق مما إذا كان الثعبان قد اصطدم بشيء (مثل الجدران أو جسمه).- إذا أعادت
checkForHits()القيمةtrue(أي حدث اصطدام):- يتم عرض رسالة تنبيه للمستخدم.
- يتم إظهار النافذة المنبثقة (
popup) التي تحتوي على زر “العب مرة أخرى”. - يتم إيقاف حركة الثعبان عن طريق استدعاء
clearInterval(interval)، مما ينهي اللعبة.
- إذا أعادت
checkForHits()القيمةfalse(أي لم يحدث اصطدام):- يتم استدعاء الدالة
moveSnake(squares)لتحريك الثعبان خطوة واحدة إلى الأمام.
- يتم استدعاء الدالة
- إذا أعادت
باختصار، كل ثانية (أو حسب intervalTime)، تقرر اللعبة إما أن تنتهي إذا كان هناك اصطدام، أو يستمر الثعبان في التحرك خطوة للأمام.
الدالة moveSnake(): تحريك الثعبان
تتولى هذه الدالة مهمة تحديث موقع الثعبان على لوحة اللعب. يتم استدعاؤها في كل خطوة حركة عندما لا يكون هناك اصطدام:
function moveSnake ( squares ) {
let tail = currentSnake.pop();
squares[tail].classList.remove( "snake" );
currentSnake.unshift(currentSnake[ 0 ] + direction);
eatApple(squares, tail);
squares[currentSnake[ 0 ]].classList.add( "snake" );
}
شرح تفصيلي لآلية الحركة:
- إزالة الذيل: أولاً، يتم إزالة العنصر الأخير من مصفوفة
currentSnakeباستخدام الدالةpop(). هذا العنصر يمثل ذيل الثعبان. ثم يتم إزالة الفئة"snake"من الخلية المقابلة فيsquares، مما يجعلها فارغة بصرياً. هذا يحاكي حركة الثعبان إلى الأمام، حيث يترك موقعه السابق. - إضافة رأس جديد: يتم إضافة عنصر جديد إلى بداية مصفوفة
currentSnakeباستخدام الدالةunshift(). قيمة هذا العنصر الجديد هي مجموع رأس الثعبان الحالي (currentSnake[0]) والاتجاه (direction).- مثال: إذا كان الثعبان في الموضع
[2, 1, 0](الرأس عند الفهرس 2)، والاتجاه هو1(لليمين).- بعد
pop()، تصبح المصفوفة[2, 1]. - يتم حساب الرأس الجديد:
2 + 1 = 3. - بعد
unshift()، تصبح المصفوفة[3, 2, 1]. وهكذا، تحرك الثعبان خطوة واحدة لليمين.
- بعد
- إذا كان الاتجاه هو
width(أي 10، للحركة للأسفل)، فإن الرأس الجديد سيكونcurrentSnake[0] + 10.
- مثال: إذا كان الثعبان في الموضع
- التحقق من أكل التفاح: يتم استدعاء الدالة
eatApple(squares, tail). هذه الدالة تتحقق مما إذا كان الثعبان قد تحرك إلى خلية تحتوي على تفاحة، وتتعامل مع النتائج (زيادة الطول، النقاط، السرعة). - عرض الرأس الجديد: أخيراً، يتم إضافة الفئة
"snake"إلى الخلية الجديدة التي يمثلها رأس الثعبان الحالي (squares[currentSnake[0]])، مما يجعله مرئياً على الشاشة.
الدالة checkForHits(): التحقق من الاصطدامات
هذه الدالة حاسمة لتحديد متى تنتهي اللعبة. تقوم بالتحقق من أربعة سيناريوهات اصطدام رئيسية: اصطدام الثعبان بالجدران الأربعة للوحة اللعب، أو اصطدامه بجسمه الخاص. إذا تحقق أي من هذه الشروط، تعيد الدالة true، وإلا تعيد false.
function checkForHits ( squares ) {
if (
(currentSnake[ 0 ] + width >= width * width && direction === width) || // الاصطدام بالجدار السفلي
(currentSnake[ 0 ] % width === width - 1 && direction === 1 ) || // الاصطدام بالجدار الأيمن
(currentSnake[ 0 ] % width === 0 && direction === -1 ) || // الاصطدام بالجدار الأيسر
(currentSnake[ 0 ] - width <= 0 && direction === -width) || // الاصطدام بالجدار العلوي
squares[currentSnake[ 0 ] + direction].classList.contains( "snake" ) // الاصطدام بجسم الثعبان
) {
return true ;
} else {
return false ;
}
}
دعنا نفصل الشروط داخل عبارة if:
- الاصطدام بالجدار السفلي:
(currentSnake[0] + width >= width * width && direction === width)- يتحقق هذا الشرط إذا كان رأس الثعبان (
currentSnake[0]) على وشك تجاوز الحد السفلي للشبكة (width * widthهو إجمالي عدد الخلايا، أي 100)، وكان اتجاه الحركة هو الأسفل (direction === width، أي 10). على سبيل المثال، إذا كان رأس الثعبان عند الفهرس 97، فإن97 + 10 = 107، وهو أكبر من 100، مما يعني تجاوز الحد.
- الاصطدام بالجدار الأيمن:
(currentSnake[0] % width === width - 1 && direction === 1)- يتحقق هذا الشرط إذا كان رأس الثعبان على وشك تجاوز الحافة اليمنى للشبكة. الخلايا الموجودة على الحافة اليمنى يكون باقي قسمة فهرسها على
width(10) مساوياً لـwidth - 1(أي 9، مثل 9، 19، 29…). إذا كان رأس الثعبان عند إحدى هذه الخلايا (مثلاً 39) وكان اتجاهه لا يزال لليمين (direction === 1)، فإنه يصطدم بالجدار الأيمن.
- الاصطدام بالجدار الأيسر:
(currentSnake[0] % width === 0 && direction === -1)- هذا هو عكس الشرط السابق. الخلايا الموجودة على الحافة اليسرى يكون باقي قسمة فهرسها على
widthمساوياً للصفر (مثل 0، 10، 20…). إذا كان رأس الثعبان عند إحدى هذه الخلايا وكان اتجاهه لا يزال لليسار (direction === -1)، فإنه يصطدم بالجدار الأيسر.
- الاصطدام بالجدار العلوي:
(currentSnake[0] - width <= 0 && direction === -width)- هذا هو عكس شرط الجدار السفلي. يتحقق إذا كان رأس الثعبان على وشك تجاوز الحد العلوي للشبكة (فهرس 0 أو أقل)، وكان اتجاه الحركة هو الأعلى (
direction === -width، أي -10).
- الاصطدام بجسم الثعبان:
squares[currentSnake[0] + direction].classList.contains("snake")- يتحقق هذا الشرط إذا كانت الخلية التي سيتجه إليها رأس الثعبان في الخطوة التالية تحتوي بالفعل على الفئة
"snake"، مما يعني أن الثعبان قد اصطدم بجزء من جسمه.
إذا كان أي من هذه الشروط صحيحاً، تعيد الدالة true وتنتهي اللعبة. وإلا، تعيد false ويستمر الثعبان في الحركة.
الدالة eatApple(): أكل التفاحة
تُستدعى هذه الدالة في كل خطوة حركة للثعبان للتحقق مما إذا كان قد وصل إلى موقع التفاحة. إذا حدث ذلك، فإنها تتعامل مع جميع الآثار المترتبة على أكل التفاحة:
function eatApple ( squares, tail ) {
if (squares[currentSnake[ 0 ]].classList.contains( "apple" )) {
squares[currentSnake[ 0 ]].classList.remove( "apple" );
squares[tail].classList.add( "snake" );
currentSnake.push(tail);
randomApple(squares);
score++;
scoreDisplay.textContent = score;
clearInterval (interval);
intervalTime = intervalTime * speed;
interval = setInterval (moveOutcome, intervalTime);
}
}
عندما يأكل الثعبان تفاحة، تحدث الإجراءات التالية:
- إزالة التفاحة: يتم إزالة الفئة
"apple"من الخلية التي كان بها التفاح، مما يجعلها خلية عادية. - زيادة طول الثعبان: يتم إعادة إضافة الذيل الذي تم إزالته في بداية حركة الثعبان (المتغير
tail) إلى نهاية مصفوفةcurrentSnakeباستخدام الدالةpush(). هذا يزيد من طول الثعبان بمقدار خلية واحدة. - وضع تفاحة جديدة: يتم استدعاء الدالة
randomApple(squares)لتحديد موقع عشوائي جديد للتفاحة على لوحة اللعب. - زيادة النقاط: يتم زيادة قيمة المتغير
scoreبمقدار واحد، ويتم تحديث عرض النقاط على الشاشة (scoreDisplay.textContent = score). - زيادة سرعة اللعبة:
- يتم أولاً إيقاف الفاصل الزمني الحالي للحركة باستخدام
clearInterval(interval). - ثم يتم تقليل
intervalTimeبضربه في عامل السرعة (speed، وهو 0.8)، مما يجعله أصغر وبالتالي يزيد من سرعة حركة الثعبان. - أخيراً، يتم إعادة تعيين فاصل زمني جديد باستخدام
setInterval(moveOutcome, intervalTime)بالسرعة الجديدة.
- يتم أولاً إيقاف الفاصل الزمني الحالي للحركة باستخدام
الدالة randomApple(): وضع التفاحة عشوائياً
تُستخدم هذه الدالة لتحديد موقع عشوائي للتفاحة على لوحة اللعب، مع التأكد من أنها لا تظهر داخل جسم الثعبان:
function randomApple ( squares ) {
do {
appleIndex = Math .floor( Math .random() * squares.length);
} while (squares[appleIndex].classList.contains( "snake" ));
squares[appleIndex].classList.add( "apple" );
}
آلية عمل الدالة:
- اختيار موقع عشوائي: تستخدم حلقة
do...whileلاختيار فهرس عشوائي (appleIndex) من بين جميع خلايا الشبكة (squares.length) باستخدامMath.random()وMath.floor(). - التحقق من وجود الثعبان: الشرط في حلقة
whileيتحقق مما إذا كانت الخلية المختارة عشوائياً (squares[appleIndex]) تحتوي على الفئة"snake".- إذا كانت تحتوي على الفئة
"snake"، فإن الحلقة تستمر في التكرار، وتختار موقعاً عشوائياً آخر. - تستمر هذه العملية حتى يتم العثور على خلية لا تحتوي على جزء من الثعبان.
- إذا كانت تحتوي على الفئة
- وضع التفاحة: بمجرد العثور على موقع مناسب، يتم إضافة الفئة
"apple"إلى تلك الخلية، مما يجعل التفاحة مرئية على الشاشة.
الدالة control(): التحكم بالثعبان
تُستخدم هذه الدالة لمعالجة مدخلات لوحة المفاتيح، مما يسمح للاعب بتغيير اتجاه حركة الثعبان. يتم استدعاؤها عند كل ضغطة مفتاح (keyup) بفضل eventListener الذي قمنا بتعيينه سابقاً:
function control ( e ) {
if (e.keycode === 39 ) {
direction = 1 ; // يمين
} else if (e.keycode === 38 ) {
direction = -width; // أعلى (التحرك 10 خلايا للأعلى)
} else if (e.keycode === 37 ) {
direction = -1 ; // يسار (التحرك خلية واحدة لليسار)
} else if (e.keycode === 40 ) {
direction = +width; // أسفل (التحرك 10 خلايا للأسفل)
}
}
تعتمد هذه الدالة على خاصية e.keycode من كائن الحدث (e) لتحديد المفتاح الذي تم الضغط عليه. كل مفتاح له رمز فريد:
39: مفتاح السهم الأيمن. عند الضغط عليه، يتم تعيينdirectionإلى1.38: مفتاح السهم العلوي. عند الضغط عليه، يتم تعيينdirectionإلى-width(أي -10)، مما يحرك الثعبان 10 خلايا للأعلى (صف كامل).37: مفتاح السهم الأيسر. عند الضغط عليه، يتم تعيينdirectionإلى-1.40: مفتاح السهم السفلي. عند الضغط عليه، يتم تعيينdirectionإلى+width(أي +10)، مما يحرك الثعبان 10 خلايا للأسفل (صف كامل).
هذه التغييرات في متغير direction هي التي توجه الثعبان في الخطوة التالية من حركته.
التحكم عبر أزرار الشاشة (للأجهزة المحمولة)
بالإضافة إلى التحكم عبر لوحة المفاتيح، قمنا بتوفير أزرار تحكم على الشاشة لمستخدمي الأجهزة المحمولة. تعمل هذه الأزرار بنفس مبدأ تغيير متغير direction:
up.addEventListener( "click" , () => (direction = -width));
bottom.addEventListener( "click" , () => (direction = +width));
left.addEventListener( "click" , () => (direction = -1 ));
right.addEventListener( "click" , () => (direction = 1 ));
كل زر من أزرار التحكم (up، bottom، left، right) يحتوي على مستمع حدث "click" يقوم بتحديث متغير direction بالقيمة المناسبة، تماماً كما تفعل مفاتيح الأسهم في لوحة المفاتيح.
الدالة replay(): إعادة تشغيل اللعبة
عند انتهاء اللعبة، تظهر النافذة المنبثقة التي تحتوي على زر "العب مرة أخرى". تتولى الدالة replay() مسؤولية إعادة تهيئة اللعبة بالكامل عندما ينقر اللاعب على هذا الزر:
function replay ( ) {
grid.innerHTML = "" ;
createBoard();
startGame();
popup.style.display = "none" ;
}
خطوات إعادة التشغيل:
- مسح لوحة اللعب: يتم مسح جميع المحتويات الحالية لعنصر
gridعن طريق تعيينgrid.innerHTML = ""، مما يزيل الثعبان والتفاحة القديمين. - إعادة بناء اللوحة: يتم استدعاء الدالة
createBoard()لإعادة إنشاء 100 خلية<div>جديدة للوحة اللعب. - بدء لعبة جديدة: يتم استدعاء الدالة
startGame()لتهيئة جميع متغيرات اللعبة، ووضع الثعبان والتفاحة في مواضعهما الأولية، وبدء دورة الحركة من جديد. - إخفاء النافذة المنبثقة: يتم إخفاء النافذة المنبثقة (
popup) لتختفي رسالة "انتهت اللعبة" وزر إعادة التشغيل.
وبهذا، تكون قد أعدت اللعبة بالكامل وجاهزة لجولة جديدة من التحدي!

تهانينا! لقد وصلت إلى نهاية هذا الدليل الشامل. نأمل أن تكون قد استمتعت ببرمجة لعبة الثعبان الخاصة بك باستخدام JavaScript. لقد قمنا بتغطية مفاهيم برمجية مهمة مثل push()، pop()، setInterval()، clearInterval()، و eventListener.
يمكنك الاطلاع على اللعبة النهائية هنا: Codepen - Snake Game.
الخلاصة التقنية
يُظهر هذا المشروع البسيط للعبة الثعبان قوة JavaScript في بناء تطبيقات ويب تفاعلية ومعقدة نسبياً من الصفر. لقد استخدمنا مزيجاً فعالاً من HTML لهيكلة العناصر، وCSS لتصميم الواجهة، وJavaScript لإضفاء الحياة والمنطق على اللعبة.
من الناحية التقنية، تُعد اللعبة مثالاً ممتازاً على التعامل مع الـ DOM ديناميكياً، وإدارة حالة اللعبة باستخدام المصفوفات (مثل currentSnake)، والتعامل مع الأحداث (DOMContentLoaded، keyup، click)، بالإضافة إلى استخدام الدوال الزمنية (setInterval و clearInterval) لتنظيم دورة اللعبة. كما أنها تقدم نظرة عملية على كيفية بناء منطق الكشف عن الاصطدامات وتعديل سرعة اللعبة ديناميكياً. هذا المشروع يمثل نقطة انطلاق قوية للمطورين المبتدئين لفهم أساسيات تطوير الألعاب على الويب وتطبيق مفاهيم برمجية جوهرية في سياق عملي وممتع.