├── Images
├── checkersgame.png
└── checkerswin.png
├── README.md
├── style.css
├── index.html
└── script.js
/Images/checkersgame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RyanBranco/Checkers/HEAD/Images/checkersgame.png
--------------------------------------------------------------------------------
/Images/checkerswin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RyanBranco/Checkers/HEAD/Images/checkerswin.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Checkers
2 | Checkers browser game for co-op player vs. player. This project is perfect to replicate if you want to better understand the basics of JS and how it talks to HTML (the DOM).
3 |
4 | * Written using vanilla:
5 | * HTML
6 | * CSS
7 | * JavaScript
8 |
9 | [Click here if you would like to play the game](https://ryanbranco.github.io/Checkers/)
10 |
11 | Screenshots:
12 |
13 | 
14 |
15 | 
16 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: auto;
3 | }
4 |
5 | main {
6 | display: flex;
7 | flex-direction: column;
8 | justify-content: space-between;
9 | align-items: center;
10 | height: 100vh;
11 | }
12 |
13 | div {
14 | display: flex;
15 | justify-content: space-between;
16 | }
17 |
18 | .mobile {
19 | display: none;
20 | }
21 |
22 | .desktop {
23 | margin: 0;
24 | width: 100%;
25 | }
26 |
27 | .noPieceHere {
28 | background-color: #F0D2B4;
29 | }
30 |
31 | td {
32 | text-align: center;
33 | background-color: #BA7A3A;
34 | width: 65px;
35 | height: 65px;
36 | }
37 |
38 | .red-turn-text {
39 | text-align: center;
40 | color: black;
41 | font-family: "open sans";
42 | font-size: 55px;
43 | }
44 |
45 | .black-turn-text {
46 | text-align: center;
47 | font-family: "open sans";
48 | font-size: 55px;
49 | color: lightgray;
50 | }
51 |
52 | .black-piece {
53 | width: 20px;
54 | height: 20px;
55 | padding: 12px;
56 | background-color: black;
57 | border: 1px solid white;
58 | border-radius: 50px;
59 | }
60 |
61 | .red-piece {
62 | width: 20px;
63 | height: 20px;
64 | padding: 12px;
65 | background-color: red;
66 | border: 1px solid white;
67 | border-radius: 50px;
68 | }
69 |
70 | .king.red-piece {
71 | background-color: orange;
72 | }
73 |
74 | .king.black-piece {
75 | background-color: purple;
76 | }
77 |
78 | p {
79 | display: inline-block;
80 | }
81 |
82 | #divider {
83 | font-size: 90px;
84 | margin-left: 15px;
85 | margin-right: 15px;
86 | }
87 |
88 | span {
89 | display: inline-block;
90 | }
91 |
92 | @media screen and (max-width: 560px) {
93 | .red-piece {
94 | height: 4.5vw;
95 | width: 4.5vw;
96 | }
97 |
98 | .black-piece {
99 | height: 4.5vw;
100 | width: 4.5vw;
101 | }
102 |
103 | td {
104 | width: 12vw;
105 | height: 12vw;
106 | }
107 |
108 | .red-turn-text {
109 | font-size: 40px;
110 | }
111 |
112 | .black-turn-text {
113 | font-size: 40px;
114 | }
115 |
116 | table {
117 | margin: 0;
118 | }
119 |
120 | .mobile {
121 | display: block;
122 | }
123 |
124 | .desktop {
125 | display: none;
126 | }
127 |
128 | .red-turn-text {
129 | -webkit-transform:rotate(-180deg);
130 | -moz-transform:rotate(-180deg);
131 | -o-transform:rotate(-180deg);
132 | transform:rotate(-180deg);
133 | filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
134 | }
135 | }
136 |
137 | @media screen and (max-width: 380px) {
138 | .red-piece {
139 | height: 3vw;
140 | width: 3vw;
141 | }
142 |
143 | .black-piece {
144 | height: 3vw;
145 | width: 3vw;
146 | }
147 | }
148 |
149 | @media screen and (max-width: 345px) {
150 | .red-piece {
151 | height: 2.5vw;
152 | width: 2.5vw;
153 | }
154 |
155 | .black-piece {
156 | height: 2.5vw;
157 | width: 2.5vw;
158 | }
159 | }
160 |
161 | @media screen and (max-width: 300px) {
162 | .red-piece {
163 | height: 1.5vw;
164 | width: 1.5vw;
165 | }
166 |
167 | .black-piece {
168 | height: 1.5vw;
169 | width: 1.5vw;
170 | }
171 |
172 | .red-turn-text {
173 | font-size: 35px;
174 | }
175 |
176 | .black-turn-text {
177 | font-size: 35px;
178 | }
179 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Checkers
13 |
14 |
15 |
16 |
17 | Reds turn
18 |
19 |
20 |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 |
30 |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 |
40 |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 |
50 |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 |
60 |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | |
69 |
70 |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 |
80 |
81 | |
82 | |
83 | |
84 | |
85 | |
86 | |
87 | |
88 | |
89 |
90 |
91 | |
92 | |
93 | |
94 |
95 | | |
96 | |
97 | |
98 | |
99 |
100 |
101 |
102 |
103 | Reds turn
104 |
105 |
|
106 |
107 | Blacks turn
108 |
109 |
110 |
111 | Blacks turn
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | /*----------- Game State Data ----------*/
2 |
3 | const board = [
4 | null, 0, null, 1, null, 2, null, 3,
5 | 4, null, 5, null, 6, null, 7, null,
6 | null, 8, null, 9, null, 10, null, 11,
7 | null, null, null, null, null, null, null, null,
8 | null, null, null, null, null, null, null, null,
9 | 12, null, 13, null, 14, null, 15, null,
10 | null, 16, null, 17, null, 18, null, 19,
11 | 20, null, 21, null, 22, null, 23, null
12 | ]
13 |
14 | /*---------- Cached Variables ----------*/
15 |
16 | // parses pieceId's and returns the index of that piece's place on the board
17 | let findPiece = function (pieceId) {
18 | let parsed = parseInt(pieceId);
19 | return board.indexOf(parsed);
20 | };
21 |
22 | // DOM referenes
23 | const cells = document.querySelectorAll("td");
24 | let redsPieces = document.querySelectorAll("p");
25 | let blacksPieces = document.querySelectorAll("span")
26 | const redTurnText = document.querySelectorAll(".red-turn-text");
27 | const blackTurntext = document.querySelectorAll(".black-turn-text");
28 | const divider = document.querySelector("#divider")
29 |
30 | // player properties
31 | let turn = true;
32 | let redScore = 12;
33 | let blackScore = 12;
34 | let playerPieces;
35 |
36 | // selected piece properties
37 | let selectedPiece = {
38 | pieceId: -1,
39 | indexOfBoardPiece: -1,
40 | isKing: false,
41 | seventhSpace: false,
42 | ninthSpace: false,
43 | fourteenthSpace: false,
44 | eighteenthSpace: false,
45 | minusSeventhSpace: false,
46 | minusNinthSpace: false,
47 | minusFourteenthSpace: false,
48 | minusEighteenthSpace: false
49 | }
50 |
51 | /*---------- Event Listeners ----------*/
52 |
53 | // initialize event listeners on pieces
54 | function givePiecesEventListeners() {
55 | if (turn) {
56 | for (let i = 0; i < redsPieces.length; i++) {
57 | redsPieces[i].addEventListener("click", getPlayerPieces);
58 | }
59 | } else {
60 | for (let i = 0; i < blacksPieces.length; i++) {
61 | blacksPieces[i].addEventListener("click", getPlayerPieces);
62 | }
63 | }
64 | }
65 |
66 | /*---------- Logic ----------*/
67 |
68 | // holds the length of the players piece count
69 | function getPlayerPieces() {
70 | if (turn) {
71 | playerPieces = redsPieces;
72 | } else {
73 | playerPieces = blacksPieces;
74 | }
75 | removeCellonclick();
76 | resetBorders();
77 | }
78 |
79 | // removes possible moves from old selected piece (* this is needed because the user might re-select a piece *)
80 | function removeCellonclick() {
81 | for (let i = 0; i < cells.length; i++) {
82 | cells[i].removeAttribute("onclick");
83 | }
84 | }
85 |
86 | // resets borders to default
87 | function resetBorders() {
88 | for (let i = 0; i < playerPieces.length; i++) {
89 | playerPieces[i].style.border = "1px solid white";
90 | }
91 | resetSelectedPieceProperties();
92 | getSelectedPiece();
93 | }
94 |
95 | // resets selected piece properties
96 | function resetSelectedPieceProperties() {
97 | selectedPiece.pieceId = -1;
98 | selectedPiece.pieceId = -1;
99 | selectedPiece.isKing = false;
100 | selectedPiece.seventhSpace = false;
101 | selectedPiece.ninthSpace = false;
102 | selectedPiece.fourteenthSpace = false;
103 | selectedPiece.eighteenthSpace = false;
104 | selectedPiece.minusSeventhSpace = false;
105 | selectedPiece.minusNinthSpace = false;
106 | selectedPiece.minusFourteenthSpace = false;
107 | selectedPiece.minusEighteenthSpace = false;
108 | }
109 |
110 | // gets ID and index of the board cell its on
111 | function getSelectedPiece() {
112 | selectedPiece.pieceId = parseInt(event.target.id);
113 | selectedPiece.indexOfBoardPiece = findPiece(selectedPiece.pieceId);
114 | isPieceKing();
115 | }
116 |
117 | // checks if selected piece is a king
118 | function isPieceKing() {
119 | if (document.getElementById(selectedPiece.pieceId).classList.contains("king")) {
120 | selectedPiece.isKing = true;
121 | } else {
122 | selectedPiece.isKing = false;
123 | }
124 | getAvailableSpaces();
125 | }
126 |
127 | // gets the moves that the selected piece can make
128 | function getAvailableSpaces() {
129 | if (board[selectedPiece.indexOfBoardPiece + 7] === null &&
130 | cells[selectedPiece.indexOfBoardPiece + 7].classList.contains("noPieceHere") !== true) {
131 | selectedPiece.seventhSpace = true;
132 | }
133 | if (board[selectedPiece.indexOfBoardPiece + 9] === null &&
134 | cells[selectedPiece.indexOfBoardPiece + 9].classList.contains("noPieceHere") !== true) {
135 | selectedPiece.ninthSpace = true;
136 | }
137 | if (board[selectedPiece.indexOfBoardPiece - 7] === null &&
138 | cells[selectedPiece.indexOfBoardPiece - 7].classList.contains("noPieceHere") !== true) {
139 | selectedPiece.minusSeventhSpace = true;
140 | }
141 | if (board[selectedPiece.indexOfBoardPiece - 9] === null &&
142 | cells[selectedPiece.indexOfBoardPiece - 9].classList.contains("noPieceHere") !== true) {
143 | selectedPiece.minusNinthSpace = true;
144 | }
145 | checkAvailableJumpSpaces();
146 | }
147 |
148 | // gets the moves that the selected piece can jump
149 | function checkAvailableJumpSpaces() {
150 | if (turn) {
151 | if (board[selectedPiece.indexOfBoardPiece + 14] === null
152 | && cells[selectedPiece.indexOfBoardPiece + 14].classList.contains("noPieceHere") !== true
153 | && board[selectedPiece.indexOfBoardPiece + 7] >= 12) {
154 | selectedPiece.fourteenthSpace = true;
155 | }
156 | if (board[selectedPiece.indexOfBoardPiece + 18] === null
157 | && cells[selectedPiece.indexOfBoardPiece + 18].classList.contains("noPieceHere") !== true
158 | && board[selectedPiece.indexOfBoardPiece + 9] >= 12) {
159 | selectedPiece.eighteenthSpace = true;
160 | }
161 | if (board[selectedPiece.indexOfBoardPiece - 14] === null
162 | && cells[selectedPiece.indexOfBoardPiece - 14].classList.contains("noPieceHere") !== true
163 | && board[selectedPiece.indexOfBoardPiece - 7] >= 12) {
164 | selectedPiece.minusFourteenthSpace = true;
165 | }
166 | if (board[selectedPiece.indexOfBoardPiece - 18] === null
167 | && cells[selectedPiece.indexOfBoardPiece - 18].classList.contains("noPieceHere") !== true
168 | && board[selectedPiece.indexOfBoardPiece - 9] >= 12) {
169 | selectedPiece.minusEighteenthSpace = true;
170 | }
171 | } else {
172 | if (board[selectedPiece.indexOfBoardPiece + 14] === null
173 | && cells[selectedPiece.indexOfBoardPiece + 14].classList.contains("noPieceHere") !== true
174 | && board[selectedPiece.indexOfBoardPiece + 7] < 12 && board[selectedPiece.indexOfBoardPiece + 7] !== null) {
175 | selectedPiece.fourteenthSpace = true;
176 | }
177 | if (board[selectedPiece.indexOfBoardPiece + 18] === null
178 | && cells[selectedPiece.indexOfBoardPiece + 18].classList.contains("noPieceHere") !== true
179 | && board[selectedPiece.indexOfBoardPiece + 9] < 12 && board[selectedPiece.indexOfBoardPiece + 9] !== null) {
180 | selectedPiece.eighteenthSpace = true;
181 | }
182 | if (board[selectedPiece.indexOfBoardPiece - 14] === null && cells[selectedPiece.indexOfBoardPiece - 14].classList.contains("noPieceHere") !== true
183 | && board[selectedPiece.indexOfBoardPiece - 7] < 12
184 | && board[selectedPiece.indexOfBoardPiece - 7] !== null) {
185 | selectedPiece.minusFourteenthSpace = true;
186 | }
187 | if (board[selectedPiece.indexOfBoardPiece - 18] === null && cells[selectedPiece.indexOfBoardPiece - 18].classList.contains("noPieceHere") !== true
188 | && board[selectedPiece.indexOfBoardPiece - 9] < 12
189 | && board[selectedPiece.indexOfBoardPiece - 9] !== null) {
190 | selectedPiece.minusEighteenthSpace = true;
191 | }
192 | }
193 | checkPieceConditions();
194 | }
195 |
196 | // restricts movement if the piece is a king
197 | function checkPieceConditions() {
198 | if (selectedPiece.isKing) {
199 | givePieceBorder();
200 | } else {
201 | if (turn) {
202 | selectedPiece.minusSeventhSpace = false;
203 | selectedPiece.minusNinthSpace = false;
204 | selectedPiece.minusFourteenthSpace = false;
205 | selectedPiece.minusEighteenthSpace = false;
206 | } else {
207 | selectedPiece.seventhSpace = false;
208 | selectedPiece.ninthSpace = false;
209 | selectedPiece.fourteenthSpace = false;
210 | selectedPiece.eighteenthSpace = false;
211 | }
212 | givePieceBorder();
213 | }
214 | }
215 |
216 | // gives the piece a green highlight for the user (showing its movable)
217 | function givePieceBorder() {
218 | if (selectedPiece.seventhSpace || selectedPiece.ninthSpace || selectedPiece.fourteenthSpace || selectedPiece.eighteenthSpace
219 | || selectedPiece.minusSeventhSpace || selectedPiece.minusNinthSpace || selectedPiece.minusFourteenthSpace || selectedPiece.minusEighteenthSpace) {
220 | document.getElementById(selectedPiece.pieceId).style.border = "3px solid green";
221 | giveCellsClick();
222 | } else {
223 | return;
224 | }
225 | }
226 |
227 | // gives the cells on the board a 'click' bassed on the possible moves
228 | function giveCellsClick() {
229 | if (selectedPiece.seventhSpace) {
230 | cells[selectedPiece.indexOfBoardPiece + 7].setAttribute("onclick", "makeMove(7)");
231 | }
232 | if (selectedPiece.ninthSpace) {
233 | cells[selectedPiece.indexOfBoardPiece + 9].setAttribute("onclick", "makeMove(9)");
234 | }
235 | if (selectedPiece.fourteenthSpace) {
236 | cells[selectedPiece.indexOfBoardPiece + 14].setAttribute("onclick", "makeMove(14)");
237 | }
238 | if (selectedPiece.eighteenthSpace) {
239 | cells[selectedPiece.indexOfBoardPiece + 18].setAttribute("onclick", "makeMove(18)");
240 | }
241 | if (selectedPiece.minusSeventhSpace) {
242 | cells[selectedPiece.indexOfBoardPiece - 7].setAttribute("onclick", "makeMove(-7)");
243 | }
244 | if (selectedPiece.minusNinthSpace) {
245 | cells[selectedPiece.indexOfBoardPiece - 9].setAttribute("onclick", "makeMove(-9)");
246 | }
247 | if (selectedPiece.minusFourteenthSpace) {
248 | cells[selectedPiece.indexOfBoardPiece - 14].setAttribute("onclick", "makeMove(-14)");
249 | }
250 | if (selectedPiece.minusEighteenthSpace) {
251 | cells[selectedPiece.indexOfBoardPiece - 18].setAttribute("onclick", "makeMove(-18)");
252 | }
253 | }
254 |
255 | /* v when the cell is clicked v */
256 |
257 | // makes the move that was clicked
258 | function makeMove(number) {
259 | document.getElementById(selectedPiece.pieceId).remove();
260 | cells[selectedPiece.indexOfBoardPiece].innerHTML = "";
261 | if (turn) {
262 | if (selectedPiece.isKing) {
263 | cells[selectedPiece.indexOfBoardPiece + number].innerHTML = ``;
264 | redsPieces = document.querySelectorAll("p");
265 | } else {
266 | cells[selectedPiece.indexOfBoardPiece + number].innerHTML = ``;
267 | redsPieces = document.querySelectorAll("p");
268 | }
269 | } else {
270 | if (selectedPiece.isKing) {
271 | cells[selectedPiece.indexOfBoardPiece + number].innerHTML = ``;
272 | blacksPieces = document.querySelectorAll("span");
273 | } else {
274 | cells[selectedPiece.indexOfBoardPiece + number].innerHTML = ``;
275 | blacksPieces = document.querySelectorAll("span");
276 | }
277 | }
278 |
279 | let indexOfPiece = selectedPiece.indexOfBoardPiece
280 | if (number === 14 || number === -14 || number === 18 || number === -18) {
281 | changeData(indexOfPiece, indexOfPiece + number, indexOfPiece + number / 2);
282 | } else {
283 | changeData(indexOfPiece, indexOfPiece + number);
284 | }
285 | }
286 |
287 | // Changes the board states data on the back end
288 | function changeData(indexOfBoardPiece, modifiedIndex, removePiece) {
289 | board[indexOfBoardPiece] = null;
290 | board[modifiedIndex] = parseInt(selectedPiece.pieceId);
291 | if (turn && selectedPiece.pieceId < 12 && modifiedIndex >= 57) {
292 | document.getElementById(selectedPiece.pieceId).classList.add("king")
293 | }
294 | if (turn === false && selectedPiece.pieceId >= 12 && modifiedIndex <= 7) {
295 | document.getElementById(selectedPiece.pieceId).classList.add("king");
296 | }
297 | if (removePiece) {
298 | board[removePiece] = null;
299 | if (turn && selectedPiece.pieceId < 12) {
300 | cells[removePiece].innerHTML = "";
301 | blackScore--
302 | }
303 | if (turn === false && selectedPiece.pieceId >= 12) {
304 | cells[removePiece].innerHTML = "";
305 | redScore--
306 | }
307 | }
308 | resetSelectedPieceProperties();
309 | removeCellonclick();
310 | removeEventListeners();
311 | }
312 |
313 | // removes the 'onClick' event listeners for pieces
314 | function removeEventListeners() {
315 | if (turn) {
316 | for (let i = 0; i < redsPieces.length; i++) {
317 | redsPieces[i].removeEventListener("click", getPlayerPieces);
318 | }
319 | } else {
320 | for (let i = 0; i < blacksPieces.length; i++) {
321 | blacksPieces[i].removeEventListener("click", getPlayerPieces);
322 | }
323 | }
324 | checkForWin();
325 | }
326 |
327 | // Checks for a win
328 | function checkForWin() {
329 | if (blackScore === 0) {
330 | divider.style.display = "none";
331 | for (let i = 0; i < redTurnText.length; i++) {
332 | redTurnText[i].style.color = "black";
333 | blackTurntext[i].style.display = "none";
334 | redTurnText[i].textContent = "RED WINS!";
335 | }
336 | } else if (redScore === 0) {
337 | divider.style.display = "none";
338 | for (let i = 0; i < blackTurntext.length; i++) {
339 | blackTurntext[i].style.color = "black";
340 | redTurnText[i].style.display = "none";
341 | blackTurntext[i].textContent = "BLACK WINS!";
342 | }
343 | }
344 | changePlayer();
345 | }
346 |
347 | // Switches players turn
348 | function changePlayer() {
349 | if (turn) {
350 | turn = false;
351 | for (let i = 0; i < redTurnText.length; i++) {
352 | redTurnText[i].style.color = "lightGrey";
353 | blackTurntext[i].style.color = "black";
354 | }
355 | } else {
356 | turn = true;
357 | for (let i = 0; i < blackTurntext.length; i++) {
358 | blackTurntext[i].style.color = "lightGrey";
359 | redTurnText[i].style.color = "black";
360 | }
361 | }
362 | givePiecesEventListeners();
363 | }
364 |
365 | givePiecesEventListeners();
--------------------------------------------------------------------------------