├── 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 |
54 |
--------------------------------------------------------------------------------
/Assets/white_pawn.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
77 |
--------------------------------------------------------------------------------
/Assets/white_rook.svg:
--------------------------------------------------------------------------------
1 |
2 |
80 |
--------------------------------------------------------------------------------
/Assets/white_knight.svg:
--------------------------------------------------------------------------------
1 |
2 |
71 |
--------------------------------------------------------------------------------
/Assets/black_bishop.svg:
--------------------------------------------------------------------------------
1 |
2 |
71 |
--------------------------------------------------------------------------------
/Assets/white_bishop.svg:
--------------------------------------------------------------------------------
1 |
2 |
71 |
--------------------------------------------------------------------------------
/Assets/white_king.svg:
--------------------------------------------------------------------------------
1 |
2 |
82 |
--------------------------------------------------------------------------------
/Assets/black_king.svg:
--------------------------------------------------------------------------------
1 |
2 |
78 |
--------------------------------------------------------------------------------
/Assets/black_knight.svg:
--------------------------------------------------------------------------------
1 |
2 |
75 |
--------------------------------------------------------------------------------
/Assets/white_queen.svg:
--------------------------------------------------------------------------------
1 |
2 |
90 |
--------------------------------------------------------------------------------
/Assets/black_rook.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
89 |
--------------------------------------------------------------------------------
/Assets/black_queen.svg:
--------------------------------------------------------------------------------
1 |
2 |
111 |
--------------------------------------------------------------------------------
/Assets/black_fork.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
89 |
--------------------------------------------------------------------------------
/Assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 | ").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 | };
--------------------------------------------------------------------------------
|