
بروزرسانی: 23 خرداد 1404
نحوه ساخت یک بازی Connect Four در HTML، CSS و Vanilla JavaScript
در این آموزش، نحوه ساخت یک بازی Connect Four کاملاً کارآمد در HTML، CSS و Vanilla JavaScript را پوشش خواهیم داد. در اینجا نسخه ی نمایشی است که ما روی آن کار خواهیم کرد. روی اسلات های موجود کلیک کنید تا رنگ بعدی را در صف قرار دهید!
با نشانه گذاری ساده HTML شروع کنید
خوب، بیایید شروع کنیم. در متن فایل HTML خود، کد زیر را اضافه کنید.
1 | |
2 | |
3 | Connect 4 |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 |
برخی از سبک های CSS را اعمال کنید
استایل خود را با وارد کردن یک فونت سفارشی شروع می کنیم.
1 | @import url("https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"); |
در بدنه، از انعطاف پذیری برای حالت دادن به همه چیز به صورت افقی و عمودی استفاده کنید. برای مطابقت با طرح، یک فونت بزرگ به عنوان اضافه کنید.
1 | body { |
2 | font-family: Arial, sans-serif; |
3 | background-color: #e0e0e0; |
4 | text-align: center; |
5 | display: flex; |
6 | justify-content: center; |
7 | align-items: center; |
8 | height: 100vh; |
9 | margin: 0; |
10 | font-family: "DM Mono", monospace; |
11 | }
|
12 | |
13 | |
14 | h1 { |
15 | font-size: 4.5rem; |
16 | color: #000; |
17 | margin-bottom: 20px; |
18 | } |
دایره قرمز در سمت راست بازیکن فعلی را نشان می دهد. اگر پخش کننده فعلی قرمز باشد، قرمز و در غیر این صورت، زرد نشان داده می شود. این سبک ها به شکل و طرح دست می یابند.
1 | .status { |
2 | width: 60px; |
3 | height: 60px; |
4 | /* background-color: #ffcc00; */
|
5 | border-radius: 50%; |
6 | box-shadow: inset -5px -5px 10px rgba(0, 0, 0, 0.3); |
7 | margin: 5px; |
8 | } |
این عنصر برنده فعلی را نشان می دهد
1 | .message .winner { |
2 | font-size: 2.5rem; |
3 | font-weight: bold; |
4 | color: #000; |
5 | margin: 0; |
6 | } |
مهم ترین جزء بازی Connect Four شبکه است که از 6 سطر و 7 ستون تشکیل شده است. استایل شبکه را به تابلو اضافه کنید و به هر سلول در تابلو مانند شکل زیر استایل دهید.
1 | .board { |
2 | display: grid; |
3 | grid-template-columns: repeat(7, 1fr); |
4 | gap: 10px; |
5 | background-color: #0066cc; |
6 | padding: 20px; |
7 | border-radius: 10px; |
8 | border: 10px solid #0055aa; |
9 | margin-bottom: 20px; |
10 | }
|
11 | .cell { |
12 | width: 80px; |
13 | height: 80px; |
14 | background-color: #ffffff; |
15 | border-radius: 50%; |
16 | cursor: pointer; |
17 | |
18 | } |
در یک بازی Connect Four، بازیکنان معمولاً به نوبت دیسک ها را به صورت شبکه رها می کنند. برای شبیه سازی بصری این افکت، سبک های خاصی را به هر بازیکن اختصاص می دهیم. به عنوان مثال، زمانی که نوبت به RED می رسد، یک را اضافه می کنیم red
کلاس به عنصر مربوطه، و وقتی نوبت به YELLOW رسید، یک را اضافه می کنیم yellow
کلاس
این سبک ها دایره وضعیت را بسته به پخش کننده فعلی تغییر می دهند.
1 | .yellow-selected { |
2 | background-color: #ffdc00; |
3 | }
|
4 | |
5 | .red-selected { |
6 | background-color: #ff4136; |
7 | } |
در نهایت، این سبک ها را به دکمه تنظیم مجدد اضافه کنید.
1 | #reset-btn { |
2 | padding: 10px 20px; |
3 | background-color: #060606; |
4 | color: white; |
5 | border: none; |
6 | margin-top: 20px; |
7 | |
8 | border-radius: 5px; |
9 | font-size: 1rem; |
10 | cursor: pointer; |
11 | }
|
12 | |
13 | #reset-btn:hover { |
14 | background-color: #383938; |
15 | } |
جاوا اسکریپت: تابلو را ایجاد کنید
برای دیدن برد، از جاوا اسکریپت برای ایجاد div برای هر سلول در شبکه استفاده می کنیم. سپس به هر سلول کلاس سلول اختصاص داده می شود و به تابلو اضافه می شود.
بیایید عناصری را که باید به روز شوند را دریافت کنیم.
1 | const board = document.querySelector(".board"); |
2 | const status = document.querySelector(".status"); |
3 | const resetBtn = document.getElementById("reset-btn"); |
4 | const winner = document.querySelector(".winner"); |
متغیرهای زیر را ایجاد کنید:
1 | let rows = 6; |
2 | let cols = 7; |
متغیرهای ردیف و ستون نشان دهنده تعداد سطرها و ستون ها در شبکه هستند.
سپس یک تابع به نام ایجاد کنید createGameBoard()
و کد زیر را اضافه کنید
1 | function createGameBoard() { |
2 | |
3 | for (let r = 0; r < rows; r++) { |
4 | for (let c = 0; c < cols; c++) { |
5 | const disc = document.createElement("div"); |
6 | disc.classList.add("cell"); |
7 | disc.setAttribute("data-col", c); |
8 | disc.setAttribute("data-row", r); |
9 | board.appendChild(disc); |
10 | |
11 | }
|
12 | }
|
13 | } |
در تابع بالا، با حلقه زدن از طریق هر سطر و ستون، برای هر سلول شروع می کنیم. ما ایجاد می کنیم element, add the cell class, and add a data attribute to store the position of each cell. Finally, we append the disc to the
board
.
گام بعدی این است که اطمینان حاصل کنید که هر بار که بازیکن روی یک سلول کلیک می کند، سلول وضعیت فعلی بازیکن را دریافت می کند. ما همچنین می خواهیم بازیکنان را به طور متناوب تغییر دهیم و رنگ های مربوطه را به هر سلول اضافه کنیم. برای اطمینان از اینکه ردیف های بالایی قبل از ردیف پایین پر نمی شوند، از پایین شروع می کنیم.
بیایید با اعلام شروع کنیم currentPlayer
و isGameOver
متغیرها
1 | let currentPlayer = "red"; |
2 | let isGameOver = false; |
بیایید یک آرایه GameBoard را نیز مقداردهی کنیم، یک آرایه دوبعدی که نشان دهنده شبکه تخته است.
1 | let gameBoard = Array.from({ length: rows }, () => Array(cols).fill(null)); |
با کد زیر آپدیت کنید:
1 | board.addEventListener("click", function (e) { |
2 | status.classList.remove(`${currentPlayer}-selected`); |
3 | let col = parseInt(e.target.getAttribute("data-col")); |
4 | |
5 | if (e.target.classList.contains("cell") && !isGameOver) { |
6 | for (let row = rows - 1; row >= 0; row--) { |
7 | let clickedCell = board.querySelector( |
8 | `(data-row="${row}")(data-col="${col}")` |
9 | );
|
10 | if ( |
11 | !clickedCell.classList.contains("red") && |
12 | !clickedCell.classList.contains("yellow") |
13 | ) { |
14 | clickedCell.classList.add(currentPlayer); |
15 | gameBoard(row)(col) = currentPlayer; |
16 | |
17 | |
18 | currentPlayer = currentPlayer === "red" ? "yellow" : "red"; |
19 | |
20 | status.classList.add(`${currentPlayer}-selected`); |
21 | break; |
22 | }
|
23 | }
|
24 | }
|
25 | }); |
این زمان خوبی برای تجزیه و تحلیل آنچه در بالا اتفاق می افتد است:
- اولین کاری که انجام می دهیم این است که نمایش وضعیت را بازنشانی کنیم تا مطمئن شویم هیچ پخش کننده ای به عنصر متصل نیست. بعد، ویژگی های سلول کلیک شده را دریافت می کنیم.
- با شروع حلقه از ردیف-1 (یعنی ردیف 5)، اطمینان حاصل می کنیم که از پایین شروع می کنیم و به سمت بالا حرکت می کنیم.
- هنگامی که یک سلول خالی پیدا کردیم - سلولی که علامت زرد یا قرمز ندارد - یک علامت برای نمایش بازیکن فعلی اضافه می کنیم.، سپس صفحه نمایش را به روز کنید تا پخش کننده بعدی نشان داده شود.
- پس از افزودن علامت، به پخش کننده دیگر سوئیچ می کنیم و وضعیت را به روز می کنیم تا بدانیم نوبت بعدی کیست. پس از قرار دادن علامت، از حلقه خارج می شویم.
وقتی روی یک سلول کلیک می شود، اکنون باید چیزی شبیه به این داشته باشیم.



