├── poster.pdf ├── Assets ├── favicon.ico ├── black_pawn.svg ├── white_pawn.svg ├── blue_dot.svg ├── white_rook.svg ├── white_knight.svg ├── black_bishop.svg ├── white_bishop.svg ├── white_king.svg ├── black_king.svg ├── black_knight.svg ├── white_queen.svg ├── black_rook.svg ├── white_fork.svg ├── black_queen.svg ├── black_fork.svg └── logo.svg ├── Js ├── Position.js ├── Piece.js ├── Square.js ├── Messages.json ├── Knight.js ├── Rook.js ├── Pawn.js ├── Bishop.js ├── King.js ├── Queen.js ├── UI.js ├── Game.js └── Board.js ├── README.md ├── .gitignore ├── Style.css └── index.html /poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainStack/ChessScouter/master/poster.pdf -------------------------------------------------------------------------------- /Assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainStack/ChessScouter/master/Assets/favicon.ico -------------------------------------------------------------------------------- /Js/Position.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function Position(x, y) { 3 | this.x = x; 4 | this.y = y; 5 | }; -------------------------------------------------------------------------------- /Js/Piece.js: -------------------------------------------------------------------------------- 1 | function Piece(color) { 2 | this.color = color; 3 | this.captured = false; 4 | } 5 | 6 | function Queen(color) { 7 | this.color = color; 8 | this.material = 9; 9 | } 10 | 11 | Queen.prototype = new Piece(this.color); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ChessScouter 2 | ============ 3 | 4 | A digital chess application that runs in the web browser and dynamically displays strategic details on the board visually. 5 | The visualizations act as a set of visual "training wheels" to help players better understand strategy and make better moves. 6 | The latest version of the prototyple is playable here: 7 | http://captainstack.github.io/ChessScouter/ 8 | 9 | ============ 10 | Andre Stackhouse 11 | Ashish Chandwani 12 | David Ewald 13 | -------------------------------------------------------------------------------- /Js/Square.js: -------------------------------------------------------------------------------- 1 | //This data structure represents one square on a chessboard and 2 | //holds information such as its position and what piece (if any) occupies it. 3 | "use strict"; 4 | function Square(x, y, piece) { 5 | this.x = x; 6 | this.y = y 7 | this.piece = piece; 8 | this.flair = false; 9 | this.legalMove = false; 10 | this.fork = false; 11 | this.current = false; 12 | this.previous = false; 13 | this.blackControl = null; 14 | this.whiteControl = null; 15 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | Icon? 37 | ehthumbs.db 38 | Thumbs.db 39 | 40 | 41 | #Sublime Text 2 42 | ChessScouter.sublime* -------------------------------------------------------------------------------- /Js/Messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "space control": "Red = a square black controls. Green = a square white controls. Yellow = a square contested by both players", 3 | "legal moves": "Click on a piece and a blue dot will appear on all the squares that piece can move to", 4 | "piece flair": "When you move a piece, all the pieces that come under attack as a direct result of the piece you moved will pulse white", 5 | "forks": "All the moves that would result in a simultaneous attack of two pieces are shown", 6 | "pins": "This feature is not yet implemented", 7 | "history": "Every move is recorded in this column using algebraic notation.", 8 | "pgn": "PGN is a plain-text file format to record chess games taht can be inputed into many computer programs" 9 | } -------------------------------------------------------------------------------- /Assets/black_pawn.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 53 | 54 | -------------------------------------------------------------------------------- /Assets/white_pawn.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 53 | 54 | -------------------------------------------------------------------------------- /Js/Knight.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function Knight(color) { 3 | this.color = color; 4 | this.captured = false; 5 | this.image = color + "_knight.svg"; 6 | this.material = 3; 7 | this.type = "knight" 8 | this.symbol = "N"; 9 | } 10 | 11 | Knight.prototype.getPosition = function getPosition() { 12 | for (var i = 0; i < game.board.grid.length; i++) { 13 | for (var j = 0; j < game.board.grid[i].length; j++) { 14 | if (game.board.grid[j][i].piece === this) { 15 | return new Position(j, i); 16 | } 17 | } 18 | } 19 | return new Position(this.x, this.y); 20 | } 21 | 22 | Knight.prototype.getPotentialMoves = function getPotentialMoves(position) { 23 | var x = position.x; 24 | var y = position.y; 25 | var potentialMoves = [ 26 | new Position(x - 2, y + 1), 27 | new Position(x - 2, y - 1), 28 | new Position(x + 1, y - 2), 29 | new Position(x - 1, y - 2), 30 | new Position(x + 2, y + 1), 31 | new Position(x + 2, y - 1), 32 | new Position(x - 1, y + 2), 33 | new Position(x + 1, y + 2) 34 | ]; 35 | for(var i = potentialMoves.length - 1; i >= 0; i--) { 36 | if (!game.board.isOnBoard(potentialMoves[i])) { 37 | potentialMoves.splice(i, 1); 38 | } 39 | } 40 | return potentialMoves; 41 | } 42 | 43 | Knight.prototype.getLegalMoves = function getLegalMoves(currentPosition) { 44 | var legalMoves = this.getPotentialMoves(currentPosition); 45 | for(var i = legalMoves.length - 1; i >= 0; i--) { 46 | if (game.board.occupiedBy(legalMoves[i]) === this.color) { 47 | legalMoves.splice(i, 1); 48 | } 49 | } 50 | return legalMoves; 51 | } 52 | 53 | Knight.prototype.getAttacks = function getAttacks(currentPosition) { 54 | return this.getPotentialMoves(currentPosition); 55 | } 56 | 57 | Knight.prototype.cloneSelf = function cloneSelf() { 58 | var selfClone = new Knight(this.color); 59 | selfClone.color = this.color; 60 | selfClone.captured = this.captured; 61 | selfClone.image = this.image; 62 | selfClone.hasMoved = this.hasMoved; 63 | selfClone.material = this.material; 64 | selfClone.type = this.type; 65 | selfClone.symbol = this.symbol; 66 | return selfClone; 67 | }; -------------------------------------------------------------------------------- /Assets/blue_dot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 45 | 47 | 48 | 50 | image/svg+xml 51 | 53 | 54 | 55 | 56 | 57 | 62 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Assets/white_rook.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 56 | 60 | 64 | 67 | 71 | 74 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Assets/white_knight.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 56 | 60 | 64 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Assets/black_bishop.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 55 | 58 | 61 | 64 | 65 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Assets/white_bishop.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 55 | 58 | 61 | 64 | 65 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Assets/white_king.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Assets/black_king.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Assets/black_knight.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 56 | 60 | 64 | 69 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Assets/white_queen.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /Assets/black_rook.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 56 | 60 | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Style.css: -------------------------------------------------------------------------------- 1 | body, button { 2 | font-family: "Segoe UI Light", "Segoe UI Web Regular", "Segoe UI Symbol", "Helvetica Neue", "BBAlpha Sans","S60 Sans", Arial, "sans-serif"; 3 | font-size: 14pt; 4 | text-align: center; 5 | margin: 0px; 6 | } 7 | 8 | header { 9 | overflow: hidden; 10 | margin: 0px; 11 | text-align: center; 12 | text-decoration: none; 13 | color: white; 14 | background-color: #4f81bc; 15 | padding: 0.5%; 16 | white-space: nowrap; 17 | } 18 | 19 | header h1 { 20 | float: left; 21 | vertical-align: middle; 22 | margin: 0px; 23 | } 24 | h1 img { 25 | padding-right: 2%; 26 | } 27 | header h2 { 28 | float: right; 29 | vertical-align: middle; 30 | margin: 0px; 31 | padding-right: 1%; 32 | } 33 | /* The area that holds the board and pieces. */ 34 | #board { 35 | margin-left: auto; 36 | margin-right: auto; 37 | position: relative; 38 | } 39 | /*Gives the board its checkboard pattern*/ 40 | #moveHistory { 41 | float: left; 42 | width: 25%; 43 | } 44 | #filters { 45 | float: left; 46 | width: 18%; 47 | text-align: left; 48 | } 49 | #gameSpace { 50 | float: left; 51 | width: 50%; 52 | margin-top: 20px; 53 | } 54 | 55 | footer { 56 | position: relative; 57 | bottom: -20px; 58 | width: 50%; 59 | clear: both; 60 | margin-left: auto; 61 | margin-right: auto; 62 | } 63 | #moveList td { 64 | padding: 0px 0px 5px 5px; 65 | } 66 | #board tr { 67 | vertical-align: middle; 68 | } 69 | #board tr td { 70 | background-color: rgb(210, 210, 210); 71 | } 72 | #board tr:nth-child(even) td:nth-child(odd) { 73 | background-color: rgb(240, 240, 240); 74 | } 75 | #board tr:nth-child(odd) td:nth-child(even) { 76 | background-color: rgb(240, 240, 240); 77 | } 78 | #board td { 79 | border: 1px solid white; 80 | margin: 100px; 81 | padding: 10px; 82 | border-spacing: 10px 5px; 83 | } 84 | #board #previous { 85 | border: 1px dotted black; 86 | } 87 | #board #current { 88 | border: 1px dotted black; 89 | } 90 | #board #previous:hover { 91 | border: 1px dotted blue; 92 | } 93 | #board #current:hover { 94 | border: 1px dotted blue; 95 | } 96 | .help { 97 | color: rgb(79, 129, 188); 98 | decoration: none; 99 | font-size: 8pt; 100 | } 101 | .help:hover { 102 | font-weight: bold; 103 | font-size: 14pt; 104 | } 105 | table { 106 | margin-left: auto; 107 | margin-right: auto; 108 | } 109 | #board td:hover { 110 | border-color: blue; 111 | } 112 | .gridLabel { 113 | font-weight: bold; 114 | } 115 | 116 | .pieceFlair { 117 | -webkit-animation-duration: 1.5s; 118 | -webkit-animation-name: pulse; 119 | -webkit-animation-iteration-count: infinite; 120 | -webkit-animation-direction: alternate; 121 | 122 | -ms-animation-duration: 1.5s; 123 | -ms-animation-name: pulse; 124 | -ms-animation-iteration-count: infinite; 125 | -ms-animation-direction: alternate; 126 | 127 | -moz-animation-duration: 1.5s; 128 | -moz-animation-name: pulse; 129 | -moz-animation-iteration-count: infinite; 130 | -moz-animation-direction: alternate; 131 | } 132 | @-webkit-keyframes pulse { 133 | from { 134 | opacity: .2; 135 | } 136 | to { 137 | opacity: 1; 138 | } 139 | } 140 | @-ms-keyframes pulse { 141 | from { 142 | opacity: .2; 143 | } 144 | to { 145 | opacity: 1; 146 | } 147 | } 148 | @-moz-keyframes pulse { 149 | from { 150 | opacity: .2; 151 | } 152 | to { 153 | opacity: 1; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Assets/white_fork.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 48 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 64 | 65 | 67 | 69 | 71 | 73 | 74 | 75 | 76 | 81 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Assets/black_queen.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 52 | 55 | 60 | 65 | 70 | 75 | 80 | 81 | 85 | 89 | 93 | 97 | 101 | 105 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Assets/black_fork.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 44 | 48 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 64 | 65 | 67 | 69 | 71 | 73 | 74 | 75 | 76 | 81 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 50 | 53 | 58 | 63 | 68 | 73 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Js/Rook.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function Rook(color) { 3 | this.color = color; 4 | this.captured = false; 5 | this.image = color + "_rook.svg"; 6 | this.hasMoved = false; 7 | this.material = 5; 8 | this.type = "rook"; 9 | this.symbol = "R"; 10 | } 11 | 12 | Rook.prototype.getPosition = function getPosition() { 13 | for (var i = 0; i < game.board.grid.length; i++) { 14 | for (var j = 0; j < game.board.grid[i].length; j++) { 15 | if (game.board.grid[j][i].piece === this) { 16 | return new Position(j, i); 17 | } 18 | } 19 | } 20 | return new Position(this.x, this.y); 21 | } 22 | 23 | Rook.prototype.getPotentialMoves = function getPotentialMoves(position) { 24 | var x = position.x; 25 | var y = position.y; 26 | var potentialMoves = []; 27 | var vectorNorth = false; 28 | var vectorSouth = false; 29 | var vectorEast = false; 30 | var vectorWest = false; 31 | var addX = 0 32 | var addY = 0 33 | var northMoves = []; 34 | var southMoves = []; 35 | var eastMoves = []; 36 | var westMoves = []; 37 | while(vectorNorth === false || vectorSouth === false || vectorEast === false || vectorWest === false) { 38 | if (vectorNorth === false) { 39 | addY--; 40 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 41 | northMoves.push(new Position (x + addX, y + addY)); 42 | } else { 43 | addY = 0; 44 | vectorNorth = true; 45 | } 46 | } else if (vectorSouth === false) { 47 | addY++; 48 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 49 | southMoves.push(new Position (x + addX, y + addY)); 50 | } else { 51 | addY = 0; 52 | vectorSouth = true; 53 | } 54 | } else if (vectorEast === false) { 55 | addX--; 56 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 57 | eastMoves.push(new Position (x + addX, y + addY)); 58 | } else { 59 | addX = 0; 60 | vectorEast = true; 61 | } 62 | } else if (vectorWest === false) { 63 | addX++; 64 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 65 | westMoves.push(new Position (x + addX, y + addY)); 66 | } else { 67 | addX = 0; 68 | vectorWest = true; 69 | } 70 | } 71 | } 72 | potentialMoves.push(northMoves); 73 | potentialMoves.push(southMoves); 74 | potentialMoves.push(eastMoves); 75 | potentialMoves.push(westMoves); 76 | return potentialMoves; 77 | } 78 | 79 | Rook.prototype.getLegalMoves = function getLegalMoves(currentPosition) { 80 | var legalMoves = []; 81 | var allMoves = this.getPotentialMoves(currentPosition); 82 | 83 | for(var i = 0; i < allMoves.length; i++) { 84 | var currVectorMoves = allMoves[i]; 85 | for(var j = 0; j < currVectorMoves.length; j++) { 86 | if (game.board.occupiedBy(currVectorMoves[j]) === null) { 87 | legalMoves.push(currVectorMoves[j]); 88 | } else if (game.board.occupiedBy(currVectorMoves[j]) !== this.color) { 89 | legalMoves.push(currVectorMoves[j]); 90 | break; 91 | } else{ 92 | break; 93 | } 94 | } 95 | } 96 | return legalMoves; 97 | } 98 | 99 | Rook.prototype.getAttacks = function getAttacks(currentPosition) { 100 | var legalMoves = []; 101 | var allMoves = this.getPotentialMoves(currentPosition); 102 | 103 | for(var i = 0; i < allMoves.length; i++) { 104 | var currVectorMoves = allMoves[i]; 105 | for(var j = 0; j < currVectorMoves.length; j++) { 106 | if (game.board.occupiedBy(currVectorMoves[j]) === null) { 107 | legalMoves.push(currVectorMoves[j]); 108 | } else if (game.board.occupiedBy(currVectorMoves[j]) !== this.color) { 109 | legalMoves.push(currVectorMoves[j]); 110 | break; 111 | } else{ 112 | legalMoves.push(currVectorMoves[j]); 113 | break; 114 | } 115 | } 116 | } 117 | return legalMoves; 118 | } 119 | 120 | Rook.prototype.cloneSelf = function cloneSelf() { 121 | var selfClone = new Rook(this.color); 122 | selfClone.color = this.color; 123 | selfClone.captured = this.captured; 124 | selfClone.image = this.image; 125 | selfClone.hasMoved = this.hasMoved; 126 | selfClone.material = this.material; 127 | selfClone.type = this.type; 128 | selfClone.symbol = this.symbol; 129 | return selfClone; 130 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chess Scouter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |

