├── 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 | ![checkers game](Images/checkersgame.png "Checkers") 14 | 15 | ![win](Images/checkerswin.png "Checkers win") 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 | 96 | 97 | 98 | 99 | 100 |

95 |
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(); --------------------------------------------------------------------------------