├── assets ├── media │ └── pieces │ │ ├── bb.png │ │ ├── bk.png │ │ ├── bn.png │ │ ├── bp.png │ │ ├── bq.png │ │ ├── br.png │ │ ├── wb.png │ │ ├── wk.png │ │ ├── wn.png │ │ ├── wp.png │ │ ├── wq.png │ │ ├── wr.png │ │ └── chessgame.PNG ├── javascript │ ├── json │ │ ├── matchhistory.json │ │ ├── black-pieces.json │ │ └── white-pieces.json │ └── javascript │ │ └── Script.js └── css │ └── Style.css ├── README.md └── index.html /assets/media/pieces/bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/bb.png -------------------------------------------------------------------------------- /assets/media/pieces/bk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/bk.png -------------------------------------------------------------------------------- /assets/media/pieces/bn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/bn.png -------------------------------------------------------------------------------- /assets/media/pieces/bp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/bp.png -------------------------------------------------------------------------------- /assets/media/pieces/bq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/bq.png -------------------------------------------------------------------------------- /assets/media/pieces/br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/br.png -------------------------------------------------------------------------------- /assets/media/pieces/wb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/wb.png -------------------------------------------------------------------------------- /assets/media/pieces/wk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/wk.png -------------------------------------------------------------------------------- /assets/media/pieces/wn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/wn.png -------------------------------------------------------------------------------- /assets/media/pieces/wp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/wp.png -------------------------------------------------------------------------------- /assets/media/pieces/wq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/wq.png -------------------------------------------------------------------------------- /assets/media/pieces/wr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/wr.png -------------------------------------------------------------------------------- /assets/media/pieces/chessgame.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustTheNorm/Chess/HEAD/assets/media/pieces/chessgame.PNG -------------------------------------------------------------------------------- /assets/javascript/json/matchhistory.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "piece": "wp4", "from": "e2", "to": "e4" }, 3 | { "piece": "bp4", "from": "e7", "to": "e5" }, 4 | { "piece": "wn1", "from": "g1", "to": "f3" }, 5 | { "piece": "bn0", "from": "b8", "to": "c6" }, 6 | { "piece": "wb1", "from": "f1", "to": "b5" }, 7 | { "piece": "bn1", "from": "g8", "to": "f6" }, 8 | { "piece": "wn0", "from": "b1", "to": "c3" }, 9 | { "piece": "bp3", "from": "d7", "to": "d5" }, 10 | { "piece": "wk0", "from": "e1", "to": "g1" } 11 | ] 12 | -------------------------------------------------------------------------------- /assets/javascript/json/black-pieces.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Pawn", 4 | "length": 8, 5 | "alias": "bp", 6 | "position": { 7 | "letter": ["a", "b", "c", "d", "e", "f", "g", "h"], 8 | "number": 7 9 | } 10 | }, 11 | { 12 | "name": "Knight", 13 | "length": 2, 14 | "alias": "bn", 15 | "position": { "letter": ["b", "g"], "number": 8 } 16 | }, 17 | { 18 | "name": "Bishop", 19 | "length": 2, 20 | "alias": "bb", 21 | "position": { "letter": ["c", "f"], "number": 8 } 22 | }, 23 | { 24 | "name": "Rook", 25 | "length": 2, 26 | "alias": "br", 27 | "position": { "letter": ["a", "h"], "number": 8 } 28 | }, 29 | { 30 | "name": "Queen", 31 | "length": 1, 32 | "alias": "bq", 33 | "position": { "letter": ["d"], "number": 8 } 34 | }, 35 | { 36 | "name": "King", 37 | "length": 1, 38 | "alias": "bk", 39 | "position": { "letter": ["e"], "number": 8 } 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /assets/javascript/json/white-pieces.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Pawn", 4 | "length": 8, 5 | "alias": "wp", 6 | "position": { 7 | "letter": ["a", "b", "c", "d", "e", "f", "g", "h"], 8 | "number": 2 9 | } 10 | }, 11 | { 12 | "name": "Knight", 13 | "length": 2, 14 | "alias": "wn", 15 | "position": { "letter": ["b", "g"], "number": 1 } 16 | }, 17 | { 18 | "name": "Bishop", 19 | "length": 2, 20 | "alias": "wb", 21 | "position": { "letter": ["c", "f"], "number": 1 } 22 | }, 23 | { 24 | "name": "Rook", 25 | "length": 2, 26 | "alias": "wr", 27 | "position": { "letter": ["a", "h"], "number": 1 } 28 | }, 29 | { 30 | "name": "Queen", 31 | "length": 1, 32 | "alias": "wq", 33 | "position": { "letter": ["d"], "number": 1 } 34 | }, 35 | { 36 | "name": "King", 37 | "length": 1, 38 | "alias": "wk", 39 | "position": { "letter": ["e"], "number": 1 } 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chess 2 | 1. The aim of chess is to checkmate your opponent’s king. 3 | 2. Familiarize yourself with the basics by going over how each piece moves. 4 | 3. White always goes first. 5 | 4. Game is over when king is captured by enemy player or if your time runs out. 6 | 7 | --- 8 | ## Description 9 | simple chess game using HTML, CSS, Javascript. 10 | 11 | -not finished still missing features 12 | 13 | --- 14 | 15 | ## Images 16 | ![Ui](./assets/media/pieces/chessgame.PNG) 17 | 18 | --- 19 | 20 | ## Tech Stack 21 | 22 |
23 | CSS  24 | HTML  25 | JavaScript  26 |
27 | 28 | --- 29 | 30 | ## Upcoming Features 31 | 32 | - allow users to pick a username 33 | - fix castling on Queen side of board 34 | - add ability for En pessant for pawns 35 | - add draw feature to allow for draws in close games or the ability to offer a draw to the opposing player. 36 | - ability to invite a friend using a unique url for multiplayer match. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Simple Chess Game 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 |

Player 1

18 |
19 | 20 |
21 |
22 |
23 |
24 |
25 | 00:00 26 |
27 |
28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 | 00:00 36 |
37 |
38 |
39 |
40 |
41 |

Player 1

42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /assets/css/Style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body{ 8 | font-family: Arial, Helvetica, sans-serif; 9 | width: 100vw; 10 | height: 100vh; 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | background: #312E2B; 15 | } 16 | 17 | 18 | .chessboard{ 19 | width: 500px; 20 | height: 500px; 21 | display: grid; 22 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; 23 | box-shadow: 10px 0px 10px -10px rgba(255, 255, 255, 0.4); 24 | border-radius: 25px; 25 | } 26 | 27 | .chessboard > .chessboard-square{ 28 | width: calc(500px / 8); 29 | height: calc(500px / 8); 30 | display: flex; 31 | justify-content: center; 32 | align-items: center; 33 | } 34 | 35 | .chessboard > .chessboard-square[role="white"]{ 36 | background: #779556; 37 | } 38 | 39 | .chessboard > .chessboard-square[role="black"]{ 40 | background: #EBECD0; 41 | } 42 | 43 | .chessboard > .chessboard-square:nth-child(1){ 44 | border-top-left-radius: 25px; 45 | } 46 | 47 | .chessboard > .chessboard-square:nth-child(8){ 48 | border-top-right-radius: 25px; 49 | } 50 | 51 | .chessboard > .chessboard-square:nth-child(57){ 52 | border-bottom-left-radius: 25px; 53 | } 54 | 55 | .chessboard > .chessboard-square:nth-child(64){ 56 | border-bottom-right-radius: 25px; 57 | } 58 | 59 | .chessboard > .chessboard-square::before{ 60 | content: ""; 61 | width: 25px; 62 | height: 25px; 63 | border-radius: 50%; 64 | background: #BEC5A3; 65 | position: absolute; 66 | opacity: 0.50; 67 | visibility: hidden; 68 | } 69 | 70 | .chessboard > .chessboard-square.move::before{ 71 | visibility: visible; 72 | } 73 | 74 | .chessboard > .chessboard-square.enemy::before{ 75 | visibility: visible; 76 | } 77 | 78 | .chessboard > .chessboard-square.castling::before{ 79 | visibility: visible; 80 | } 81 | 82 | .chessboard > .chessboard-square.from{ 83 | background: #bd5c6c; 84 | } 85 | 86 | .chessboard > .chessboard-square.to{ 87 | background: #e27587; 88 | } 89 | 90 | .chessboard > .chessboard-square > img.chessboard-piece{ 91 | max-width: 50px; 92 | width: 50px; 93 | height: auto; 94 | cursor: grab; 95 | user-select: none; 96 | -webkit-user-select: none; 97 | -webkit-user-drag: none; 98 | } 99 | 100 | .chessboard > .chessboard-square > img.chessboard-piece:active{ 101 | cursor: grabbing; 102 | } 103 | 104 | .player-card{ 105 | padding: 2.5px 25px; 106 | display: flex; 107 | position: relative; 108 | } 109 | 110 | .player-card > .rows{ 111 | display: flex; 112 | align-items: center; 113 | width: 100%; 114 | } 115 | 116 | .player-card > .row-1{ 117 | cursor: pointer; 118 | border-radius: 6px; 119 | margin:2.5px; 120 | } 121 | 122 | .player-card > .row-1 > .icon { 123 | width:30px; 124 | height:30px; 125 | margin: 5px; 126 | border-radius: 50%; 127 | background: #ffffff; 128 | } 129 | 130 | .player-card > .row-1 > .text{ 131 | margin: 0px 5px; 132 | color: #ffffff; 133 | } 134 | 135 | .player-card > .row-1 > .text > .status span{ 136 | font-size: 12px; 137 | } 138 | 139 | .player-card.player-1 > .row-1{ 140 | justify-content: flex-end; 141 | } 142 | 143 | .player-card.player-2 > .row-2{ 144 | justify-content: flex-end; 145 | } 146 | 147 | .player-card.player-1 > .row-2 > .timer{ 148 | text-align: left; 149 | } 150 | 151 | .player-card.player-2 > .row-2 > .timer{ 152 | text-align: right 153 | } 154 | 155 | .player-card > .row-2 > .timer{ 156 | padding: 10px; 157 | background: #52504E; 158 | border-radius: 6px; 159 | margin: 5px 0px; 160 | width: 80px; 161 | text-align: right; 162 | cursor: pointer; 163 | } 164 | 165 | .player-card > .row-2 > .timer > span{ 166 | color: #ebeeee; 167 | font-weight: 600; 168 | font-size: 18px; 169 | user-select: none; 170 | } 171 | 172 | .player-card > .row-2 > .timer.timeout{ 173 | background: #9E4F4C; 174 | } 175 | 176 | .player-card > .row-2 > .timer > span{ 177 | color: #ffffff; 178 | font-weight: 600; 179 | } 180 | 181 | /* CSS */ 182 | .button-64 { 183 | align-items: center; 184 | background-image: linear-gradient(144deg,#AF40FF, #5B42F3 50%,#00DDEB); 185 | border: 0; 186 | border-radius: 8px; 187 | box-shadow: rgba(151, 65, 252, 0.2) 0 15px 30px -5px; 188 | box-sizing: border-box; 189 | color: #FFFFFF; 190 | display: flex; 191 | font-family: Phantomsans, sans-serif; 192 | font-size: 20px; 193 | justify-content: center; 194 | line-height: 1em; 195 | max-width: 100%; 196 | min-width: 140px; 197 | padding: 3px; 198 | text-decoration: none; 199 | user-select: none; 200 | -webkit-user-select: none; 201 | touch-action: manipulation; 202 | white-space: nowrap; 203 | cursor: pointer; 204 | margin: 70px; 205 | margin-right: 10px; 206 | } 207 | 208 | .button-64:active, 209 | .button-64:hover { 210 | outline: 0; 211 | } 212 | 213 | .button-64 span { 214 | background-color: rgb(5, 6, 45); 215 | padding: 5px 2px; 216 | border-radius: 6px; 217 | width: 100%; 218 | height: 100%; 219 | transition: 300ms; 220 | } 221 | 222 | .button-64:hover span { 223 | background: none; 224 | } 225 | 226 | @media (min-width: 768px) { 227 | .button-64 { 228 | font-size: 20px; 229 | min-width: 130px; 230 | } 231 | } -------------------------------------------------------------------------------- /assets/javascript/javascript/Script.js: -------------------------------------------------------------------------------- 1 | const chessboardParent = document.getElementById("chessboard"); 2 | 3 | 4 | // Chess Game 5 | class Chess { 6 | constructor() { 7 | this.setDefault(); 8 | } 9 | 10 | // set chess info as default 11 | setDefault() { 12 | this.info = { 13 | preview: false, // when previewing match history 14 | started: false, // when the started 15 | ended: false, // when the game is ended 16 | won: null, // Winning player 17 | turn: null, // Player turn 18 | timer: 10, // Five minutes Timer 19 | }; 20 | 21 | this.data = { 22 | players: [], // all the players 23 | matchHistory: [], // storing match history 24 | board: null, // board 25 | }; 26 | } 27 | 28 | // initialize game 29 | async init(callback) { 30 | // create new board 31 | this.data.board = new Board(this); 32 | // then create board elements 33 | this.data.board.create(); 34 | 35 | // assign players 36 | await this.assignPlayers(); 37 | 38 | // make sure that players is ready 39 | await this.data.players[0].init(this); 40 | await this.data.players[1].init(this); 41 | 42 | callback && callback.call(this); 43 | } 44 | 45 | // assign players (player1,player2) 46 | async assignPlayers() { 47 | // will return a promise 48 | return new Promise((resolve) => { 49 | const player1 = new Player({ username: "Player 1", id: 1, role: "white" }); // player 1 50 | const player2 = new Player({ username: "Player 2", id: 2, role: "black" }); // player 2 51 | 52 | this.data.players = [player1, player2]; // assign into the game players 53 | 54 | // player 1 is first to move 55 | this.info.turn = player1; 56 | player1.info.isTurn = true; 57 | 58 | resolve(); // return 59 | }); 60 | } 61 | 62 | // when the game start 63 | start() { 64 | this.info.started = true; 65 | this.info.ended = false; 66 | this.info.won = false; 67 | 68 | this.data.board.placePiecesAsDefault(); // and place player pieces as default pos 69 | this.data.players.forEach((p) => p.startTimer()); // start the player 70 | } 71 | 72 | notify() { 73 | const players = this.data.players; 74 | const ischecked = players[0].info.isChecked || players[1].info.isChecked; 75 | const checkedPlayer = this.checkedPlayer(); 76 | ischecked && console.log(checkedPlayer.data.username + " is in check"); 77 | } 78 | 79 | // when their is a winner 80 | winner() { 81 | const Winner = this.info.won; 82 | const CreatePopUp = function () { 83 | alert(`The winner is ${Winner.data.username}`) 84 | }; 85 | 86 | console.log(`The winner is ${Winner.data.username}`); 87 | 88 | CreatePopUp(); 89 | } 90 | 91 | // end the game 92 | checkmate(player) { 93 | this.info.started = false; 94 | this.info.ended = true; 95 | this.info.won = player; 96 | 97 | console.log(`${this.info.turn.data.username} is in Mate`); 98 | 99 | this.winner(); 100 | } 101 | 102 | updatePlayers() { 103 | this.data.players.forEach((player) => player.update()); 104 | } 105 | 106 | checkedPlayer() { 107 | const players = this.data.players; 108 | return players.filter((player) => { 109 | return player.info.isChecked == true; 110 | })[0]; 111 | } 112 | 113 | // change turning player 114 | changeTurn() { 115 | const turn = this.info.turn; 116 | const players = this.data.players; 117 | this.info.turn = players.filter((p, index) => { 118 | return players.indexOf(turn) != index; 119 | })[0]; 120 | } 121 | 122 | // switch player into another player 123 | switchTurn(player) { 124 | const players = this.data.players; 125 | return players.filter((p, index) => { 126 | return players.indexOf(player) != index; 127 | })[0]; 128 | } 129 | 130 | // test and check the move wheater checked or not 131 | // then alert when can't possibly moved 132 | testMove(piece, square) { 133 | const board = this.data.board; 134 | piece = board.filterPiece(this, piece); // filter piece 135 | square = board.filterSquare(square); // filter square 136 | 137 | if (!piece || !square) return false; 138 | const backup = { square: piece.square, piece: square.piece }; // back up current data 139 | let player = backup.piece ? backup.piece.player : null; 140 | let pieces = backup.piece ? player.data.pieces : null; 141 | let index = backup.piece ? pieces.indexOf(backup.piece) : null; // if there's piece inside store 142 | let status = false; 143 | 144 | // if there is piece, remove it from the board 145 | index && pieces.splice(index, 1); 146 | 147 | // then move the piece 148 | piece.silentMove(square); 149 | 150 | // then check how the board, react and possibilities 151 | status = this.data.board.analyze(); // will return false, if you are checked 152 | 153 | // move back again the piece in it's position 154 | piece.silentMove(backup.square); 155 | 156 | // place the piece in to it'square 157 | square.piece = backup.piece; 158 | 159 | // and place again in to the board 160 | index && pieces.splice(index, 0, backup.piece); 161 | 162 | return status; 163 | } 164 | 165 | // After the player moves 166 | moved(...param) { 167 | this.data.board.resetSquares(); // reset possible squares ui 168 | this.data.board.setMovedSquare(...param); 169 | this.changeTurn(); // whem player moves, change turn player 170 | this.notify(); // update, alert and prompt 171 | this.isMate(); // check if mate 172 | this.updatePlayers(); // update players 173 | this.insertToMatchHistory(...param); // insert into match history 174 | } 175 | 176 | // insert moves into game match history 177 | insertToMatchHistory(from, to) { 178 | const move = { 179 | piece: to.piece.getAlias(), 180 | from: from.info.position, 181 | to: to.info.position, 182 | }; 183 | 184 | this.data.matchHistory.push(move); 185 | to.piece.player.data.movesHistory.push(move); 186 | console.log(JSON.stringify(this.data.matchHistory)); 187 | } 188 | 189 | // load match history before it preview 190 | async loadMatchHistory(matchHistoryJsonFile) { 191 | const isjson = typeof matchHistoryJsonFile == "object"; 192 | const isString = typeof matchHistoryJsonFile == "string"; 193 | 194 | // when not json or url string 195 | if (!isjson && !isString) { 196 | throw new Error("Invalid Match History!"); 197 | } 198 | 199 | // if it is url, fetch 200 | if (isString) { 201 | try { 202 | matchHistoryJsonFile = await fetch(matchHistoryJsonFile); 203 | matchHistoryJsonFile = await matchHistoryJsonFile.json(); 204 | } catch (e) { 205 | // if somethings not right throw an error 206 | throw new Error("Error, Can't load match history!"); 207 | } 208 | } 209 | 210 | // notify if the match history is empty 211 | if (matchHistoryJsonFile.length == 0) { 212 | throw new Error("Empty Match History!"); 213 | } else { 214 | // otherwise preview it 215 | this.previewMatchHistory(matchHistoryJsonFile); 216 | } 217 | } 218 | 219 | // preview match history 220 | async previewMatchHistory(matchHistory, index = 0) { 221 | const board = this.data.board; 222 | // tell that you are previewing 223 | // so player can't move 224 | this.info.preview = true; 225 | 226 | // move piece that has transition effect 227 | const moveTo = async function (player, piece, square) { 228 | return new Promise((resolve) => { 229 | setTimeout(function () { 230 | if (!piece) resolve(); // if piece is undefine just return 231 | // analyze first the board 232 | board.setSquarePossibilities(piece.getPossibleSqOnly()); 233 | // then move the piece 234 | resolve(player.move(piece, square)); // 235 | }, 1000); 236 | }); 237 | }; 238 | 239 | for (let i = 0; i < matchHistory.length; i++) { 240 | let player = this.data.players[index]; // the player 241 | let piece = board.filterPiece(player, matchHistory[i].piece); // convert it to piece class 242 | let square = board.filterSquare(matchHistory[i].to); // find the square 243 | let move = await moveTo(player, piece, square); // move the piece 244 | 245 | // if there's an error into a movement then throw an error 246 | if (!move) 247 | throw new Error( 248 | `Opps Something is wrong, Movement ${matchHistory[i].from} to ${matchHistory[i].to} is not right` 249 | ); 250 | 251 | index = index == 0 ? 1 : 0; 252 | } 253 | 254 | // Previewing is Done, Player can move now 255 | this.info.preview = false; 256 | } 257 | 258 | // set time (min, sec) 259 | // will convert int into duration format => 5 mins = 300 260 | setTime(minutes, sec) { 261 | return 60 * parseInt(minutes) ?? 0 + parseInt(sec) ?? 0; 262 | } 263 | 264 | // parse time into string format 265 | parseTime(time) { 266 | let minutes = parseInt(time / 60, 10); 267 | let seconds = parseInt(time % 60, 10); 268 | 269 | minutes = minutes < 10 ? "0" + minutes : minutes; 270 | seconds = seconds < 10 ? "0" + seconds : seconds; 271 | 272 | return { minutes, seconds, text: minutes + ":" + seconds }; 273 | } 274 | 275 | // is chess is ready 276 | isReady() { 277 | return this.info.started && !this.info.ended && !this.info.won; 278 | } 279 | 280 | // check if the player is mate 281 | isMate() { 282 | const playerTurn = this.info.turn; // turning player 283 | const pieces = playerTurn.data.pieces; // player pieces 284 | const King = this.data.board.findPiece(pieces, "King", true); // find a king 285 | const moves = []; // store the possible moves 286 | 287 | // if the player is checked 288 | if (playerTurn.info.isChecked) { 289 | for (const piece of pieces) { 290 | for (const square of piece.getPossibilities().moves) { 291 | if (this.testMove(piece, square)) { 292 | // if there was an success move 293 | // insert that move into moves array 294 | moves.push(piece); 295 | } 296 | } 297 | } 298 | 299 | // if there's no possible move, and King was don't have move also 300 | // then checkmate 301 | if (!moves.length && !King.getPossibleSqOnly()) { 302 | this.checkmate(this.switchTurn(playerTurn)); 303 | return true; 304 | } 305 | } 306 | } 307 | } 308 | 309 | // Chess Board 310 | class Board { 311 | constructor(game) { 312 | this.default = { 313 | col_row: 8, // col len 314 | col: ["a", "b", "c", "d", "e", "f", "g", "h"], // col literals 315 | row: [8, 7, 6, 5, 4, 3, 2, 1], // row literals 316 | }; 317 | 318 | this.game = game; // the game 319 | this.data = []; // empty data values 320 | } 321 | 322 | // create ui 323 | create() { 324 | const col_row = this.default.col_row; 325 | const col = this.default.col; 326 | const row = this.default.row; 327 | 328 | let role = "white"; // start with white 329 | 330 | // change role 331 | const setRole = () => { 332 | return (role = role == "white" ? "black" : "white"); 333 | }; 334 | 335 | for (let r = 0; r < col_row; r++) { 336 | const squares = []; // store all the square 337 | for (let c = 0; c < col_row; c++) { 338 | const letter = col[c]; 339 | const number = row[r]; 340 | const position = `${letter}${number}`; // new position 341 | const boardPos = { y: r, x: c }; 342 | const square = new Square(boardPos, position, setRole(), this.game); // new square 343 | 344 | squares.push(square); // push the square 345 | } 346 | 347 | this.data.push(squares) && setRole(); // push the squares in the board data 348 | } 349 | } 350 | 351 | // place defaut piece into board 352 | placePiecesAsDefault() { 353 | const board = this; 354 | const game = this.game; // the game 355 | const players = game.data.players; // all player 356 | 357 | const place = function (piece) { 358 | const position = piece.info.position; // piece pos 359 | const square = board.filterSquare(position); // select square acccording to its pos 360 | const pieceElement = piece.info.element; // piece image 361 | const squareElement = square.info.element; // and the square element 362 | 363 | piece.square = square; // declare square into piece 364 | square.piece = piece; // declare piece into square 365 | 366 | squareElement.appendChild(pieceElement); // just append the image to the square el 367 | }; 368 | 369 | // loop through players and place their pieces 370 | players.forEach((player) => player.data.pieces.forEach(place)); 371 | } 372 | 373 | // get all players possibilities 374 | // enemies, moves and castling 375 | getAllPossibilities() { 376 | const players = this.game.data.players; // players 377 | const white = players[0].analyze(); // player 1 378 | const black = players[1].analyze(); // player 2 379 | 380 | return { white, black }; 381 | } 382 | 383 | // analyze the board 384 | analyze() { 385 | let status = true; // stat 386 | let turnPlayer = this.game.info.turn; 387 | let AP = this.getAllPossibilities(); // all player possibilities 388 | let entries = Object.entries(AP); // convert as object 389 | 390 | // loop through players and collect their enemies 391 | for (let data of entries) { 392 | const King = this.findPiece(data[1].enemies, "King"); 393 | if (King) { 394 | King.player.info.isChecked = true; 395 | // if the turn player role is equal to the king player role 396 | if (turnPlayer.data.role != data[0]) { 397 | status = false; // set as false 398 | King.player.info.isChecked = false; 399 | } 400 | break; 401 | } 402 | } 403 | 404 | return status; 405 | } 406 | 407 | // setting classes and possibilities 408 | setSquarePossibilities(possibilities, insertUI) { 409 | if (!possibilities) return; 410 | let { moves, enemies, castling } = possibilities; 411 | 412 | // reset first 413 | this.resetSquares(); 414 | 415 | // then set square properties according to possibilities values 416 | moves.forEach((square) => square.setAs("move", true, insertUI)); 417 | enemies.forEach((square) => square.setAs("enemy", true, insertUI)); 418 | castling.forEach((square) => square.setAs("castling", true, insertUI)); 419 | } 420 | 421 | // remove all class from all squares 422 | resetSquares() { 423 | for (let squares of this.data) { 424 | for (let square of squares) { 425 | square.setAs("move", false, true); 426 | square.setAs("enemy", false, true); 427 | square.setAs("castling", false, true); 428 | square.setAs("from", false, true); 429 | square.setAs("to", false, true); 430 | } 431 | } 432 | } 433 | 434 | setMovedSquare(from, to) { 435 | from.setAs("from", true, true); 436 | to.setAs("to", true, true); 437 | } 438 | 439 | // Check if the x and y position is valid in board 440 | isValidPos(y, x) { 441 | return this.data[y] ? this.data[y][x] : false; 442 | } 443 | 444 | // will convert position square in to a Square object 445 | // e4 => Square 446 | filterSquare(sq) { 447 | // check if it is already an object 448 | if (!sq || typeof sq == "object") return sq; 449 | 450 | // loop in board 451 | for (let squares of this.data) { 452 | // loop through the squares 453 | for (let square of squares) { 454 | // check if square the position is equal to the given pos 455 | if (square.info.position == sq) { 456 | return square; 457 | } 458 | } 459 | } 460 | } 461 | 462 | // will convert piece alias into a Piece object 463 | // wP4 => Piece 464 | filterPiece(player, piece) { 465 | // check if it is already an object 466 | if (!piece || !player || typeof piece == "object") return piece; 467 | 468 | const pieces = player.data.pieces; // player pieces 469 | const alias = piece.substring(0, 2); // alias 470 | const index = piece.charAt(2); // index 471 | 472 | // loop through the pieces 473 | for (let piece of pieces) { 474 | // check if the alias and index is correct 475 | // the return it 476 | if (piece.info.alias == alias) { 477 | if (piece.info.index == index) { 478 | return piece; 479 | } 480 | } 481 | } 482 | } 483 | 484 | // find piece on array of piece or array of squares 485 | findPiece(squares, piece, isPieces) { 486 | if (!squares || !squares.length || !piece) return false; 487 | 488 | // if is not object then just return piece means it is alias or name of piece 489 | piece = this.filterPiece(piece) ?? piece; 490 | 491 | const filter = squares.filter((square) => { 492 | const p = isPieces 493 | ? square 494 | : typeof square == "object" 495 | ? square.piece 496 | : this.filterSquare(square).piece; // the piece 497 | const name = piece.info ? piece.info.name : piece; // piece name 498 | const alias = piece.info ? piece.info.alias : piece; // piece alias 499 | return p.info.name == name || p.info.alias == alias; // find piece where alias or name is equal to the given piece 500 | }); 501 | 502 | return ( 503 | filter.map((sq) => { 504 | return this.filterSquare(sq).piece ?? sq; 505 | })[0] ?? false 506 | ); 507 | } 508 | 509 | } 510 | 511 | // Chess Piece 512 | class Piece { 513 | constructor(pieceObj, player, game) { 514 | this.info = { 515 | ...pieceObj, // piece information 516 | fastpawn: pieceObj.name == "Pawn", // only if pawn 517 | castling: pieceObj.name == "King", // only if king 518 | element: null, 519 | }; 520 | 521 | this.data = {}; // just set to an empty * bug 522 | this.player = player; // players 523 | this.game = game; // game 524 | 525 | this.init(); 526 | } 527 | 528 | init() { 529 | this.create(); // create new Image element 530 | this.listener(); // some listeners 531 | } 532 | 533 | // when there's piece inside the target square, eat them 534 | eat(piece) { 535 | if (!piece) return; 536 | const piecePlayer = piece.player; 537 | const player = this.player; 538 | 539 | // if element exist, remove the element 540 | piece.info.element && piece.info.element.remove(); 541 | 542 | // insert into the target player dropped pieces 543 | piecePlayer.data.dropped.push(piece); 544 | // remove piece into the target player pieces 545 | piecePlayer.data.pieces.splice(piecePlayer.data.pieces.indexOf(piece), 1); 546 | // insert into the player eated pieces 547 | player.data.eated.push(piece); 548 | 549 | return piece; 550 | } 551 | 552 | moveElementTo(square) { 553 | // set fastpawn and castling to false 554 | this.info.fastpawn = false; 555 | this.info.castling = false; 556 | 557 | // append the element into the target square element 558 | square.info.element.appendChild(this.info.element); 559 | } 560 | 561 | // move from current square to the target square 562 | move(square, castling) { 563 | let old = this.square; 564 | // eat piece inside 565 | this.eat(square.piece); 566 | // move piece into the square 567 | this.silentMove(square); 568 | // move the image into the square element 569 | this.moveElementTo(square); 570 | 571 | // trigger, finished moved 572 | this.game.moved(old, square); 573 | 574 | // if the move is castling, then castle 575 | castling && this.castling(); 576 | } 577 | 578 | // move in the background 579 | silentMove(square) { 580 | const piece = this; 581 | const board = this.game.data.board; 582 | 583 | // make sure it is Square object 584 | square = board.filterSquare(square) 585 | // set first to false 586 | square.piece = false; 587 | piece.square.piece = false; 588 | 589 | // change data 590 | square.piece = piece; 591 | piece.square = square; 592 | piece.info.position = square.info.position; 593 | piece.square.piece = piece; 594 | } 595 | 596 | // castling 597 | castling() { 598 | // castling only if it is King 599 | if (this.info.name != "King") return false; 600 | 601 | const game = this.game; 602 | const board = game.data.board.data; 603 | const { x, y } = this.square.info.boardPosition; 604 | 605 | const check = function (piece, square, condition) { 606 | // move only if the condition is true 607 | if (!condition) return; 608 | 609 | // move piece into the square 610 | piece.silentMove(square); 611 | // move element into the square element 612 | piece.moveElementTo(square); 613 | }; 614 | 615 | // right and left rook 616 | const rr = board[y][x + 1].piece; 617 | const lr = board[y][x - 2].piece; 618 | // console.log(lr) 619 | // console.log("hello") 620 | // console.dir(board[y][x-2].piece) 621 | // check each rook 622 | check(rr, board[y][x - 1], rr && rr.info.name == "Rook"); 623 | check(lr, board[y][x + 1], lr && lr.info.name == "Rook"); 624 | 625 | } 626 | 627 | create() { 628 | const pieceElement = new Image(); // new Image element 629 | const classname = "chessboard-piece"; 630 | 631 | // apply 632 | pieceElement.src = `./assets/media/pieces/${this.info.alias}.png`; 633 | pieceElement.classList.add(classname); 634 | 635 | this.info.element = pieceElement; // store 636 | } 637 | 638 | listener() { 639 | const piece = this; // selected piece 640 | const game = this.game; // the game 641 | const element = this.info.element; // the image of piece 642 | const board = game.data.board; // the board 643 | 644 | // on mousedown event 645 | const mousedown = function (event) { 646 | let current = null; // set as null a target square 647 | let elemBelow, droppableBelow; // squares positioning 648 | 649 | // if player is previewing match history 650 | // return false 651 | if (game.info.preview) return; 652 | 653 | // move the piece towards direction 654 | const move = function (pageX, pageY) { 655 | element.style.cursor = "grabbing"; // set the cursor as grab effect 656 | element.style.left = pageX - element.offsetWidth / 2 + "px"; 657 | element.style.top = pageY - element.offsetHeight / 2 + "px"; 658 | }; 659 | 660 | // when user mousemove 661 | const mousemove = function (event) { 662 | move(event.pageX, event.pageY); // move the piece in mouse position 663 | 664 | element.hidden = true; // hide the element so it will not affect searching point 665 | elemBelow = document.elementFromPoint(event.clientX, event.clientY); // search from point x and y 666 | element.hidden = false; // then show again 667 | 668 | if (!elemBelow) return; 669 | 670 | // find the closest square from the mouse 671 | droppableBelow = elemBelow.closest(".chessboard-square"); 672 | 673 | // if it is not the current square 674 | if (current != droppableBelow) current = droppableBelow; 675 | }; 676 | 677 | // when the user drop the piece 678 | const drop = function () { 679 | // remove first the mousemove event 680 | document.removeEventListener("mousemove", mousemove); 681 | 682 | // then assign styles to go back to it's position in square 683 | element.removeAttribute("style"); 684 | 685 | if (!current) return false; 686 | if (game.info.turn != piece.player) return false; 687 | 688 | piece.player.move(piece, current.getAttribute("data-position")); 689 | }; 690 | 691 | // just setting the styles 692 | const setStyle = function () { 693 | // set the position to absolute so the image can drag anywhere on the screen 694 | element.style.position = "absolute"; 695 | // set the z index to max so it will go above all elements 696 | element.style.zIndex = 1000; 697 | }; 698 | 699 | // just sets some listeners 700 | const manageListener = function () { 701 | // drop on mouseup event 702 | element.onmouseup = drop; 703 | 704 | // disabled dragging 705 | element.ondragstart = function () { 706 | return false; 707 | }; 708 | 709 | // add mousemove listener again 710 | document.addEventListener("mousemove", mousemove); 711 | }; 712 | 713 | // declaration 714 | setStyle(); 715 | manageListener(); 716 | move(event.pageX, event.pageY); 717 | 718 | if (game.info.turn != piece.player) return false; 719 | // get the piece possibilities, values(moves(array), enemies(array), castling(array)) 720 | // then show circles to all that squares 721 | board.setSquarePossibilities(piece.getPossibleSqOnly(), true); 722 | 723 | piece.player.data.currentPiece = piece; 724 | }; 725 | 726 | // add mousedown listener 727 | element.addEventListener("mousedown", mousedown); 728 | } 729 | 730 | // get piece possibilites, move, enemies, castling 731 | getPossibilities() { 732 | const piece = this; // the current piece 733 | const square = this.square; // the current square where piece located 734 | const player = this.player; // the turning player 735 | const role = player.data.role; // player role values(white, black) 736 | const game = this.game; // the game 737 | const gameboard = game.data.board; // game board 738 | const board = gameboard.data; // and the board data 739 | const pos = { moves: [], enemies: [], castling: [] }; // possibilities object 740 | let { x, y } = square.info.boardPosition; // square position into board 741 | 742 | // will check if the piece inside the given square is enemy or not 743 | // then if it is push it into enemies pos 744 | const testEnemy = function (y, x) { 745 | // check if the position is valid 746 | if (!gameboard.isValidPos(y, x)) return false; 747 | 748 | const square = board[y][x]; // target square 749 | const piece = square.piece; // piece inside the target square 750 | 751 | if (!square || !piece) return false; 752 | if (piece.player.data.role == role) return false; 753 | 754 | pos.enemies.push(square); 755 | }; 756 | 757 | // test the square when piece can be move or there is enemy 758 | const testSquare = function (y, x) { 759 | // check if the position is valid 760 | if (!gameboard.isValidPos(y, x)) return false; 761 | 762 | const square = board[y][x]; // target square 763 | const sqpiece = square.piece; // piece inside the target square 764 | 765 | if (!square) return false; 766 | 767 | if (sqpiece) { 768 | if (piece.info.name != "Pawn") testEnemy(y, x); 769 | return false; 770 | } else { 771 | pos.moves.push(square); 772 | return true; 773 | } 774 | }; 775 | 776 | // test directions and check how long the piece can be move from the board 777 | // yi / xi = y/x need to change 778 | // yo / xo = what operation, true = addition while false = subtration 779 | // un = until (number), how many squares need to check 780 | // is = isKing, then if it is check for castlings 781 | const testLoopSquare = function (yi, yo, xi, xo, un = 8, is) { 782 | for (let i = 1; i < un; i++) { 783 | const ny = yi ? (yo ? y + i : y - i) : y; 784 | const nx = xi ? (xo ? x + i : x - i) : x; 785 | 786 | // check if the position is valid 787 | if (!gameboard.isValidPos(ny, nx)) return false; 788 | 789 | const square = board[ny][nx]; // target square 790 | const sqpiece = square.piece; // piece inside the target square 791 | 792 | if (square) { 793 | if (sqpiece) { 794 | // if not pawn then test if there is enemy 795 | if (piece.info.name != "Pawn") testEnemy(ny, nx); 796 | break; 797 | } else if (is && i == 2) { 798 | // if isKing then check then run as one only in a loop 799 | 800 | const check = function (condition) { 801 | if (condition) pos.castling.push(square); 802 | }; 803 | 804 | const rightrook = board[ny][nx + 1].piece; 805 | const leftrook = board[ny][nx - 1].piece; 806 | 807 | 808 | check(rightrook && rightrook.info.name == "Rook"); 809 | check(leftrook && leftrook.info.name == "Rook"); 810 | 811 | } 812 | 813 | pos.moves.push(square); 814 | } 815 | } 816 | }; 817 | 818 | // All Piece move patterns 819 | const Pattern = { 820 | Pawn: function () { 821 | // check if pawn can fastpawn then if it is, increment 1 to it's possible move 822 | let until = piece.info.fastpawn ? 3 : 2; 823 | 824 | // loop through until values 825 | for (let i = 1; i < until; i++) { 826 | if (role == "white") { 827 | // if it is white, subrtact current i value 828 | // so it moves from bottom to top 829 | if (!testSquare(y - i, x)) break; 830 | } else { 831 | // if it is black, add current i value 832 | // so it moves from top to bottom 833 | if (!testSquare(y + i, x)) break; 834 | } 835 | } 836 | 837 | // enemy detection 838 | if (role == "white") { 839 | // (white) check the top left and right square from it's position 840 | testEnemy(y - 1, x - 1); 841 | testEnemy(y - 1, x + 1); 842 | } else { 843 | // (black) check the bottom left and right square from it's position 844 | testEnemy(y + 1, x - 1); 845 | testEnemy(y + 1, x + 1); 846 | } 847 | }, 848 | 849 | Rook: function () { 850 | // Top 851 | testLoopSquare(true, false, false, false); 852 | // Bottom 853 | testLoopSquare(true, true, false, false); 854 | // Left 855 | testLoopSquare(false, false, true, false); 856 | // Right 857 | testLoopSquare(false, false, true, true); 858 | }, 859 | 860 | Bishop: function () { 861 | // Top left 862 | testLoopSquare(true, false, true, false); 863 | // Bottom Left 864 | testLoopSquare(true, true, true, false); 865 | // Bottom Right 866 | testLoopSquare(true, false, true, true); 867 | // Bottom Right 868 | testLoopSquare(true, true, true, true); 869 | }, 870 | 871 | Knight: function () { 872 | // Top 873 | testSquare(y - 2, x - 1); 874 | testSquare(y - 2, x + 1); 875 | // Bottom 876 | testSquare(y + 2, x - 1); 877 | testSquare(y + 2, x + 1); 878 | // Left 879 | testSquare(y - 1, x - 2); 880 | testSquare(y + 1, x - 2); 881 | // Right 882 | testSquare(y - 1, x + 2); 883 | testSquare(y + 1, x + 2); 884 | }, 885 | 886 | Queen: function () { 887 | Pattern.Rook(); // can move like a rook 888 | Pattern.Bishop(); // can move like a bishope 889 | }, 890 | 891 | King: function () { 892 | // Top 893 | testSquare(y - 1, x); 894 | // Bottom 895 | testSquare(y + 1, x); 896 | // Top Left 897 | testSquare(y - 1, x - 1); 898 | // Top Right 899 | testSquare(y - 1, x + 1); 900 | // Bottom Left 901 | testSquare(y + 1, x - 1); 902 | // Bottom Right 903 | testSquare(y + 1, x + 1); 904 | 905 | if (piece.info.castling) { 906 | testLoopSquare(false, false, true, true, 3, true); 907 | testLoopSquare(false, false, true, false, 3, true); 908 | } 909 | }, 910 | }; 911 | 912 | // then get the pattern base on their name 913 | // and call it 914 | Pattern[this.info.name].call(); 915 | 916 | // return possibilities 917 | return pos; 918 | } 919 | 920 | getPossibleSqOnly() { 921 | let { moves, enemies, castling } = this.getPossibilities(); 922 | const game = this.game; 923 | 924 | const filter = (s) => { 925 | return s.filter((sq) => { 926 | return game.testMove(this, sq); 927 | }); 928 | }; 929 | 930 | game.data.board.resetSquares(); 931 | moves = filter(moves); 932 | enemies = filter(enemies); 933 | castling = filter(castling); 934 | 935 | return moves.length || enemies.length || castling.length 936 | ? { moves, enemies, castling } 937 | : false; 938 | } 939 | 940 | getAlias() { 941 | return `${this.info.alias}${this.info.index}`; 942 | } 943 | } 944 | 945 | // Chess Square 946 | class Square { 947 | constructor(boardPosition, position, role, game) { 948 | this.info = { 949 | boardPosition, // square board position 950 | position, // square position 951 | role, // square role 952 | element: null, // square element 953 | isMove: false, // possible move 954 | isEnemy: false, // possible enemy 955 | isCastle: false, // possible castle 956 | }; 957 | 958 | this.piece = null; // the piece 959 | this.game = game; // the game 960 | 961 | this.init(); 962 | } 963 | 964 | // initialize and ready 965 | init() { 966 | this.create(); // create square element 967 | this.listener(); // some listeners 968 | } 969 | 970 | // create ui 971 | create() { 972 | const squareElement = document.createElement("DIV"); // new Div element 973 | const classname = "chessboard-square"; // element classname 974 | 975 | squareElement.classList.add(classname); // add 976 | squareElement.setAttribute("role", this.info.role); // set role 977 | squareElement.setAttribute("data-position", this.info.position); // and pos 978 | 979 | chessboardParent.appendChild(squareElement); // append to parent 980 | this.info.element = squareElement; // store 981 | } 982 | 983 | listener() { 984 | // action when player clicks on square 985 | const action = function () { 986 | const player = this.game.info.turn; 987 | const info = this.info; 988 | const isQualified = info.isMove || info.isEnemy || info.isCastle; 989 | const currentPiece = player.data.currentPiece; 990 | 991 | if (!isQualified || !currentPiece) return false; 992 | 993 | // move the player on the selected squares 994 | player.move(currentPiece, this); 995 | }; 996 | 997 | this.info.element.addEventListener("click", action.bind(this)); 998 | } 999 | 1000 | setAs(classname, bool, ui) { 1001 | const element = this.info.element; 1002 | 1003 | this.info.isEnemy = classname == "enemy" && bool; // if there's enemy on the square 1004 | this.info.isMove = classname == "move" && bool; // if can possibly move the piece 1005 | this.info.isCastle = classname == "castling" && bool; // if can castling through that position 1006 | 1007 | if (!ui) return; 1008 | // add class if true and remove if false 1009 | bool 1010 | ? element.classList.add(classname) 1011 | : element.classList.remove(classname); 1012 | } 1013 | } 1014 | 1015 | // Player 1016 | class Player { 1017 | constructor(player) { 1018 | this.info = { 1019 | isTurn: false, // is player turn 1020 | isWinner: false, // is already won 1021 | isStarted: false, // is player started to move 1022 | isTimeout: false, // is player time was ended 1023 | isLeave: false, // is player was leaved 1024 | isChecked: false, // is player was checked 1025 | isReady: false, // is player is ready to go 1026 | }; 1027 | 1028 | this.data = { 1029 | ...player, // rewrite player information 1030 | total_moves: 0, // all the moves 1031 | timer: { m: null, s: null }, 1032 | piecesData: {}, // data pieces 1033 | pieces: [], // array of pieces 1034 | dropped: [], // all of the pieces that enemy slay 1035 | eated: [], // eated pieces 1036 | moves: [], // total possible moves 1037 | enemies: [], // total possible enemies 1038 | movesHistory: [], // player moves history 1039 | currentPiece: null, // current Piece Holding, 1040 | card: null, 1041 | }; 1042 | 1043 | this.game = null; // empty game 1044 | } 1045 | 1046 | // analyze player side 1047 | analyze() { 1048 | this.data.moves = []; // empty the array 1049 | this.data.enemies = []; // empty the array 1050 | 1051 | const game = this.game; // the game 1052 | const turnPlayer = game.info.turn; 1053 | const pieces = this.data.pieces; // player pieces 1054 | const pos = { moves: [], enemies: [], castling: [] }; // store 1055 | 1056 | // loop through the pieces 1057 | for (const piece of pieces) { 1058 | for (const data of Object.entries(piece.getPossibilities())) { 1059 | for (const square of data[1]) { 1060 | if (!square) return; 1061 | if (!pos[data[0]].includes(square.info.position)) { 1062 | pos[data[0]].push(square.info.position); 1063 | } 1064 | } 1065 | } 1066 | } 1067 | 1068 | this.data.moves = pos.moves; // set the moves 1069 | this.data.enemies = pos.enemies; // set the enemies 1070 | this.info.isTurn = turnPlayer.data.username == this.data.username; // if the player is equal to turning player 1071 | 1072 | return pos; 1073 | } 1074 | 1075 | // update ui 1076 | update() { 1077 | const game = this.game; 1078 | const players = game.data.players; 1079 | const pos = players.indexOf(this) + 1; 1080 | const playerCard = document.querySelector(`.player-card.player-${pos}`); 1081 | const isTurn = game.info.turn == this; 1082 | 1083 | if (!playerCard) return; 1084 | const username = playerCard.querySelector(".row-1 .text .headline h4"); 1085 | const status = playerCard.querySelector(".row-1 .text .status span"); 1086 | const timer = playerCard.querySelector(".row-2 .timer"); 1087 | 1088 | username.innerText = this.data.username; 1089 | status.innerText = isTurn ? "Player Turn" : ""; 1090 | 1091 | this.data.card = { username, status, timer }; 1092 | 1093 | try { 1094 | this.analyze(); 1095 | } catch (e) {} 1096 | } 1097 | 1098 | // move target piece to the target square 1099 | move(piece, square) { 1100 | if (!piece || !square) return false; 1101 | const board = this.game.data.board; 1102 | // make sure piece and square is an object 1103 | piece = board.filterPiece(this, piece); 1104 | square = board.filterSquare(square); 1105 | 1106 | const game = this.game; // the game 1107 | const test = game.testMove(piece, square); // test first the move, will return bollean 1108 | const info = square.info; // square information 1109 | const isQualified = info.isMove || info.isEnemy || info.isCastle; // wheater move, enemy or castle 1110 | 1111 | // if the game was not started 1112 | if (!game.isReady()) return false; 1113 | 1114 | // if not ready or not already fetch all the pieces 1115 | if (!this.info.isReady) return false; 1116 | 1117 | // if checked and not correct move 1118 | if (this.info.isChecked) return false; 1119 | 1120 | // if out of time 1121 | if (this.info.isTimeout) return false; 1122 | 1123 | // if not turn 1124 | if (!this.info.isTurn) return false; 1125 | 1126 | // if not qualified, or not possible (move, enemy) 1127 | if (!isQualified) return false; 1128 | 1129 | // if theres no wrong in move, then move 1130 | if (test) piece.move(square, info.isCastle); 1131 | 1132 | return test; 1133 | } 1134 | 1135 | // start the timer 1136 | startTimer() { 1137 | const game = this.game; // the game 1138 | const player = this; // the player 1139 | const card = player.data.card; // playr card element 1140 | const timer = card.timer; // player card timer element 1141 | const span = timer.querySelector("span"); // timer span element 1142 | let { m, s } = player.data.timer; // current data 1143 | let curentduration = parseInt(60 * m) + parseInt(s) ?? 0; 1144 | let duration = m ? curentduration : game.setTime(game.info.timer); 1145 | 1146 | // callback 1147 | const countdownfunction = function () { 1148 | let { minutes, seconds, text } = game.parseTime(duration); // parse time 1149 | 1150 | span.innerText = text; // insert into span 1151 | 1152 | // countdown only if player turn 1153 | if (player.info.isTurn) { 1154 | // if timeout 1155 | if (--duration < 0) { 1156 | timer.classList.add("timeout"); 1157 | player.info.isTimeout = true; 1158 | game.info.won = game.switchTurn(player); 1159 | game.winner(); 1160 | clearInterval(countdown); 1161 | } 1162 | 1163 | // store current data 1164 | player.data.timer = { m: minutes, s: seconds }; 1165 | } 1166 | }; 1167 | 1168 | const countdown = setInterval(countdownfunction, 1000); 1169 | 1170 | countdownfunction(); 1171 | } 1172 | 1173 | async getPieces() { 1174 | let role = this.data.role; // values ("white", "black") 1175 | let path = `./assets/javascript/json/${role}-pieces.json`; // just a path of json file 1176 | let data = await fetch(path); // get the file content 1177 | this.data.piecesData = await data.json(); // convert data as json 1178 | this.info.isReady = true; // now the player is ready to go 1179 | } 1180 | 1181 | async setPieces() { 1182 | const player = this; // the player 1183 | const game = this.game; // the game 1184 | const pieces = this.data.pieces; // array of class Pieces 1185 | const piecesData = this.data.piecesData; // data of all Chess Pieces 1186 | 1187 | const set = function (setPieceObj) { 1188 | // Get Values 1189 | let { name, length, alias, position } = setPieceObj; 1190 | let { letter: letters, number } = position; 1191 | // Loop through their lengths 1192 | for (let i = 0; i < length; i++) { 1193 | const position = `${letters[i]}${number}`; // get the position 1194 | const obj = { name, alias, position, index: i }; // create piece information 1195 | const piece = new Piece(obj, player, game); // new Piece 1196 | pieces.push(piece); // insert to the array of class Pieces 1197 | } 1198 | }; 1199 | 1200 | // Loop through all the data then generate some Piece base on their length 1201 | // And game rules 1202 | piecesData.forEach(set); 1203 | } 1204 | 1205 | async init(game) { 1206 | this.game = game; // initialize the game 1207 | 1208 | await this.getPieces(); // get all data pieces 1209 | await this.setPieces(); // set object pieces to class Pieces 1210 | 1211 | this.update(); 1212 | } 1213 | } 1214 | const reset = document.querySelector(".button-64"); 1215 | reset.addEventListener("click", resetGame); 1216 | 1217 | function resetGame (){ 1218 | location.reload() 1219 | } 1220 | 1221 | const Game = new Chess(); // game 1222 | 1223 | Game.init(function () { 1224 | this.start(); 1225 | // this.loadMatchHistory("./assets/javascript/json/matchhistory.json"); 1226 | }); // initialize 1227 | 1228 | --------------------------------------------------------------------------------