LogoChess Scouter

33 |

34 |
35 | 36 |
37 |

Move History

38 | 39 | 40 | 41 |
42 |
43 |
44 | 45 | 46 | 47 | 48 |
49 |

What would you like to promote your pawn to?

50 | 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
abcdefgh
70 |
71 |
72 |

Visualizations

73 |

74 |

75 |

76 |

77 |
78 | 79 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Js/Pawn.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function Pawn(color) { 3 | this.color = color; 4 | this.captured = false; 5 | this.hasMoved = false; 6 | this.image = color + "_pawn.svg"; 7 | this.material = 1; 8 | this.type = "pawn"; 9 | this.symbol = ""; 10 | this.movedDouble = false; 11 | } 12 | 13 | Pawn.prototype.getPosition = function getPosition() { 14 | for (var i = 0; i < game.board.grid.length; i++) { 15 | for (var j = 0; j < game.board.grid[i].length; j++) { 16 | if (game.board.grid[j][i].piece === this) { 17 | return new Position(j, i); 18 | } 19 | } 20 | } 21 | return new Position(this.x, this.y); 22 | } 23 | 24 | Pawn.prototype.getPotentialMoves = function getPotentialMoves(position) { 25 | var x = position.x; 26 | var y = position.y; 27 | if (this.color === "white") { 28 | var potentialMoves = [new Position(x, y - 1)]; 29 | if (!this.hasMoved) { 30 | potentialMoves.push(new Position(x, y - 2)); 31 | } 32 | potentialMoves.push(new Position(x - 1, y - 1)); 33 | potentialMoves.push(new Position(x + 1, y - 1)); 34 | } else{ 35 | var potentialMoves = [new Position(x, y + 1)]; 36 | if (!this.hasMoved) { 37 | potentialMoves.push(new Position(x, y + 2)); 38 | } 39 | potentialMoves.push(new Position(x - 1, y + 1)); 40 | potentialMoves.push(new Position(x + 1, y + 1)); 41 | } 42 | return potentialMoves; 43 | } 44 | 45 | Pawn.prototype.getLegalMoves = function getLegalMoves(currentPosition) { 46 | var potentialMoves = []; 47 | var x = currentPosition.x; 48 | var y = currentPosition.y; 49 | if (this.color === "white") { 50 | if (game.board.occupiedBy(new Position(x, y - 1)) === null) { 51 | potentialMoves.push(new Position(x, y - 1)); 52 | if (!this.hasMoved && game.board.occupiedBy(new Position(x, y - 2)) === null) { 53 | potentialMoves.push(new Position(x, y - 2)); 54 | } 55 | } 56 | if (game.board.occupiedBy(new Position(x - 1, y - 1)) === "black") { 57 | potentialMoves.push(new Position(x - 1, y - 1)); 58 | } 59 | if (game.board.occupiedBy(new Position(x + 1, y - 1)) === "black") { 60 | potentialMoves.push(new Position(x + 1, y - 1)); 61 | } 62 | if (y === 3 && game.board.lastPiece instanceof Pawn && game.board.lastPiece.getPosition().y === 3 && game.board.lastPiece.movedDouble) { 63 | if (game.board.lastPiece.getPosition().x === x - 1) { 64 | potentialMoves.push(new Position(x - 1, y - 1)); 65 | } 66 | else if (game.board.lastPiece.getPosition().x === this.getPosition().x + 1) { 67 | potentialMoves.push(new Position(x + 1, y - 1)); 68 | } 69 | } 70 | } else { 71 | if (game.board.occupiedBy(new Position(x, y + 1)) === null) { 72 | potentialMoves.push(new Position(x, y + 1)); 73 | if (!this.hasMoved && game.board.occupiedBy(new Position(x, y + 2)) === null) { 74 | potentialMoves.push(new Position(x, y + 2)); 75 | } 76 | } 77 | if (game.board.occupiedBy(new Position(x - 1, y + 1)) === "white") { 78 | potentialMoves.push(new Position(x - 1, y + 1)); 79 | } 80 | if (game.board.occupiedBy(new Position(x + 1, y + 1)) === "white") { 81 | potentialMoves.push(new Position(x + 1, y + 1)); 82 | } 83 | if (y === 4 && game.board.lastPiece instanceof Pawn && game.board.lastPiece.getPosition().y === 4 && game.board.lastPiece.movedDouble) { 84 | if (game.board.getPosition(game.board.lastPiece).x === x - 1) { 85 | potentialMoves.push(new Position(x - 1, y + 1)); 86 | } 87 | else if (game.board.lastPiece.getPosition().x === this.getPosition().x + 1) { 88 | potentialMoves.push(new Position(x + 1, y + 1)); 89 | } 90 | } 91 | } 92 | for(var i = potentialMoves.length - 1; i >= 0; i--) { 93 | if (!game.board.isOnBoard(potentialMoves[i])) { 94 | potentialMoves.splice(i, 1); 95 | } 96 | } 97 | return potentialMoves; 98 | } 99 | 100 | Pawn.prototype.getAttacks = function getAttacks(currentPosition) { 101 | var attacks = []; 102 | var x = currentPosition.x; 103 | var y = currentPosition.y; 104 | if (this.color === "white") { 105 | attacks.push(new Position(x - 1, y - 1)); 106 | attacks.push(new Position(x + 1, y - 1)); 107 | } else{ 108 | attacks.push(new Position(x - 1, y + 1)); 109 | attacks.push(new Position(x + 1, y + 1)); 110 | } 111 | for(var i = attacks.length - 1; i >= 0; i--) { 112 | if (!game.board.isOnBoard(attacks[i])) { 113 | attacks.splice(i, 1); 114 | } 115 | } 116 | return attacks; 117 | } 118 | 119 | Pawn.prototype.cloneSelf = function cloneSelf() { 120 | var selfClone = new Pawn(this.color); 121 | selfClone.color = this.color; 122 | selfClone.captured = this.captured; 123 | selfClone.image = this.image; 124 | selfClone.hasMoved = this.hasMoved; 125 | selfClone.material = this.material; 126 | selfClone.type = this.type; 127 | selfClone.symbol = this.symbol; 128 | return selfClone; 129 | }; -------------------------------------------------------------------------------- /Js/Bishop.js: -------------------------------------------------------------------------------- 1 | //This file controls the behavior of the bishop pieces on the board. 2 | "use strict"; 3 | function Bishop(color) { 4 | this.color = color; 5 | this.captured = false; 6 | this.image = color + "_bishop.svg"; 7 | this.material = 3; 8 | this.type = "bishop"; 9 | this.symbol = "B"; 10 | } 11 | 12 | Bishop.prototype.getPosition = function getPosition() { 13 | for (var i = 0; i < game.board.grid.length; i++) { 14 | for (var j = 0; j < game.board.grid[i].length; j++) { 15 | if (game.board.grid[j][i].piece === this) { 16 | return new Position(j, i); 17 | } 18 | } 19 | } 20 | return new Position(this.x, this.y); 21 | } 22 | 23 | //This method records the possible moves the piece can make and inserts them into number of arrays for easy future acess. 24 | Bishop.prototype.getPotentialMoves = function getPotentialMoves(postion) { 25 | var x = postion.x; 26 | var y = postion.y; 27 | var potentialMoves = []; 28 | var vectorNorthEast = false; 29 | var vectorSouthEast = false; 30 | var vectorNorthWest = false; 31 | var vectorSouthWest = false; 32 | var addX = 0; 33 | var addY = 0; 34 | var northEastMoves = []; 35 | var southEastMoves = []; 36 | var northWestMoves = []; 37 | var southWestMoves = []; 38 | while(vectorNorthEast === false || vectorSouthEast === false || vectorNorthWest === false || vectorSouthWest === false) { 39 | //North: Y-- South: Y++ East: X-- West: X++ 40 | if (vectorNorthEast === false) { 41 | addY--; 42 | addX--; 43 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 44 | northEastMoves.push(new Position (x + addX, y + addY)); 45 | } else { 46 | addY = 0; 47 | addX = 0; 48 | vectorNorthEast = true; 49 | } 50 | } else if (vectorSouthEast === false) { 51 | addY++; 52 | addX--; 53 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 54 | southEastMoves.push(new Position (x + addX, y + addY)); 55 | } else { 56 | addY = 0; 57 | addX = 0; 58 | vectorSouthEast = true; 59 | } 60 | } else if (vectorNorthWest === false) { 61 | addY--; 62 | addX++; 63 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 64 | northWestMoves.push(new Position (x + addX, y + addY)); 65 | } else { 66 | addX = 0; 67 | addY = 0; 68 | vectorNorthWest = true; 69 | } 70 | } else if (vectorSouthWest === false) { 71 | addX++; 72 | addY++; 73 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 74 | southWestMoves.push(new Position (x + addX, y + addY)); 75 | } else { 76 | addX = 0; 77 | vectorSouthWest = true; 78 | } 79 | } 80 | } 81 | potentialMoves.push(northEastMoves); 82 | potentialMoves.push(southEastMoves); 83 | potentialMoves.push(northWestMoves); 84 | potentialMoves.push(southWestMoves); 85 | return potentialMoves; 86 | } 87 | 88 | //This method filters poential moves to produce legal moves. 89 | Bishop.prototype.getLegalMoves = function getLegalMoves(currentPosition) { 90 | var legalMoves = []; 91 | var allMoves = this.getPotentialMoves(currentPosition); 92 | 93 | for(var i = 0; i < allMoves.length; i++) { 94 | var currVectorMoves = allMoves[i]; 95 | for(var j = 0; j < currVectorMoves.length; j++) { 96 | if (game.board.occupiedBy(currVectorMoves[j]) === null) { 97 | legalMoves.push(currVectorMoves[j]); 98 | } else if (game.board.occupiedBy(currVectorMoves[j]) !== this.color) { 99 | legalMoves.push(currVectorMoves[j]); 100 | break; 101 | } else{ 102 | break; 103 | } 104 | } 105 | } 106 | return legalMoves; 107 | } 108 | 109 | //This method identifies where the piece can legally attack another piece. 110 | Bishop.prototype.getAttacks = function getAttacks(currentPosition) { 111 | var legalMoves = []; 112 | var allMoves = this.getPotentialMoves(currentPosition) 113 | for(var i = 0; i < allMoves.length; i++) { 114 | var currVectorMoves = allMoves[i]; 115 | for(var j = 0; j < currVectorMoves.length; j++) { 116 | if (game.board.occupiedBy(currVectorMoves[j]) === null) { 117 | legalMoves.push(currVectorMoves[j]); 118 | } else if (game.board.occupiedBy(currVectorMoves[j]) !== this.color) { 119 | legalMoves.push(currVectorMoves[j]); 120 | break; 121 | } else{ 122 | legalMoves.push(currVectorMoves[j]); 123 | break; 124 | } 125 | } 126 | } 127 | return legalMoves; 128 | } 129 | 130 | Bishop.prototype.cloneSelf = function cloneSelf() { 131 | var selfClone = new Bishop(this.color); 132 | selfClone.color = this.color; 133 | selfClone.captured = this.captured; 134 | selfClone.image = this.image; 135 | selfClone.hasMoved = this.hasMoved; 136 | selfClone.material = this.material; 137 | selfClone.type = this.type; 138 | selfClone.symbol = this.symbol; 139 | return selfClone; 140 | }; -------------------------------------------------------------------------------- /Js/King.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function King(color) { 3 | this.color = color; 4 | this.captured = false; 5 | this.image = color + "_king.svg"; 6 | this.hasMoved = false; 7 | this.material = 100; 8 | this.type = "king"; 9 | this.symbol = "K"; 10 | } 11 | 12 | King.prototype.getPosition = function getPosition() { 13 | for (var i = 0; i < game.board.grid.length; i++) { 14 | for (var j = 0; j < game.board.grid[i].length; j++) { 15 | if (game.board.grid[j][i].piece === this) { 16 | return new Position(j, i); 17 | } 18 | } 19 | } 20 | return new Position(this.x, this.y); 21 | } 22 | 23 | King.prototype.getPotentialMoves = function getPotentialMoves(startPosition) { 24 | var x = startPosition.x; 25 | var y = startPosition.y; 26 | var potentialMoves = [ 27 | new Position(x, y + 1), 28 | new Position(x, y - 1), 29 | new Position(x + 1, y), 30 | new Position(x - 1, y), 31 | new Position(x + 1, y + 1), 32 | new Position(x + 1, y - 1), 33 | new Position(x - 1, y + 1), 34 | new Position(x - 1, y - 1) 35 | ]; 36 | for(var i = potentialMoves.length - 1; i >= 0; i--) { 37 | if (!game.board.isOnBoard(potentialMoves[i])) { 38 | potentialMoves.splice(i, 1); 39 | } 40 | } 41 | return potentialMoves; 42 | } 43 | 44 | King.prototype.getLegalMoves = function getLegalMoves(currentPosition) { 45 | var legalMoves = this.getPotentialMoves(currentPosition); 46 | 47 | for(var i = legalMoves.length - 1; i >= 0; i--) { 48 | if (game.board.occupiedBy(new Position(legalMoves[i].x, legalMoves[i].y)) === this.color) { 49 | legalMoves.splice(i, 1); 50 | } 51 | } 52 | 53 | //Castling logic 54 | if (this.color === "white" && !game.isInCheck("white") && !this.hasMoved) { 55 | if ( 56 | game.board.grid[5][7].piece === null && 57 | game.board.grid[6][7].piece === null && 58 | game.board.grid[7][7].piece !== null && 59 | !game.board.grid[7][7].piece.hasMoved && 60 | game.board.grid[7][7].piece.image === "white_rook.svg" 61 | ) { 62 | var throughCheck = false; 63 | var otherAttacks = game.getAllLegalAttacks("black"); 64 | for(var i = 0; i < otherAttacks.length; i++) { 65 | if ((otherAttacks[i].x === 5 && otherAttacks[i].y === 7) || (otherAttacks[i].x === 6 && otherAttacks[i].y === 7)) { 66 | throughCheck = true; 67 | } 68 | } 69 | if (!throughCheck) { 70 | legalMoves.push(new Position(6, 7)); 71 | } 72 | } 73 | if ( 74 | game.board.grid[1][7].piece === null && 75 | game.board.grid[2][7].piece === null && 76 | game.board.grid[3][7].piece === null && 77 | game.board.grid[0][7].piece !== null && 78 | !game.board.grid[0][7].piece.hasMoved && 79 | game.board.grid[0][7].piece.image === "white_rook.svg" 80 | ) { 81 | var throughCheck = false; 82 | var otherAttacks = game.getAllLegalAttacks("black"); 83 | for(var i = 0; i < otherAttacks.length; i++) { 84 | if ((otherAttacks[i].x === 1 && otherAttacks[i].y === 7) || (otherAttacks[i].x === 2 && otherAttacks[i].y === 7) || (otherAttacks[i].x === 3 && otherAttacks[i].y === 7)) { 85 | throughCheck = true; 86 | } 87 | } 88 | if (!throughCheck) { 89 | legalMoves.push(new Position(2, 7)); 90 | } 91 | } 92 | } else if (this.color === "black" && !game.isInCheck("black") && !this.hasMoved) { 93 | if ( 94 | game.board.grid[5][0].piece === null && 95 | game.board.grid[6][0].piece === null && 96 | game.board.grid[7][0].piece !== null && 97 | !game.board.grid[7][0].piece.hasMoved && 98 | game.board.grid[7][0].piece.image === "black_rook.svg" 99 | ) { 100 | var throughCheck = false; 101 | var otherAttacks = game.getAllLegalAttacks("white"); 102 | for(var i = 0; i < otherAttacks.length; i++) { 103 | if ((otherAttacks[i].x === 5 && otherAttacks[i].y === 0) || (otherAttacks[i].x === 6 && otherAttacks[i].y === 0)) { 104 | throughCheck = true; 105 | } 106 | } 107 | if (!throughCheck) { 108 | legalMoves.push(new Position(6, 0)); 109 | } 110 | } 111 | if ( 112 | game.board.grid[1][0].piece === null && 113 | game.board.grid[2][0].piece === null && 114 | game.board.grid[3][0].piece === null && 115 | game.board.grid[0][0].piece !== null && 116 | !game.board.grid[0][0].piece.hasMoved && 117 | game.board.grid[0][0].piece.image === "black_rook.svg" 118 | ) { 119 | var throughCheck = false; 120 | var otherAttacks = game.getAllLegalAttacks("white"); 121 | for(var i = 0; i < otherAttacks.length; i++) { 122 | if ((otherAttacks[i].x === 1 && otherAttacks[i].y === 0) || (otherAttacks[i].x === 2 && otherAttacks[i].y === 0) || (otherAttacks[i].x === 3 && otherAttacks[i].y === 0)) { 123 | throughCheck = true; 124 | } 125 | } 126 | if (!throughCheck) { 127 | legalMoves.push(new Position(2, 0)); 128 | } 129 | } 130 | } 131 | return legalMoves; 132 | } 133 | 134 | King.prototype.getAttacks = function getAttacks(currentPosition) { 135 | return this.getPotentialMoves(currentPosition); 136 | } 137 | 138 | King.prototype.cloneSelf = function cloneSelf() { 139 | var selfClone = new King(this.color); 140 | selfClone.color = this.color; 141 | selfClone.captured = this.captured; 142 | selfClone.image = this.image; 143 | selfClone.hasMoved = this.hasMoved; 144 | selfClone.material = this.material; 145 | selfClone.type = this.type; 146 | selfClone.symbol = this.symbol; 147 | return selfClone; 148 | }; -------------------------------------------------------------------------------- /Js/Queen.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function Queen(color) { 3 | this.color = color; 4 | this.captured = false; 5 | this.image = color + "_queen.svg"; 6 | this.material = 9; 7 | this.type = "queen"; 8 | this.symbol = "Q"; 9 | } 10 | 11 | Queen.prototype.getPosition = function getPosition() { 12 | for (var i = 0; i < game.board.grid.length; i++) { 13 | for (var j = 0; j < game.board.grid[i].length; j++) { 14 | if (game.board.grid[j][i].piece === this) { 15 | return new Position(j, i); 16 | } 17 | } 18 | } 19 | return new Position(this.x, this.y); 20 | } 21 | 22 | Queen.prototype.getPotentialMoves = function getPotentialMoves(position) { 23 | var x = position.x; 24 | var y = position.y; 25 | var potentialMoves = []; 26 | var vectorNorth = false; 27 | var vectorSouth = false; 28 | var vectorEast = false; 29 | var vectorWest = false; 30 | var vectorNorthEast = false; 31 | var vectorSouthEast = false; 32 | var vectorNorthWest = false; 33 | var vectorSouthWest = false; 34 | var addX = 0 35 | var addY = 0 36 | var northMoves = []; 37 | var southMoves = []; 38 | var eastMoves = []; 39 | var westMoves = []; 40 | var northEastMoves = []; 41 | var southEastMoves = []; 42 | var northWestMoves = []; 43 | var southWestMoves = []; 44 | while(vectorNorth === false || vectorSouth === false || vectorEast === false || vectorWest === false || vectorNorthEast === false || vectorSouthEast === false || vectorNorthWest === false || vectorSouthWest === false) { 45 | if (vectorNorth === false) { 46 | addY--; 47 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 48 | northMoves.push(new Position (x + addX, y + addY)); 49 | } else { 50 | addY = 0; 51 | vectorNorth = true; 52 | } 53 | } else if (vectorSouth === false) { 54 | addY++; 55 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 56 | southMoves.push(new Position (x + addX, y + addY)); 57 | } else { 58 | addY = 0; 59 | vectorSouth = true; 60 | } 61 | } else if (vectorEast === false) { 62 | addX--; 63 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 64 | eastMoves.push(new Position (x + addX, y + addY)); 65 | } else { 66 | addX = 0; 67 | vectorEast = true; 68 | } 69 | } else if (vectorWest === false) { 70 | addX++; 71 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 72 | westMoves.push(new Position (x + addX, y + addY)); 73 | } else { 74 | addX = 0; 75 | vectorWest = true; 76 | } 77 | } else if (vectorNorthEast === false) { 78 | addY--; 79 | addX--; 80 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 81 | northEastMoves.push(new Position (x + addX, y + addY)); 82 | } else { 83 | addY = 0; 84 | addX = 0; 85 | vectorNorthEast = true; 86 | } 87 | } else if (vectorSouthEast === false) { 88 | addY++; 89 | addX--; 90 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 91 | southEastMoves.push(new Position (x + addX, y + addY)); 92 | } else { 93 | addY = 0; 94 | addX = 0; 95 | vectorSouthEast = true; 96 | } 97 | } else if (vectorNorthWest === false) { 98 | addY--; 99 | addX++; 100 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 101 | northWestMoves.push(new Position (x + addX, y + addY)); 102 | } else { 103 | addX = 0; 104 | addY = 0; 105 | vectorNorthWest = true; 106 | } 107 | } else if (vectorSouthWest === false) { 108 | addX++; 109 | addY++; 110 | if (game.board.isOnBoard(new Position(x + addX,y + addY))) { 111 | southWestMoves.push(new Position (x + addX, y + addY)); 112 | } else { 113 | addX = 0; 114 | vectorSouthWest = true; 115 | } 116 | } 117 | } 118 | potentialMoves.push(northMoves); 119 | potentialMoves.push(southMoves); 120 | potentialMoves.push(eastMoves); 121 | potentialMoves.push(westMoves); 122 | potentialMoves.push(northEastMoves); 123 | potentialMoves.push(southEastMoves); 124 | potentialMoves.push(northWestMoves); 125 | potentialMoves.push(southWestMoves); 126 | return potentialMoves; 127 | } 128 | 129 | Queen.prototype.getLegalMoves = function getLegalMoves(currentPosition) { 130 | var legalMoves = []; 131 | var allMoves = this.getPotentialMoves(currentPosition); 132 | 133 | for(var i = 0; i < allMoves.length; i++) { 134 | var currVectorMoves = allMoves[i]; 135 | for(var j = 0; j < currVectorMoves.length; j++) { 136 | if (game.board.occupiedBy(currVectorMoves[j]) === null) { 137 | legalMoves.push(currVectorMoves[j]); 138 | } else if (game.board.occupiedBy(currVectorMoves[j]) !== this.color) { 139 | legalMoves.push(currVectorMoves[j]); 140 | break; 141 | } else{ 142 | break; 143 | } 144 | } 145 | } 146 | return legalMoves; 147 | } 148 | 149 | Queen.prototype.getAttacks = function getAttacks(currentPosition) { 150 | var legalMoves = []; 151 | var allMoves = this.getPotentialMoves(currentPosition) 152 | for(var i = 0; i < allMoves.length; i++) { 153 | var currVectorMoves = allMoves[i]; 154 | for(var j = 0; j < currVectorMoves.length; j++) { 155 | if (game.board.occupiedBy(currVectorMoves[j]) === null) { 156 | legalMoves.push(currVectorMoves[j]); 157 | } else if (game.board.occupiedBy(currVectorMoves[j]) !== this.color) { 158 | legalMoves.push(currVectorMoves[j]); 159 | break; 160 | } else{ 161 | legalMoves.push(currVectorMoves[j]); 162 | break; 163 | } 164 | } 165 | } 166 | return legalMoves; 167 | } 168 | 169 | Queen.prototype.cloneSelf = function cloneSelf() { 170 | var selfClone = new Queen(this.color); 171 | selfClone.color = this.color; 172 | selfClone.captured = this.captured; 173 | selfClone.image = this.image; 174 | selfClone.hasMoved = this.hasMoved; 175 | selfClone.material = this.material; 176 | selfClone.type = this.type; 177 | selfClone.symbol = this.symbol; 178 | return selfClone; 179 | }; -------------------------------------------------------------------------------- /Js/UI.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is the most global function and is what binds the logic and objects to 3 | DOM elements. 4 | */ 5 | "use strict"; 6 | var BOARD_SIZE = 400; 7 | var firstClick = null; 8 | var secondClick = null; 9 | var game; 10 | 11 | $(function() { 12 | game = new Game(); 13 | game.board.sumSquareControl(); 14 | layoutBoard(); 15 | // $("#turnSpace").text("It is " + game.whoseTurn() + "'s turn"); 16 | var temp = $("#board"); 17 | $("#promotion").hide(); 18 | //Create a seperate method for first click and then another for second click. 19 | //TODO just pass legal moves to movePiece so you don't need to check twice. 20 | $("#board").on("click", "td", boardClicks); 21 | $(".help").click(alertHelp); 22 | $("#moveHistory").on("click", "button", downloadPgn); 23 | $("#filters").on("click", "input", function() { 24 | layoutBoard(); 25 | }) 26 | 27 | //Watch for ctrl + z for undo 28 | var ctrlDown = false; 29 | var ctrlKey = 17, zKey = 90, yKey = 89; 30 | 31 | $(document).keydown(function(e) { 32 | if (e.keyCode === ctrlKey) ctrlDown = true; 33 | }).keyup(function(e){ 34 | if (e.keyCode === ctrlKey) ctrlDown = false; 35 | }); 36 | 37 | $(document).keydown(function(e){ 38 | if (ctrlDown && (e.keyCode === zKey)){ 39 | game.board.undoMove(); 40 | } 41 | }); 42 | }); 43 | 44 | function downloadPgn () { 45 | var pgn = ""; 46 | for (var i = 0; i < $("#moveList").children().length; i++) { 47 | for(var j = 0; j < $("#moveList").children()[i].cells.length; j++){ 48 | pgn += $("#moveList").children()[i].cells[j].textContent + " "; 49 | } 50 | } 51 | 52 | if (pgn != "") { 53 | var b = new Blob([pgn], {type: 'pgn'}); 54 | var fileURL = URL.createObjectURL(b); 55 | 56 | var a = document.createElement('a'), 57 | e = document.createEvent("MouseEvents"); // simulated click 58 | e.initMouseEvent("click", true, false, self, 0, 0, 0, 0, 0, false, false, false, false, 0, null); 59 | a.setAttribute('href', fileURL); 60 | a.setAttribute('target', '_blank'); // fallback behaviour 61 | a.setAttribute('download', 'ChessScouterGame.pgn'); // file name 62 | a.dispatchEvent(e); // download 63 | } else { 64 | alert("PGN has no contents yet. Make some moves and try again"); 65 | } 66 | } 67 | 68 | function alertHelp() { 69 | var text = $(this).context.parentNode.textContent; 70 | var option = $(this).attr("id"); 71 | $.getJSON("Js/Messages.json", function(json) { 72 | alert(json[option]); 73 | }); 74 | } 75 | 76 | function movePiece(first, second) { 77 | var firstX = first.x; 78 | var firstY = first.y; 79 | if (game.board.getPiece(firstX, firstY)) { 80 | firstClick = null; 81 | } 82 | var piece = game.board.getPiece(firstX, firstY); 83 | if (game.whoseTurn() === piece.color) { 84 | var secondX = second.x; 85 | var secondY = second.y; 86 | game.board.movePiece(new Position(firstX, firstY), new Position(secondX, secondY)); 87 | } else { 88 | messageUser("It's not your turn!"); 89 | } 90 | } 91 | 92 | function boardClicks() { 93 | var firstX = $(this).context.cellIndex; 94 | var firstY = $(this).context.parentNode.rowIndex; 95 | if (firstClick === null && game.board.getPiece(firstX, firstY) !== null && game.board.getPiece(firstX, firstY).color === game.whoseTurn()) { 96 | firstClick = $(this); 97 | firstClick = new Position(firstX, firstY); 98 | var piece = game.board.getPiece(firstX, firstY); 99 | var legalMoves = game.pieceLegalMoves(new Position(firstX, firstY)); 100 | if ($("#legalMoves").attr("checked") != undefined) { 101 | for (var i = 0; i < legalMoves.length; i++) { 102 | game.board.grid[legalMoves[i].x][legalMoves[i].y].legalMove = true; 103 | } 104 | } 105 | layoutBoard(); 106 | getTableData(firstX, firstY).css("border-color", "blue"); 107 | } else if (firstClick !== null && game.board.getPiece(firstX, firstY) && game.board.getPiece(firstX, firstY).color === game.whoseTurn() && (firstClick.x !== firstX || firstClick.y !== firstY)) { 108 | var oldLegalMoves = game.pieceLegalMoves(new Position(firstClick.x, firstClick.y)); 109 | for (var i = 0; i < oldLegalMoves.length; i++) { 110 | game.board.grid[oldLegalMoves[i].x][oldLegalMoves[i].y].legalMove = false; 111 | } 112 | layoutBoard(); 113 | 114 | firstClick = $(this); 115 | firstClick = new Position(firstX, firstY); 116 | var piece = game.board.getPiece(firstX, firstY); 117 | var legalMoves = game.pieceLegalMoves(new Position(firstX, firstY)); 118 | if ($("#legalMoves").attr("checked") != undefined) { 119 | for (var i = 0; i < legalMoves.length; i++) { 120 | game.board.grid[legalMoves[i].x][legalMoves[i].y].legalMove = true; 121 | } 122 | } 123 | layoutBoard(); 124 | getTableData(firstX, firstY).css("border-color", "blue"); 125 | } else if (firstClick !== null) { 126 | secondClick = new Position($(this).context.cellIndex, $(this).context.parentNode.rowIndex); 127 | movePiece(firstClick, secondClick); 128 | firstClick = null; 129 | secondClick = null; 130 | layoutBoard(); 131 | } else if (game.board.getPiece(firstX, firstY) && game.board.getPiece(firstX, firstY).color !== game.whoseTurn()) { 132 | messageUser("It's not your turn!"); 133 | } 134 | } 135 | 136 | // 'Update' function updates the visual state of the board 137 | function layoutBoard() { 138 | // $("#turnSpace").text("It is " + game.whoseTurn() + "'s turn"); 139 | $("#board").empty(); 140 | for (var i = 0; i < game.board.grid.length; i++) { 141 | $("").attr("id", "tr" + i).addClass("gridLabel").appendTo($("#board")); 142 | $("
").text(8 - i).css("vertical-align", "text-middle").css("line-height", BOARD_SIZE / 8 + 24 + "px").appendTo($("#tr" + i)); 143 | for (var j = 0; j < game.board.grid[i].length; j++) { 144 | $("") 145 | .addClass(occupied(j, i)) 146 | .attr("id", j + "-" + i) 147 | .css("color", "white") 148 | .css("width", BOARD_SIZE / 8) 149 | .css("height", BOARD_SIZE / 8) 150 | .css("background-position", "center") 151 | .css("background-repeat", "no-repeat") 152 | .appendTo("#tr" + i) 153 | .css("background-image", getBackgroundImageString(new Position(j, i))); 154 | } 155 | } 156 | if ($("#space").attr("checked") != undefined) { 157 | showSpaceControl(); 158 | } 159 | if ($("#pieceFlair").attr("checked") != undefined) { 160 | showPieceFlair(); 161 | } 162 | showLastMove(); 163 | } 164 | 165 | function showPieceFlair() { 166 | for (var i = 0; i < game.board.grid.length; i++) { 167 | for (var j = 0; j < game.board.grid[i].length; j++) { 168 | if (game.board.grid[i][j].flair === true) { 169 | $(getTableData(i, j)).addClass("pieceFlair"); 170 | } 171 | } 172 | } 173 | } 174 | 175 | function showSpaceControl() { 176 | for (var i = 0; i < 8; i++) { 177 | for (var j = 0; j < 8; j++) { 178 | var square = getTableData(i, j); 179 | if (!game.board.grid[i][j].whiteControl && !game.board.grid[i][j].blackControl) { 180 | var control = null; 181 | } else { 182 | var control = game.board.grid[i][j].whiteControl - game.board.grid[i][j].blackControl; 183 | } 184 | var color = calculateColor(control); 185 | square.css("background-color", color); 186 | } 187 | } 188 | } 189 | 190 | function showLastMove() { 191 | if (game.board.previousMove){ 192 | $(getTableData(game.board.previousMove.x, game.board.previousMove.y)).attr("id", "previous"); 193 | } 194 | if (game.board.currentMove) { 195 | $(getTableData(game.board.currentMove.x, game.board.currentMove.y)).attr("id", "current"); 196 | } 197 | } 198 | 199 | function getBackgroundImageString(position) { 200 | var piece = ""; 201 | var dot = ""; 202 | var fork = ""; 203 | if (game.board.grid[position.x][position.y].legalMove) { 204 | dot = "url(Assets/blue_dot.svg)"; 205 | } 206 | if ($("#forks").attr("checked") != undefined && game.board.grid[position.x][position.y].fork === true) { 207 | if (dot === "") { 208 | fork = "url(Assets/white_fork.svg)"; 209 | } else { 210 | fork = ", url(Assets/white_fork.svg)"; 211 | } 212 | } 213 | if (game.board.getPiece(position.x, position.y) != null) { 214 | if (dot !== "" || fork !== "") { 215 | piece = ", url(Assets/" + game.board.getPiece(position.x, position.y).image + ")"; 216 | } else { 217 | piece = "url(Assets/" + game.board.getPiece(position.x, position.y).image + ")"; 218 | } 219 | } 220 | if (fork !== "") { 221 | var backgroundPosition = "bottom right"; 222 | if (dot !== "") { 223 | backgroundPosition = "center center, " + backgroundPosition; 224 | } 225 | if (piece !== "") { 226 | backgroundPosition += ", center center"; 227 | } 228 | getTableData(position.x, position.y).css("background-position", backgroundPosition); 229 | } 230 | return dot + fork + piece; 231 | } 232 | 233 | function showSimpleSpaceControl() { 234 | var blackAttacks = game.getAllLegalAttacks("black"); 235 | for (var i = 0; i < blackAttacks.length; i++) { 236 | getTableData(blackAttacks[i].x, blackAttacks[i].y).css("background-color", "rgb(255, 153, 153)"); 237 | } 238 | var whiteAttacks = game.getAllLegalAttacks("white"); 239 | for (var i = 0; i < whiteAttacks.length; i++) { 240 | if ($(getTableData(whiteAttacks[i].x, whiteAttacks[i].y)).css("background-color") === "rgb(255, 153, 153)" || $(getTableData(whiteAttacks[i].x, whiteAttacks[i].y)).css("background-color") === "rgb(240, 230, 140)") { 241 | getTableData(whiteAttacks[i].x, whiteAttacks[i].y).css("background-color", "rgb(240, 230, 140)"); 242 | } else { 243 | getTableData(whiteAttacks[i].x, whiteAttacks[i].y).css("background-color", "rgb(153, 235, 153)"); 244 | } 245 | } 246 | 247 | } 248 | 249 | function calculateColor (scale) { 250 | if (scale === 0) { 251 | return "rgb(240, 230, 140)"; 252 | } 253 | else if (scale === 1) { 254 | return "#CCF5CC"; 255 | } 256 | else if (scale === 2) { 257 | return "rgb(153, 235, 153)"; 258 | } 259 | else if (scale === 3) { 260 | return "#66E066"; 261 | } 262 | else if (scale === 4) { 263 | return "#33D633"; 264 | } 265 | else if (scale > 4) { 266 | return "#00CC00"; 267 | } 268 | else if (scale === -1) { 269 | return "#FFCCCC"; 270 | } 271 | else if (scale === -2) { 272 | return "rgb(255, 153, 153)"; 273 | } 274 | else if (scale === -3) { 275 | return "#FF6666"; 276 | } 277 | else if (scale === -4) { 278 | return "#FF3333"; 279 | } 280 | else if (scale < -4) { 281 | return "#FF0000"; 282 | } 283 | } 284 | 285 | // send a message to the user 286 | function messageUser(message) { 287 | if (message !== null) { 288 | var $messages = $('#messages'); 289 | $messages.empty(); 290 | var $p = $('').text(message); 291 | $messages.append($p); 292 | // $p.fadeOut(7000, function() { 293 | // $(this).remove(); 294 | // }); 295 | } 296 | } 297 | 298 | function getBackgroundColor(x, y) { 299 | if ((x % 2 === 0 && y % 2 === 0) || (x % 2 !== 0 && y % 2 !== 0)) { 300 | return "white"; 301 | } else { 302 | return "lightgrey"; 303 | } 304 | } 305 | 306 | function occupied(x, y) { 307 | return game.board.getPiece(x, y); 308 | } 309 | 310 | function getTableData(x, y) { 311 | return $("#" + x + "-" + y); 312 | } -------------------------------------------------------------------------------- /Js/Game.js: -------------------------------------------------------------------------------- 1 | //This object initializes a board and manages the state of the game. 2 | "use strict"; 3 | function Game() { 4 | this.turn = 1; 5 | this.board = new Board(); 6 | this.moveHistory = []; 7 | } 8 | 9 | Game.prototype.whoseTurn = function whoseTurn() { 10 | if (this.turn % 2 === 0) { 11 | return "black"; 12 | } else { 13 | return "white"; 14 | } 15 | } 16 | 17 | Game.prototype.otherTurn = function otherTurn() { 18 | if (this.turn % 2 === 0) { 19 | return "white"; 20 | } else { 21 | return "black"; 22 | } 23 | } 24 | 25 | Game.prototype.otherPlayer = function otherPlayer(player) { 26 | if (player === "white") { 27 | return "black"; 28 | } else { 29 | return "white" 30 | } 31 | } 32 | 33 | Game.prototype.getPieces = function getPieces(player) { 34 | var pieceList = []; 35 | try { 36 | if (player === "white" || player === "black" || player === "all") { 37 | for(var i = 0; i < this.board.grid.length; i++) { 38 | for(var j = 0; j < this.board.grid[i].length; j++) { 39 | var square = this.board.grid[i][j]; 40 | if (square.piece !== null) { 41 | if (square.piece.color === "white" && player === "white") { 42 | pieceList.push(this.board.grid[i][j].piece); 43 | } else if (square.piece.color === "black" && player === "black") { 44 | pieceList.push(this.board.grid[i][j].piece); 45 | } else if (player === "all") { 46 | pieceList.push(this.board.grid[i][j].piece); 47 | } 48 | } 49 | } 50 | } 51 | return pieceList; 52 | } else { 53 | throw new Error("bad input"); 54 | } 55 | } catch(err) { 56 | alert(err + "player must be \"black\" or \"white\" or \"all\""); 57 | } 58 | } 59 | 60 | //All pieces have a getAttacks method that is redundant with getLegalMoves. This is because Pawn moves 61 | //differently from how it attacks. For now it's easier but we should get inheritance working so it's less 62 | //stupid. 63 | Game.prototype.getAllLegalAttacks = function getAllLegalAttacks(player) { 64 | try { 65 | var attacks = []; 66 | if (player === "white" || player === "black") { 67 | var pieces = this.getPieces(player); 68 | for(var i = 0; i < pieces.length; i++) { 69 | var pieceAttack = pieces[i].getAttacks(pieces[i].getPosition()); 70 | for(var j = 0; j < pieceAttack.length; j++) { 71 | attacks.push(pieceAttack[j]); 72 | } 73 | } 74 | return attacks; 75 | } else { 76 | throw "bad input"; 77 | } 78 | } catch(err) { 79 | alert(err + " player must be \"black\" or \"white\""); 80 | } 81 | } 82 | 83 | Game.prototype.getAllLegalMoves = function getAllLegalMoves(player) { 84 | try { 85 | var moves = []; 86 | if (player === "white" || player === "black") { 87 | var pieces = this.getPieces(player); 88 | for(var i = 0; i < pieces.length; i++) { 89 | var pieceMoves = this.pieceLegalMoves(pieces[i].getPosition()); 90 | for(var j = 0; j < pieceMoves.length; j++) { 91 | moves.push(pieceMoves[j]); 92 | } 93 | } 94 | return moves; 95 | } else { 96 | throw "bad input"; 97 | } 98 | } catch(err) { 99 | alert(err + " player must be \"black\" or \"white\""); 100 | } 101 | } 102 | 103 | //Add try catch behavior 104 | Game.prototype.isInCheck = function isInCheck(player) { 105 | var pieces = game.getPieces(player); 106 | var king = this.findKing(player); 107 | var enemyAttacks; 108 | if (player === "white") { 109 | enemyAttacks = this.getAllLegalAttacks("black"); 110 | } else { 111 | enemyAttacks = this.getAllLegalAttacks("white"); 112 | } 113 | for(var i = 0; i < enemyAttacks.length; i++) { 114 | if (enemyAttacks[i].x === king.getPosition().x && enemyAttacks[i].y === king.getPosition().y) { 115 | return true; 116 | break; 117 | } 118 | } 119 | } 120 | 121 | Game.prototype.findKing = function findKing(player) { 122 | var pieces = this.getPieces(player); 123 | var king = null; 124 | var kingFound = false; 125 | var counter = 0; 126 | // Loop over the board until the king is found 127 | while (!kingFound) { 128 | if (pieces[counter].image === player + "_king.svg") { 129 | king = pieces[counter]; 130 | kingFound = true; 131 | } 132 | counter++; 133 | } 134 | if (kingFound) { 135 | return king; 136 | } else { 137 | return null; 138 | } 139 | } 140 | 141 | Game.prototype.isInCheckmate = function isInCheckmate(player) { 142 | var pieces = this.getPieces(player); 143 | var legalMoves = []; 144 | for(var i = 0; i < pieces.length; i++) { 145 | var pieceMoves = this.pieceLegalMoves(pieces[i].getPosition()); 146 | for(var j = 0; j < pieceMoves.length; j++) { 147 | legalMoves.push(pieceMoves[i]); 148 | } 149 | } 150 | if (legalMoves.length === 0 && this.isInCheck(player)) { 151 | return true; 152 | } else { 153 | return false; 154 | } 155 | } 156 | 157 | Game.prototype.isInStalemate = function isInStalemate(player) { 158 | var pieces = this.getPieces(player); 159 | var legalMoves = []; 160 | for(var i = 0; i < pieces.length; i++) { 161 | var pieceMoves = this.pieceLegalMoves(pieces[i].getPosition()); 162 | for(var j = 0; j < pieceMoves.length; j++) { 163 | legalMoves.push(pieceMoves[i]); 164 | } 165 | } 166 | if ( 167 | (legalMoves.length === 0 && !this.isInCheck(player)) || 168 | ((this.getPieces("white").length === 1 && this.findKing(player) !== null) && (this.getPieces("black").length === 1 && this.findKing(player))) || 169 | ((this.getPieces("white").length === 1 && this.getPieces("white")[0] instanceof King) && (this.getPieces("black").length === 1 && this.getPieces("black")[0] instanceof King)) 170 | ) { 171 | return true; 172 | } else { 173 | return false; 174 | } 175 | } 176 | 177 | Game.prototype.insufficientMatingMaterial = function insufficientMatingMaterial() { 178 | var sufficient = true; 179 | var whitePieces = this.getPieces("white"); 180 | var blackPieces = this.getPieces("black"); 181 | var allPieces = this.getPieces("all"); 182 | 183 | if (allPieces.length > 4) { 184 | return false; 185 | } else { 186 | var whiteKnights = 0; 187 | var whiteBishops = 0; 188 | var blackKnights = 0; 189 | var blackBishops = 0; 190 | var whiteBishopSquareColor = undefined; 191 | var blackBishopSquareColor = undefined; 192 | for(var i = 0; i < allPieces.length; i++) { 193 | if (allPieces[i] instanceof Rook || allPieces[i] instanceof Queen || allPieces[i] instanceof Pawn) { 194 | return false; 195 | } else { 196 | var color = allPieces[i].color; 197 | var type = allPieces[i].type; 198 | 199 | switch(type) 200 | { 201 | case "bishop": 202 | if (color === "black") { 203 | blackBishops++; 204 | blackBishopSquareColor = allPieces[i].getPosition().x % 2 === allPieces[i].getPosition().y % 2; 205 | break; 206 | } else { 207 | whiteBishops++; 208 | whiteBishopSquareColor = allPieces[i].getPosition().x % 2 === allPieces[i].getPosition().y % 2; 209 | break; 210 | } 211 | case "knight": 212 | if (color === "black") { 213 | blackKnights++; 214 | } else { 215 | whiteKnights++; 216 | } 217 | break; 218 | case "king": 219 | break; 220 | } 221 | } 222 | } 223 | if ( 224 | (whiteKnights === 0 && whiteBishops === 0 && blackKnights === 0 && blackBishops === 0) || 225 | (whiteKnights === 1 && whiteBishops === 0 && blackKnights === 0 && blackBishops === 0) || 226 | (whiteKnights === 0 && whiteBishops === 1 && blackKnights === 0 && blackBishops === 0) || 227 | (whiteKnights === 0 && whiteBishops === 0 && blackKnights === 1 && blackBishops === 0) || 228 | (whiteKnights === 0 && whiteBishops === 0 && blackKnights === 0 && blackBishops === 1) || 229 | ((whiteKnights === 0 && whiteBishops === 1 && blackKnights === 0 && blackBishops === 1) && (blackBishopSquareColor === whiteBishopSquareColor)) 230 | ) { 231 | return true; 232 | } 233 | else { 234 | return false; 235 | } 236 | } 237 | } 238 | 239 | Game.prototype.pieceLegalMoves = function pieceLegalMoves(position) { 240 | var piece = game.board.grid[position.x][position.y].piece; 241 | var legalMoves = piece.getLegalMoves(position); 242 | for(var i = legalMoves.length - 1; i >= 0; i--) { 243 | var foreignContents = game.board.grid[legalMoves[i].x][legalMoves[i].y].piece; 244 | game.board.grid[position.x][position.y].piece = null; 245 | game.board.grid[legalMoves[i].x][legalMoves[i].y].piece = piece; 246 | if (this.isInCheck(game.whoseTurn())) { 247 | game.board.grid[position.x][position.y].piece = piece; 248 | game.board.grid[legalMoves[i].x][legalMoves[i].y].piece = foreignContents; 249 | legalMoves.splice(i, 1); 250 | } else { 251 | game.board.grid[position.x][position.y].piece = piece; 252 | game.board.grid[legalMoves[i].x][legalMoves[i].y].piece = foreignContents; 253 | } 254 | } 255 | return legalMoves; 256 | } 257 | 258 | Game.prototype.attackedPieces = function attackedPieces(player) { 259 | var initialAttacks = this.getAllLegalAttacks(this.otherPlayer(player)); 260 | for(var i = initialAttacks.length - 1; i >= 0; i--) { 261 | if (this.board.grid[initialAttacks[i].x][initialAttacks[i].y].piece === null || 262 | this.board.grid[initialAttacks[i].x][initialAttacks[i].y].piece.color === this.otherPlayer(player)) { 263 | initialAttacks.splice(i, 1); 264 | } 265 | } 266 | return initialAttacks; 267 | } 268 | 269 | Game.prototype.getWhiteForks = function getWhiteForks() { 270 | var forks = []; 271 | var pieces = this.getPieces("white"); 272 | for(var i = 0; i < pieces.length; i++) { 273 | var pieceMoves = pieces[i].getLegalMoves(pieces[i].getPosition()); 274 | for(var j = 0; j < pieceMoves.length; j++) { 275 | var initialAttacks = this.attackedPieces("black"); 276 | //Make the move but save information so move can be undone 277 | var piece = pieces[i]; 278 | var oldPosition = piece.getPosition(); 279 | var foreignContents = this.board.grid[pieceMoves[j].x][pieceMoves[j].y].piece; 280 | this.board.grid[pieceMoves[j].x][pieceMoves[j].y].piece = piece; 281 | this.board.grid[oldPosition.x][oldPosition.y].piece = null; 282 | 283 | var afterAttacks = this.attackedPieces("black"); 284 | for (var k = afterAttacks.length - 1; k >= 0; k--) { 285 | for (var l = initialAttacks.length - 1; l >= 0; l--) { 286 | if ((afterAttacks[k].x === initialAttacks[l].x) && (afterAttacks[k].y === initialAttacks[l].y)) { 287 | initialAttacks.splice(l, 1); 288 | afterAttacks.splice(k, 1); 289 | break; 290 | } 291 | } 292 | } 293 | 294 | //Check if move results in more than 2 pieces under attack 295 | if (afterAttacks.length >= 2) { 296 | forks.push(pieceMoves[j]); 297 | } 298 | this.board.grid[oldPosition.x][oldPosition.y].piece = piece; 299 | this.board.grid[pieceMoves[j].x][pieceMoves[j].y].piece = foreignContents; 300 | } 301 | } 302 | return forks; 303 | }; -------------------------------------------------------------------------------- /Js/Board.js: -------------------------------------------------------------------------------- 1 | //This object acts as a data structure that is made up of square that store all the pieces. 2 | //It has functions that can return information about the board. 3 | "use strict"; 4 | 5 | function Board() { 6 | this.promoPosition = null; 7 | this.lastPiece = null; 8 | 9 | var grid = []; 10 | 11 | for(var i = 0; i < 8; i++) { 12 | var row = []; 13 | for(var j = 0; j < 8; j++) { 14 | row[j] = new Square(i, j, null); 15 | } 16 | grid[i] = row; 17 | } 18 | 19 | grid[0][0] = new Square(0, 0, new Rook("black")); 20 | grid[1][0] = new Square(1, 0, new Knight("black")); 21 | grid[2][0] = new Square(2, 0, new Bishop("black")); 22 | grid[3][0] = new Square(3, 0, new Queen("black")); 23 | grid[4][0] = new Square(4, 0, new King("black")); 24 | grid[5][0] = new Square(5, 0, new Bishop("black")); 25 | grid[6][0] = new Square(6, 0, new Knight("black")); 26 | grid[7][0] = new Square(7, 0, new Rook("black")); 27 | 28 | grid[0][1] = new Square(0, 1, new Pawn("black")); 29 | grid[1][1] = new Square(1, 1, new Pawn("black")); 30 | grid[2][1] = new Square(2, 1, new Pawn("black")); 31 | grid[3][1] = new Square(3, 1, new Pawn("black")); 32 | grid[4][1] = new Square(4, 1, new Pawn("black")); 33 | grid[5][1] = new Square(5, 1, new Pawn("black")); 34 | grid[6][1] = new Square(6, 1, new Pawn("black")); 35 | grid[7][1] = new Square(7, 1, new Pawn("black")); 36 | 37 | grid[0][6] = new Square(0, 6, new Pawn("white")); 38 | grid[1][6] = new Square(1, 6, new Pawn("white")); 39 | grid[2][6] = new Square(2, 6, new Pawn("white")); 40 | grid[3][6] = new Square(3, 6, new Pawn("white")); 41 | grid[4][6] = new Square(4, 6, new Pawn("white")); 42 | grid[5][6] = new Square(5, 6, new Pawn("white")); 43 | grid[6][6] = new Square(6, 6, new Pawn("white")); 44 | grid[7][6] = new Square(7, 6, new Pawn("white")); 45 | 46 | grid[0][7] = new Square(0, 7, new Rook("white")); 47 | grid[1][7] = new Square(1, 7, new Knight("white")); 48 | grid[2][7] = new Square(2, 7, new Bishop("white")); 49 | grid[3][7] = new Square(3, 7, new Queen("white")); 50 | grid[4][7] = new Square(4, 7, new King("white")); 51 | grid[5][7] = new Square(5, 7, new Bishop("white")); 52 | grid[6][7] = new Square(6, 7, new Knight("white")); 53 | grid[7][7] = new Square(7, 7, new Rook("white")); 54 | 55 | for(var i = 2; i < 6; i++) { 56 | for(var j = 0; j < 8; j++) { 57 | grid[j][i] = new Square(j, i, null); 58 | } 59 | } 60 | this.grid = grid; 61 | } 62 | 63 | //Pass me a square's x and y coordinates and I'll return the piece on that square. Null if not any 64 | Board.prototype.getPiece = function getPiece(x, y) { 65 | if (this.isOnBoard(new Position(x, y))) { 66 | return this.grid[x][y].piece; 67 | } else { 68 | return null; 69 | } 70 | } 71 | 72 | //TODO If destination is enemy piece, don't null out, but set to captured and move out of the way. 73 | Board.prototype.movePiece = function movePiece(oldPosition, newPosition) { 74 | game.moveHistory.push(this.cloneBoard()); 75 | 76 | this.resetVisualData(); 77 | 78 | var oldX = oldPosition.x; 79 | var oldY = oldPosition.y; 80 | var newX = newPosition.x; 81 | var newY = newPosition.y; 82 | var message = null; 83 | var submitted = null; 84 | var capture = false; 85 | var piece = this.grid[oldX][oldY].piece; 86 | var initialAttacks = game.attackedPieces(game.otherTurn()); 87 | 88 | if (this.isLegalMove(oldPosition, newPosition)) { 89 | //Determine if move is a capture (needed for algebraic move history) 90 | if (this.grid[newX][newY].piece != null) { 91 | capture = true; 92 | } 93 | piece.hasMoved = true; 94 | piece.movedDouble = this.isMovingDouble(piece, oldPosition, newPosition); 95 | 96 | if (piece instanceof Pawn && !this.grid[newPosition.x][newPosition.y].piece) { 97 | //Check for enpassant. Removed piece is in a different spot than newPosition 98 | this.grid[oldX][oldY].piece = null; 99 | this.grid[newX][newY].piece = piece; 100 | this.grid[newX][oldY].piece = null; 101 | } else{ 102 | this.grid[oldX][oldY].piece = null; 103 | this.grid[newX][newY].piece = piece; 104 | } 105 | 106 | this.completeCastle(piece, oldPosition, newPosition); 107 | 108 | if ((newY === 0 || newY === 7) && this.grid[newX][newY].piece instanceof Pawn) { 109 | $("#promotion").show(); 110 | submitted = false; 111 | $("#board").off(); 112 | $("#submit").on("click", function() { 113 | if (this.promoPosition != null) { 114 | var pieceType = $("#promotionOptions").val(); 115 | if (pieceType === "Queen") { 116 | this.grid[this.promoPosition.x][this.promoPosition.y].piece = new Queen(game.whoseTurn()); 117 | } else if (pieceType === "Rook") { 118 | this.grid[this.promoPosition.x][this.promoPosition.y].piece = new Rook(game.whoseTurn()); 119 | } else if (pieceType === "Bishop") { 120 | this.grid[this.promoPosition.x][this.promoPosition.y].piece = new Bishop(game.whoseTurn()); 121 | } else if (pieceType === "Knight") { 122 | this.grid[this.promoPosition.x][this.promoPosition.y].piece = new Knight(game.whoseTurn()); 123 | } 124 | 125 | $("#promotion").hide(); 126 | submitted = true; 127 | $("#board").on("click", "td", boardClicks); 128 | game.turn++; 129 | this.promoPosition = null; 130 | layoutBoard(); 131 | } 132 | }.bind(this)); 133 | } 134 | 135 | var afterAttacks = game.attackedPieces(game.otherTurn()); 136 | this.addFlair(initialAttacks, afterAttacks); 137 | this.previousMove = new Position(oldX, oldY); 138 | this.currentMove = new Position(newX, newY); 139 | this.addForks(); 140 | 141 | if (game.whoseTurn() === "white") { 142 | // $("#moveList").append("
  • " + this.createMoveString(piece, oldPosition, newPosition, capture) + "
  • "); 143 | $("#moveList").append("" + "" + ($("#moveList").children().length + 1) + "." + "" + "" + this.createMoveString(piece, oldPosition, newPosition, capture) + "" + "" + ""); 144 | } else { 145 | // $("#moveList").append("" + "" + this.createMoveString(piece, oldPosition, newPosition, capture) + "" + ""); 146 | // $("#moveList").children()[$("#moveList").children().length - 1].insertCell(1); 147 | $("#moveList").children()[$("#moveList").children().length - 1].cells[2].textContent = this.createMoveString(piece, oldPosition, newPosition, capture); 148 | // $("#moveList").children()[$("#moveList").children().length - 1].textContent += " " + this.createMoveString(piece, oldPosition, newPosition, capture); 149 | } 150 | 151 | if (submitted === null) { 152 | game.turn++; 153 | } else { 154 | this.promoPosition = new Position(newX, newY); 155 | } 156 | 157 | message = this.checkStates(); 158 | } else if (oldX === newX && oldY === newY) { 159 | this.addForks(); 160 | } else { 161 | message = 'Invalid Move'; 162 | } 163 | 164 | if (message !== null) { 165 | messageUser(message, true); 166 | } 167 | this.sumSquareControl(); 168 | layoutBoard(); 169 | this.lastPiece = piece; 170 | } 171 | 172 | Board.prototype.sumSquareControl = function sumSquareControl() { 173 | var blackAttacks = game.getAllLegalAttacks("black"); 174 | var whiteAttacks = game.getAllLegalAttacks("white"); 175 | for (var i = 0; i < blackAttacks.length; i++) { 176 | this.grid[blackAttacks[i].x][blackAttacks[i].y].blackControl++; 177 | } 178 | for (var i = 0; i < whiteAttacks.length; i++) { 179 | this.grid[whiteAttacks[i].x][whiteAttacks[i].y].whiteControl++; 180 | } 181 | } 182 | 183 | Board.prototype.convertFromAlgebra = function convertFromAlgebra(move, moveIndex) { 184 | //Qxd7+ 185 | // ["+", "x"].forEach(function(character){ 186 | // move.replace(character, ""); 187 | // }); 188 | // var oldPosition = new Position(move.charCodeAt(0) - 97, move.charAt(1)); 189 | var movePosition = new Position(move.charCodeAt(move.length - 2) - 97, 8 - parseInt(move.charAt(move.length - 1))); 190 | // this.movePiece(oldPosition, newPosition); 191 | 192 | var player = ""; 193 | if (moveIndex % 2 === 0) { 194 | player = "white"; 195 | } else { 196 | player = "black"; 197 | } 198 | var validMoves = []; 199 | var possiblePiece = game.getPieces(player); 200 | 201 | for (var i = possiblePiece.length - 1; i >= 0; i--) { 202 | var pieceMoves = game.pieceLegalMoves(possiblePiece[i].getPosition()); 203 | for (var j = pieceMoves.length - 1; j >= 0; j--) { 204 | if (pieceMoves[j].x === movePosition.x && pieceMoves[j].y === movePosition.y) { 205 | validMoves.push(possiblePiece[i]); 206 | } 207 | } 208 | } 209 | if (validMoves.length > 1) { 210 | alert("Unclear"); 211 | } 212 | else if (validMoves.length === 0) { 213 | alert("No candidate Piece"); 214 | } 215 | else { 216 | alert(validMoves[0].type + " to " + movePosition.x + ", " + movePosition.y); 217 | } 218 | } 219 | 220 | Board.prototype.cloneBoard = function cloneBoard() { 221 | var boardClone = new Board(); 222 | for (var i = 0; i < this.grid.length; i++) { 223 | for (var j = 0; j < this.grid[i].length; j++) { 224 | if (this.grid[i][j].piece) { 225 | boardClone.grid[i][j].piece = this.grid[i][j].piece.cloneSelf(); 226 | } else { 227 | boardClone.grid[i][j].piece = null; 228 | } 229 | } 230 | } 231 | if (this.previousMove) { 232 | boardClone.previousMove = new Position(this.previousMove.x, this.previousMove.y); 233 | } 234 | if (this.currentMove) { 235 | boardClone.currentMove = new Position(this.currentMove.x, this.currentMove.y); 236 | } 237 | boardClone.sumSquareControl(); 238 | return boardClone; 239 | } 240 | 241 | Board.prototype.undoMove = function undoMove() { 242 | if (game.moveHistory.length > 0) { 243 | var currentState = game.moveHistory.pop(); 244 | game.board = currentState; 245 | 246 | if (game.whoseTurn() === "white") { 247 | var moveString = $("#moveList").children()[$("#moveList").children().length - 1].textContent; 248 | $("#moveList").children()[$("#moveList").children().length - 1].textContent = moveString.substring(moveString.indexOf(" ")); 249 | } else { 250 | $("#moveList").children()[$("#moveList").children().length - 1].remove(); 251 | } 252 | game.turn--; 253 | layoutBoard(); 254 | } 255 | } 256 | 257 | Board.prototype.createMoveString = function createMoveString(piece, oldPosition, newPosition, capture, check, promotionType) { 258 | var moveString = piece.symbol; 259 | if (capture) { 260 | if (piece instanceof Pawn) { 261 | moveString += String.fromCharCode(97 + oldPosition.x); 262 | } 263 | moveString += "x"; 264 | } 265 | game.getPieces(piece.color).forEach(function(otherPiece) { 266 | if (piece.type === otherPiece.type && piece !== otherPiece && !piece instanceof Pawn) { 267 | var otherPosition = otherPiece.getPosition(); 268 | otherPiece.getAttacks(otherPosition).forEach(function(otherMove) { 269 | if (otherMove.x === newPosition.x && otherMove.y === newPosition.y) { 270 | if (otherPosition.x !== oldPosition.x) { 271 | moveString += String.fromCharCode(97 + oldPosition.x); 272 | } else if (otherPosition.y !== oldPosition.y) { 273 | moveString += 8 - oldPosition.y; 274 | } else { 275 | moveString += String.fromCharCode(97 + oldPosition.x) + (8 - oldPosition.y); 276 | } 277 | } 278 | }); 279 | } 280 | }); 281 | moveString += String.fromCharCode(97 + newPosition.x) + (8 - newPosition.y); 282 | if (game.isInCheck(game.otherPlayer(piece.color))) { 283 | if (game.isInCheckmate(game.otherPlayer(piece.color))) { 284 | moveString += "#"; 285 | } else { 286 | moveString += "+"; 287 | } 288 | } 289 | return moveString; 290 | } 291 | 292 | Board.prototype.resetVisualData = function resetVisualData() { 293 | for (var i = 0; i < this.grid.length; i++) { 294 | for (var j = 0; j < this.grid[i].length; j++) { 295 | this.grid[i][j].legalMove = false; 296 | this.grid[i][j].fork = false; 297 | this.grid[i][j].flair = false; 298 | this.grid[i][j].blackControl = null; 299 | this.grid[i][j].whiteControl = null; 300 | } 301 | } 302 | } 303 | 304 | Board.prototype.isMovingDouble = function isMovingDouble(piece, oldPosition, newPosition) { 305 | return piece instanceof Pawn && Math.abs(newPosition.y - oldPosition.y) === 2; 306 | } 307 | 308 | Board.prototype.completeCastle = function completeCastle(piece, oldPosition, newPosition) { 309 | if (piece instanceof King && Math.abs(newPosition.x - oldPosition.x) === 2) { 310 | if (newPosition.x - oldPosition.x === 2) { 311 | this.grid[5][newPosition.y].piece = this.grid[7][newPosition.y].piece; 312 | this.grid[7][newPosition.y].piece = null; 313 | } else if (newPosition.x - oldPosition.x === -2) { 314 | this.grid[3][newPosition.y].piece = this.grid[0][newPosition.y].piece; 315 | this.grid[0][newPosition.y].piece = null; 316 | } 317 | } 318 | } 319 | 320 | Board.prototype.addFlair = function addFlair(initialAttacks, afterAttacks) { 321 | for (var i = afterAttacks.length - 1; i >= 0; i--) { 322 | for (var j = initialAttacks.length - 1; j >= 0; j--) { 323 | if ((afterAttacks[i].x === initialAttacks[j].x) && (afterAttacks[i].y === initialAttacks[j].y)) { 324 | initialAttacks.splice(j, 1); 325 | afterAttacks.splice(i, 1); 326 | break; 327 | } 328 | } 329 | } 330 | for (var i = 0; i < afterAttacks.length; i++) { 331 | this.grid[afterAttacks[i].x][afterAttacks[i].y].flair = true; 332 | } 333 | } 334 | 335 | Board.prototype.addForks = function addForks() { 336 | var forks = game.getWhiteForks(); 337 | for(var i = 0; i < forks.length; i++) { 338 | this.grid[forks[i].x][forks[i].y].fork = true; 339 | } 340 | } 341 | 342 | Board.prototype.checkStates = function checkStates() { 343 | if (game.isInStalemate(game.whoseTurn())) { 344 | $("#board").off(); 345 | return "Stalemate"; 346 | } else if (game.insufficientMatingMaterial()) { 347 | $("#board").off(); 348 | return "Insufficient pieces for checkmate. Draw!"; 349 | } else if (game.isInCheck(game.whoseTurn())) { 350 | if (game.isInCheckmate(game.whoseTurn())) { 351 | $("#board").off(); 352 | return "Checkmate"; 353 | } 354 | return "Check"; 355 | } else { 356 | return ""; 357 | } 358 | } 359 | 360 | Board.prototype.occupiedBy = function occupiedBy(position) { 361 | var x = position.x; 362 | var y = position.y; 363 | if (this.getPiece(x, y) === null) { 364 | return null; 365 | } else { 366 | return this.getPiece(x, y).color; 367 | } 368 | } 369 | 370 | Board.prototype.isLegalMove = function isLegalMove(oldPosition, newPosition) { 371 | var piece = this.grid[oldPosition.x][oldPosition.y].piece; 372 | var legalMoves = game.pieceLegalMoves(oldPosition); 373 | var moved = false; 374 | var initialAttacks = game.attackedPieces(game.otherTurn()); 375 | for (var i = 0; i < legalMoves.length; i++) { 376 | if (legalMoves[i].x === newPosition.x && legalMoves[i].y === newPosition.y) { 377 | return true; 378 | } 379 | } 380 | return false; 381 | } 382 | 383 | // Returns whether the given position is on the board 384 | Board.prototype.isOnBoard = function isOnBoard(position) { 385 | return !(position.x < 0 || position.x > 7 || position.y < 0 || position.y > 7); 386 | }; --------------------------------------------------------------------------------