اکنون که بازیکنان می توانند به نوبت بازی را انجام دهند، وقت آن است که برنده شدن را بررسی کنیم.
در یک بازی Connect Four، یک برد با داشتن چهار دیسک متوالی همرنگ در شرایط زیر انجام می شود:
- برد افقی: چهار سلول متوالی در یک ردیف افقی حاوی دیسک های هم رنگ.
- برد عمودی: چهار سلول متوالی در یک ستون عمودی حاوی دیسک های هم رنگ.
- برد مورب: چهار سلول متوالی که یک خط مورب (بالا از چپ به پایین-راست یا از بالا-راست به پایین-چپ) با دیسک های همرنگ تشکیل می دهند.
یک تابع به نام ایجاد کنید checkWin()
.
1 | function checkWin(){ |
2 | // check win logic
|
3 | |
4 | } |
در این تابع، هر جهت را بررسی می کنیم. بیایید با یک شروع کنیم افقی برنده شوید
برد افقی
برای برد افقی، می خواهیم از منطق استفاده کنیم gameBoard(row)(col)
که سلول فعلی را برمی گرداند و برای هر سلول، 3 سلول کنار آن را بررسی می کنیم. اگر سلول های متعلق به همان بازیکن را داشته باشیم، برای بازیکن فعلی یک برد در نظر گرفته می شود.
کد خود را به صورت زیر به روز کنید:
1 | function checkForWin() { |
2 | for (let r = 0; r < rows; r++) { |
3 | for (let c = 0; c <= cols - 4; c++) { |
4 | const player = gameBoard(r)(c); |
5 | if (player) { |
6 | if ( |
7 | player === gameBoard(r)(c + 1) && |
8 | player === gameBoard(r)(c + 2) && |
9 | player === gameBoard(r)(c + 3) |
10 | ) { |
11 | // console.log("Win horizontally");
|
12 | return true; |
13 | }
|
14 | }
|
15 | }
|
16 | }
|
17 | |
18 | return false; |
19 | } |
بیایید کد را تجزیه کنیم، ما آن را داریم gameBoard
آرایه ای که به شکل زیر است:
1 | const gameBoard = ( |
2 | (null, null, null, null, null, null, null), // row 0 |
3 | (null, null, null, null, null, null, null), // row 1 |
4 | (null, null, null, null, null, null, null), // row 2 |
5 | (null, null, null, null, null, null, null), // row 3 |
6 | (null, null, null, null, null, null, null), // row 4 |
7 | (null, null, null, null, null, null, null), // row 5 |
8 | );
|
هنگامی که ما حلقه از طریق row = 0
به row = 5.
برای هر ردیف، چهار ستون متوالی را در یک زمان بررسی می کنیم. به عنوان مثال، در row = 5,
اگر از ستون شروع کنیم col = 0,
سلول های زیر را با توجه به بررسی می کنیم (5)(0):
1 | (5)(0+1), (5)(0+2), and (5)(0+3) |
اگر همه این سلول ها حاوی همان بازیکنی باشند که سلول اصلی را با آن مقایسه می کنیم، ((5(0))، برای آن بازیکن یک برد در نظر گرفته می شود. برای مثال، اگر RED برای شرایط افقی برنده شود، به نظر می رسد. این:



برد عمودی
برای بررسی یک عمودی برد، منطق مشابه چک افقی است، اما این بار، ما در حال چرخش از طریق ستون ها هستیم. برای هر ستون، ما در حال بررسی چهار ردیف متوالی با اشاره به هر سلول در آن سطر هستیم. اگر چهار سلول متوالی عمودی به دست آوریم، یک برد محسوب می شود
1 | function checkForWin() { |
2 | for (let r = 0; r < rows; r++) { |
3 | for (let c = 0; c <= cols - 4; c++) { |
4 | const player = gameBoard(r)(c); |
5 | if (player) { |
6 | if ( |
7 | player === gameBoard(r)(c + 1) && |
8 | player === gameBoard(r)(c + 2) && |
9 | player === gameBoard(r)(c + 3) |
10 | ) { |
11 | // console.log("Win horizontally");
|
12 | return true; |
13 | }
|
14 | }
|
15 | }
|
16 | }
|
17 | |
18 | for (let c = 0; c < cols; c++) { |
19 | for (let r = 0; r <= rows - 4; r++) { |
20 | const player = gameBoard(r)(c); |
21 | if (player) { |
22 | if ( |
23 | player === gameBoard(r + 1)(c) && |
24 | player === gameBoard(r + 2)(c) && |
25 | player === gameBoard(r + 3)(c) |
26 | ) { |
27 | // console.log("Win vertically");
|
28 | return true; |
29 | }
|
30 | }
|
31 | }
|
32 | }
|
33 | |
34 | return false; |
35 | } |
برد مورب
برای مورب برنده شوید، ما هر دو جهت پایین-چپ به بالا-راست و بالا-چپ-پایین-راست را بررسی می کنیم. ما این کار را با حلقه زدن روی 3 سطر و 3 ستون در یک زمان انجام خواهیم داد. به عنوان مثال، فرض کنید ما یک برد داریم که به شکل زیر است:
1 | const gameBoard = ( |
2 | (null, null, null, null, null, null, null), // row 0 |
3 | (null, null, null, null, null, null, null), // row 1 |
4 | (null, null, null, "red", null, null, null), // row 2 |
5 | (null, null, "red", null, null, null, null), // row 3 |
6 | (null, "red", null, null, null, null, null), // row 4 |
7 | ("red", null, null, null, null, null, null), // row 5 |
8 | |
9 | );
|
اگر شروع کنیم در col =0
، (0)(0) را در برابر سلول های زیر بررسی می کنیم.
1 | (4)(1) , (3)(2) and (2)(3) |
در هر مرحله، یک ردیف به سمت بالا (-) و یک ستون به سمت راست (+) حرکت می کنیم. را به روز کنید checkWin()
عملکرد به شرح زیر است.
1 | function checkForWin() { |
2 | |
3 | for (let r = 0; r < rows; r++) { |
4 | for (let c = 0; c <= cols - 4; c++) { |
5 | const player = gameBoard(r)(c); |
6 | if (player) { |
7 | if ( |
8 | player === gameBoard(r)(c + 1) && |
9 | player === gameBoard(r)(c + 2) && |
10 | player === gameBoard(r)(c + 3) |
11 | ) { |
12 | // console.log("Win horizontally");
|
13 | return true; |
14 | }
|
15 | }
|
16 | }
|
17 | }
|
18 | |
19 | |
20 | for (let c = 0; c < cols; c++) { |
21 | for (let r = 0; r <= rows - 4; r++) { |
22 | const player = gameBoard(r)(c); |
23 | if (player) { |
24 | if ( |
25 | player === gameBoard(r + 1)(c) && |
26 | player === gameBoard(r + 2)(c) && |
27 | player === gameBoard(r + 3)(c) |
28 | ) { |
29 | // console.log("Win vertically");
|
30 | return true; |
31 | }
|
32 | }
|
33 | }
|
34 | }
|
35 | |
36 | for (let r = 3; r < rows; r++) { |
37 | for (let c = 0; c <= cols - 4; c++) { |
38 | const player = gameBoard(r)(c); |
39 | if (player) { |
40 | |
41 | if ( |
42 | player === gameBoard(r - 1)(c + 1) && |
43 | player === gameBoard(r - 2)(c + 2) && |
44 | player === gameBoard(r - 3)(c + 3) |
45 | ) { |
46 | |
47 | return true; |
48 | }
|
49 | }
|
50 | }
|
51 | }
|
52 | |
53 | return false; |
54 | }
|
55 |
یک سناریوی برد دیگر را در نظر بگیرید که به این صورت است:
1 | const gameBoard = ( |
2 | ("red", null, null, null, null, null, null), // row 0 |
3 | (null, "red", null, null, null, null, null), // row 1 |
4 | (null, null, "red", null, null, null, null), // row 2 |
5 | (null, null, null, "red", null, null, null), // row 3 |
6 | (null, null, null, null, null, null, null), // row 4 |
7 | (null, null, null, null, null, null, null), // row 5 |
8 | );
|
در این حالت هر بار روی 3 سطر و 3 ستون حلقه می زنیم و برای هر مرحله یک سطر به سمت پایین (+) و یک ستون به سمت راست (+) حرکت می کنیم. تابع checkWin نهایی به این صورت خواهد بود
1 | function checkForWin() { |
2 | |
3 | for (let r = 0; r < rows; r++) { |
4 | for (let c = 0; c <= cols - 4; c++) { |
5 | const player = gameBoard(r)(c); |
6 | if (player) { |
7 | if ( |
8 | player === gameBoard(r)(c + 1) && |
9 | player === gameBoard(r)(c + 2) && |
10 | player === gameBoard(r)(c + 3) |
11 | ) { |
12 | // console.log("Win horizontally");
|
13 | return true; |
14 | }
|
15 | }
|
16 | }
|
17 | }
|
18 | |
19 | |
20 | for (let c = 0; c < cols; c++) { |
21 | for (let r = 0; r <= rows - 4; r++) { |
22 | const player = gameBoard(r)(c); |
23 | if (player) { |
24 | if ( |
25 | player === gameBoard(r + 1)(c) && |
26 | player === gameBoard(r + 2)(c) && |
27 | player === gameBoard(r + 3)(c) |
28 | ) { |
29 | // console.log("Win vertically");
|
30 | return true; |
31 | }
|
32 | }
|
33 | }
|
34 | }
|
35 | |
36 | for (let r = 0; r <= rows - 4; r++) { |
37 | for (let c = 0; c <= cols - 4; c++) { |
38 | const player = gameBoard(r)(c); |
39 | if (player) { |
40 | |
41 | if ( |
42 | player === gameBoard(r + 1)(c + 1) && |
43 | player === gameBoard(r + 2)(c + 2) && |
44 | player === gameBoard(r + 3)(c + 3) |
45 | ) { |
46 | console.log("Win vertically"); |
47 | |
48 | return true; |
49 | |
50 | }
|
51 | }
|
52 | }
|
53 | }
|
54 | |
55 | |
56 | for (let r = 3; r < rows; r++) { |
57 | for (let c = 0; c <= cols - 4; c++) { |
58 | const player = gameBoard(r)(c); |
59 | if (player) { |
60 | |
61 | if ( |
62 | player === gameBoard(r - 1)(c + 1) && |
63 | player === gameBoard(r - 2)(c + 2) && |
64 | player === gameBoard(r - 3)(c + 3) |
65 | ) { |
66 | |
67 | return true; |
68 | }
|
69 | }
|
70 | }
|
71 | }
|
72 | |
73 | |
74 | return false; |
75 | } |
رویدادهای تخته را به روز کنید تا مطمئن شوید که وقتی یک برد شناسایی شد، برنده را نشان می دهد و بازی را به پایان می رساند.
1 | board.addEventListener("click", function (e) { |
2 | status.classList.remove(`${currentPlayer}-selected`); |
3 | let col = parseInt(e.target.getAttribute("data-col")); |
4 | |
5 | if (e.target.classList.contains("cell") && !isGameOver) { |
6 | for (let row = rows - 1; row >= 0; row--) { |
7 | let clickedCell = board.querySelector( |
8 | `(data-row="${row}")(data-col="${col}")` |
9 | );
|
10 | if ( |
11 | !clickedCell.classList.contains("red") && |
12 | !clickedCell.classList.contains("yellow") |
13 | ) { |
14 | clickedCell.classList.add(currentPlayer); |
15 | gameBoard(row)(col) = currentPlayer; |
16 | |
17 | |
18 | if (checkForWin()) { |
19 | status.style.display = "block"; |
20 | console.log(currentPlayer.toUpperCase()); |
21 | status.style.display = "none"; |
22 | |
23 | winner.innerText = `${currentPlayer.toUpperCase()} wins!`; |
24 | isGameOver = true; |
25 | return; |
26 | }
|
27 | |
28 | |
29 | currentPlayer = currentPlayer === "red" ? "yellow" : "red"; |
30 | |
31 | status.classList.add(`${currentPlayer}-selected`); |
32 | break; |
33 | }
|
34 | }
|
35 | }
|
36 | }); |
آخرین مرحله این است که در صورت پیروزی، عملکرد بازی را بازنشانی کنید.
1 | resetBtn.addEventListener("click", function () { |
2 | isGameOver = false; |
3 | board.querySelectorAll(".cell").forEach((cell) => { |
4 | cell.classList.remove("red", "yellow"); |
5 | });
|
6 | currentPlayer = "red"; |
7 | winner.innerText = ""; |
8 | |
9 | status.classList.add(`${currentPlayer}-selected`); |
10 | }); |
هنگامی که دکمه استراحت کلیک می شود، بازی با حذف تمام رنگ های اختصاص داده شده به تمام سلول ها و حذف هر گونه پیام از بازی قبلی، بازنشانی می شود.
برای یادآوری، در اینجا نسخه ی نمایشی نهایی است!
نتیجه گیری
فقط با HTML، CSS و جاوا اسکریپت، ما یک بازی Connect Four کاملاً کاربردی را از ابتدا ساخته ایم. امیدوارم از دنبال کردن لذت برده باشید
برای چالش برانگیزتر کردن بازی، می توانید با اضافه کردن یک تایمر آن را بهبود بخشید تا به هر حرکت کمی فشار بیاورید!
منبع: https://webdesign.tutsplus.com/connect-four-game-in-html-css-and-vanilla-javascript--cms-109043t