/**
Auxuliary methods
@module Auxuliary
**/
/**
Auxuliary methods
This is not actually a class, but a necessary convention to get YUIdoc to document standalone items.
@class Auxuliary
*/
/**
* draws rounded rectangle on the canvas plane
* credit: http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas/7592676#7592676
* @method roundRect
* @param {Number} ctx canvas context
* @param {Number} x x coordinate on the canvas plane
* @param {Number} y y coordinate on the canvas plane
* @param {Number} width width of the rectangle
* @param {Number} height height of the rectangle
* @param {Number} radius corner radius
* @param {Boolean} fill fill boolean flag
* @param {Boolean} stroke stroke boolean flag
*/
function roundRect(ctx, x, y, width, height, radius, fill, stroke) {
if (typeof stroke == 'undefined') {
stroke = true;
}
if (typeof radius === 'undefined') {
radius = 5;
}
if (typeof radius === 'number') {
radius = {tl: radius, tr: radius, br: radius, bl: radius};
} else {
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
for (var side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side];
}
}
ctx.beginPath();
ctx.moveTo(x + radius.tl, y);
ctx.lineTo(x + width - radius.tr, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
ctx.lineTo(x + width, y + height - radius.br);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
ctx.lineTo(x + radius.bl, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
ctx.lineTo(x, y + radius.tl);
ctx.quadraticCurveTo(x, y, x + radius.tl, y);
ctx.closePath();
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
/**
* converts coordinates of the chess board to a coordinates of the canvas plane
* @method getCanvasCoordinates
* @param {Number} x x coordinate on the canvas plane
* @param {Number} y y coordinate on the canvas plane
* @param {Number} fieldsize size of the chess field
* @return {Object} coordinates on the canvas plane
*/
function getCanvasCoordinates(x,y,fieldsize) {
if(board.side == "black") {
return {
x: (7 - x) * fieldsize,
y: (7 - y) * fieldsize
}
}
return {
x: x * fieldsize,
y: y * fieldsize
}
}
/**
* removes an elements from an array with a given value
* credit: http://stackoverflow.com/questions/281264/remove-empty-elements-from-an-array-in-javascript/281335#281335
* @method clean
* @param {Object} deleteValue a values to delete
* @return {Array} cleaned array
*/
Array.prototype.clean = function(deleteValue) {
for (var i = 0; i < this.length; i++) {
if (this[i] == deleteValue) {
this.splice(i, 1);
i--;
}
}
return this;
};
/**
* converts mouse coordinates to a coordinates on the chess board
* @method getMousePos
* @param {Object} c canvas context
* @param {Object} evt mouse event
* @return {Object} coordinates on chess board
*/
function getMousePos(c, evt) {
var rect = c.getBoundingClientRect();
var fieldSize = c.width / 8;
return {
x: Math.floor((evt.clientX - rect.left)/fieldSize),
y: Math.floor((evt.clientY - rect.top)/fieldSize)
};
}
/**
* converts mouse coordinates to a coordinates on the chess board invarian of the side perspective
* @method getChessCoordinates
* @param {Object} evt mouse event
* @return {Object} player invariant coordinates on chess board
*/
function getChessCoordinates(evt) {
var c = $('#chess-board').get(0);
var rect = c.getBoundingClientRect();
var fieldSize = c.width / 8;
if(board.side == "black") {
return {
x: 7 - Math.floor((evt.clientX - rect.left)/fieldSize),
y: 7 - Math.floor((evt.clientY - rect.top)/fieldSize)
};
}
return {
x: Math.floor((evt.clientX - rect.left)/fieldSize),
y: Math.floor((evt.clientY - rect.top)/fieldSize)
};
}
/**
* checks if string represents a valud positive integer
* credit: http://stackoverflow.com/questions/10834796/validate-that-a-string-is-a-positive-integer/10834843#10834843
* @method isNormalInteger
* @param {String} str string to be checked
* @return {Boolean} true if an input is a positive integer, false otherwise
*/
function isNormalInteger(str) {
return /^\+?(0|[1-9]\d*)$/.test(str);
}
/**
* converts piece notation symbol to a HTML Entitiy, representing a piece
* @method getHTMLcharFromNotation
* @param {String} str piece notation symbol
* @param {String} color color of the piece, valid values are 'black' and 'white'
* @return {String} HTML Entity
*/
function getHTMLcharFromNotation(val, color) {
if(val == "0-0") return val;
if(val == "0-0-0") return val;
var res = val;
if(color == "white") {
if(val.indexOf("K") > -1) res = res.replace("K", "♔");
if(val.indexOf("Q") > -1) res = res.replace("Q", "♕");
if(val.indexOf("R") > -1) res = res.replace("R", "♖");
if(val.indexOf("B") > -1) res = res.replace("B", "♗");
if(val.indexOf("N") > -1) res = res.replace("N", "♘");
if(val.indexOf("p") > -1) res = res.replace("p", "♙");
} else {
if(val.indexOf("K") > -1) res = res.replace("K", "♚");
if(val.indexOf("Q") > -1) res = res.replace("Q", "♛");
if(val.indexOf("R") > -1) res = res.replace("R", "♜");
if(val.indexOf("B") > -1) res = res.replace("B", "♝");
if(val.indexOf("N") > -1) res = res.replace("N", "♞");
if(val.indexOf("p") > -1) res = res.replace("p", "♟");
}
return res;
}
/**
* converts unix timestamp to a human readable string in mm:ss format
* @method unixTimeStampToTimerString
* @param {Number} ts unix timestamp
* @return {String} time in mm:ss format
*/
function unixTimeStampToTimerString(ts) {
var date = new Date(ts*1000);
var minutes = "0" + date.getMinutes();
var seconds = "0" + date.getSeconds();
var formattedTime = minutes.substr(-2) + ':' + seconds.substr(-2);
return formattedTime;
}
/**
* Increments chess coordinates to a given diagonal direction. Helper method.
* @method getBishopDirections
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} direction direction to increment. Valid values are 'up-right', 'up-left', 'down-right' and 'down-left'
* @return {Object} incremented chess coordinates
*/
function getBishopDirections(x, y, direction, i) {
if(direction == "up-right") {
return {x:x+i,y:y-i};
};
if(direction == "up-left") {
return {x:x-i,y:y-i};
};
if(direction == "down-right") {
return {x:x+i,y:y+i};
};
if(direction == "down-left") {
return {x:x-i,y:y+i};
};
}
/**
* Increments chess coordinates to a given lateral direction. Helper method.
* @method getRookDirections
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} direction direction to increment. Valid values are 'right', 'left', 'down' and 'up'
* @return {Object} incremented chess coordinates
*/
function getRookDirections(x, y, direction, i) {
if(direction == "right") {
return {x:x+i,y:y};
};
if(direction == "left") {
return {x:x-i,y:y};
};
if(direction == "up") {
return {x:x,y:y+i};
};
if(direction == "down") {
return {x:x,y:y-i};
};
}
/**
* calculates moves in a diagonal directions given an initial coordinates and a direction. Helper method.
* @method getBishopLegalMovesHelper
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the piece, valid values are 'black' and 'white'
* @param {String} direction direction to increment. Valid values are 'up-right', 'up-left', 'down-right' and 'down-left'
* @return {Array} an array of moves
*/
function getBishopLegalMovesHelper(x,y,color, direction) {
var moves = [];
parentLoop:
for(var i = 1; i < 9; i++) {
var curCoor = getBishopDirections(x, y, direction, i);
curX = curCoor.x;
curY = curCoor.y;
if(curX > 7 || curY > 7 || curX < 0 || curY < 0) break;
for(var j = 0; j < board.pieces.length; j++) {
if(board.pieces[j].X == curX && board.pieces[j].Y == curY && board.pieces[j].color == color) {
break parentLoop;
}
if(board.pieces[j].X == curX && board.pieces[j].Y == curY && board.pieces[j].color != color) {
moves.push({x:curX, y:curY});
break parentLoop;
}
}
moves.push({x:curX, y:curY});
}
return moves;
}
/**
* calculates moves in a lateral directions given an initial coordinates and a direction. Helper method.
* @method getRookLegalMovesHelper
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the piece, valid values are 'black' and 'white'
* @param {String} direction direction to increment. Valid values are 'right', 'left', 'down' and 'up'
* @return {Array} an array of moves
*/
function getRookLegalMovesHelper(x,y,color, direction) {
var moves = [];
parentLoop:
for(var i = 1; i < 9; i++) {
var curCoor = getRookDirections(x, y, direction, i);
curX = curCoor.x;
curY = curCoor.y;
if(curX > 7 || curY > 7 || curX < 0 || curY < 0) break;
for(var j = 0; j < board.pieces.length; j++) {
if(board.pieces[j].X == curX && board.pieces[j].Y == curY && board.pieces[j].color == color) {
break parentLoop;
}
if(board.pieces[j].X == curX && board.pieces[j].Y == curY && board.pieces[j].color != color) {
moves.push({x:curX, y:curY});
break parentLoop;
}
}
moves.push({x:curX, y:curY});
}
return moves;
}
/**
* calculates coordinate of the field behind the opponent King in a diagonal directions given an initial
* coordinates of the atacking piece and direction.
* Necessary for calculation of the King's legal moves. Helper method.
* @method getBishopFieldBehindHelper
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the atacking piece, valid values are 'black' and 'white'
* @param {String} direction direction to increment. Valid values are 'up-right', 'up-left', 'down-right' and 'down-left'
* @return {Object} a coordinate of the field behind the opponent's king
*/
function getBishopFieldBehindHelper(x,y,color,direction) {
var flag = false;
parentLoop:
for(var i = 1; i < 9; i++) {
var curCoor = getBishopDirections(x, y, direction, i);
curX = curCoor.x;
curY = curCoor.y;
if(curX > 7 || curY > 7 || curX < 0 || curY < 0) break;
if(flag) return {x:curX, y:curY};
for(var j = 0; j < board.pieces.length; j++) {
if(board.pieces[j].X == curX
&& board.pieces[j].Y == curY
&& board.pieces[j].type != "King") {
return null;
}
if(board.pieces[j].X == curX
&& board.pieces[j].Y == curY
&& board.pieces[j].type == "King") {
if(board.pieces[j].color == color) {
return null;
}
flag = true;
break;
}
}
}
}
/**
* calculates coordinate of the field behind the opponent King in a lateral directions given an initial
* coordinates of the atacking piece and direction.
* Necessary for calculation of the King's legal moves. Helper method.
* @method getRookFieldBehindHelper
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the atacking piece, valid values are 'black' and 'white'
* @param {String} direction direction to increment. Valid values are 'right', 'left', 'down' and 'up'
* @return {Object} a coordinate of the field behind the opponent's king
*/
function getRookFieldBehindHelper(x,y,color,direction) {
var flag = false;
parentLoop:
for(var i = 1; i < 9; i++) {
var curCoor = getRookDirections(x, y, direction, i);
curX = curCoor.x;
curY = curCoor.y;
if(curX > 7 || curY > 7 || curX < 0 || curY < 0) break;
if(flag) return {x:curX, y:curY};
for(var j = 0; j < board.pieces.length; j++) {
if(board.pieces[j].X == curX
&& board.pieces[j].Y == curY
&& board.pieces[j].type != "King") {
return null;
}
if(board.pieces[j].X == curX
&& board.pieces[j].Y == curY
&& board.pieces[j].type == "King") {
if(board.pieces[j].color == color) {
return null;
}
flag = true;
break;
}
}
}
}
/**
* calculates an array of fields covered by a piece in diagonal directions given an initial
* coordinates of the piece and direction. Helper method.
* @method getBishopCoveredFieldsHelper
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the atacking piece, valid values are 'black' and 'white'
* @param {String} direction direction to increment. Valid values are 'right', 'left', 'down' and 'up'
* @return {Array} an array of fields covered by a piece
*/
function getBishopCoveredFieldsHelper(x,y,color, direction) {
var moves = [];
parentLoop:
for(var i = 1; i < 9; i++) {
var curCoor = getBishopDirections(x, y, direction, i);
curX = curCoor.x;
curY = curCoor.y;
if(curX > 7 || curY > 7 || curX < 0 || curY < 0) break;
for(var j = 0; j < board.pieces.length; j++) {
if(board.pieces[j].X == curX && board.pieces[j].Y == curY) {
moves.push({x:curX, y:curY});
break parentLoop;
}
}
moves.push({x:curX, y:curY});
}
return moves;
}
/**
* calculates an array of fields covered by a piece in lateral directions given an initial
* coordinates of the piece and direction. Helper method.
* @method getRookCoveredFieldsHelper
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the atacking piece, valid values are 'black' and 'white'
* @param {String} direction direction to increment. Valid values are 'right', 'left', 'down' and 'up'
* @return {Array} an array of fields covered by a piece
*/
function getRookCoveredFieldsHelper(x,y,color, direction) {
var moves = [];
parentLoop:
for(var i = 1; i < 9; i++) {
var curCoor = getRookDirections(x, y, direction, i);
curX = curCoor.x;
curY = curCoor.y;
if(curX > 7 || curY > 7 || curX < 0 || curY < 0) break;
for(var j = 0; j < board.pieces.length; j++) {
if(board.pieces[j].X == curX && board.pieces[j].Y == curY) {
moves.push({x:curX, y:curY});
break parentLoop;
}
}
moves.push({x:curX, y:curY});
}
return moves;
}
/**
* calculates an array of legal moves for a piece in a diagonal directions
* @method getBishopLegalMoves
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the piece, valid values are 'black' and 'white'
* @return {Array} an array of legal moves
*/
function getBishopLegalMoves(x,y,color) {
var moves = [];
var moves1 = getBishopLegalMovesHelper(x,y,color, "up-right");
var moves2 = getBishopLegalMovesHelper(x,y,color, "up-left");
var moves3 = getBishopLegalMovesHelper(x,y,color, "down-right");
var moves4 = getBishopLegalMovesHelper(x,y,color, "down-left");
return moves = moves.concat(moves1, moves2, moves3, moves4);
}
/**
* calculates an array of covered fields for a piece in a diagonal directions
* @method getBishopCoveredFields
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the piece, valid values are 'black' and 'white'
* @return {Array} an array of covered fields
*/
function getBishopCoveredFields(x,y,color) {
var moves = [];
var moves1 = getBishopCoveredFieldsHelper(x,y,color, "up-right");
var moves2 = getBishopCoveredFieldsHelper(x,y,color, "up-left");
var moves3 = getBishopCoveredFieldsHelper(x,y,color, "down-right");
var moves4 = getBishopCoveredFieldsHelper(x,y,color, "down-left");
return moves = moves.concat(moves1, moves2, moves3, moves4);
}
/**
* calculates an array of legal moves for a piece in a lateral directions
* @method getRookLegalMoves
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the piece, valid values are 'black' and 'white'
* @return {Array} an array of legal moves
*/
function getRookLegalMoves(x,y,color) {
var moves = [];
var moves1 = getRookLegalMovesHelper(x,y,color, "up");
var moves2 = getRookLegalMovesHelper(x,y,color, "down");
var moves3 = getRookLegalMovesHelper(x,y,color, "left");
var moves4 = getRookLegalMovesHelper(x,y,color, "right");
return moves = moves.concat(moves1, moves2, moves3, moves4);
}
/**
* calculates an array of covered fields for a piece in a lateral directions
* @method getRookCoveredFields
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the piece, valid values are 'black' and 'white'
* @return {Array} an array of covered fields
*/
function getRookCoveredFields(x,y,color) {
var moves = [];
var moves1 = getRookCoveredFieldsHelper(x,y,color, "up");
var moves2 = getRookCoveredFieldsHelper(x,y,color, "down");
var moves3 = getRookCoveredFieldsHelper(x,y,color, "left");
var moves4 = getRookCoveredFieldsHelper(x,y,color, "right");
return moves = moves.concat(moves1, moves2, moves3, moves4);
}
/**
* calculates coordinate of the field behind the opponent King in a diagonal directions given an initial
* coordinates of the atacking piece
* @method getBishopFieldBehind
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the atacking piece, valid values are 'black' and 'white'
* @return {Object} a coordinate of the field behind the opponent's king
*/
function getBishopFieldBehind(x,y,color) {
var fieldBehind;
fieldBehind = getBishopFieldBehindHelper(x,y,color,"up-right");
if(fieldBehind != null) return fieldBehind;
fieldBehind = getBishopFieldBehindHelper(x,y,color,"up-left");
if(fieldBehind != null) return fieldBehind;
fieldBehind = getBishopFieldBehindHelper(x,y,color,"down-right");
if(fieldBehind != null) return fieldBehind;
fieldBehind = getBishopFieldBehindHelper(x,y,color,"down-left");
if(fieldBehind != null) return fieldBehind;
return null;
}
/**
* calculates coordinate of the field behind the opponent King in a lateral directions given an initial
* coordinates of the atacking piece
* @method getRookFieldBehind
* @param {Number} x X coordinate of the field on the chess board
* @param {Number} y Y coordinate of the field on the chess board
* @param {String} color color of the atacking piece, valid values are 'black' and 'white'
* @return {Object} a coordinate of the field behind the opponent's king
*/
function getRookFieldBehind(x,y,color) {
var fieldBehind;
fieldBehind = getRookFieldBehindHelper(x,y,color,"right");
if(fieldBehind != null) return fieldBehind;
fieldBehind = getRookFieldBehindHelper(x,y,color,"left");
if(fieldBehind != null) return fieldBehind;
fieldBehind = getRookFieldBehindHelper(x,y,color,"up");
if(fieldBehind != null) return fieldBehind;
fieldBehind = getRookFieldBehindHelper(x,y,color,"down");
if(fieldBehind != null) return fieldBehind;
return null;
}
/**
* removes captured piece from a pieces array of the board object
* @method removeCapturedPiece
* @param {Number} x X coordinate of the captured piece on the chess board
* @param {Number} y Y coordinate of the captured piece on the chess board
* @return {Boolean} true if piece is found, false otherwise
*/
function removeCapturedPiece(x,y) {
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].X == x && board.pieces[i].Y == y) {
if(board.pieces[i].X == 0 && board.pieces[i].Y == 0) board.blackCanCastleLong = "false";
if(board.pieces[i].X == 7 && board.pieces[i].Y == 0) board.blackCanCastleShort = "false";
if(board.pieces[i].X == 7 && board.pieces[i].Y == 7) board.whiteCanCastleShort = "false";
if(board.pieces[i].X == 0 && board.pieces[i].Y == 7) board.whiteCanCastleLong = "false";
board.pieces[i] = null;
return true;
}
}
return false;
}
/**
* check whatever is King under atack
* @method isKingAttacked
* @param {String} color color of the King
* @return {Boolean} true if King is under atack, false otherwise
*/
function isKingAttacked(color) {
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].color == color && board.pieces[i].type == "King")
var king = board.pieces[i];
}
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].color != color) {
var cf = board.pieces[i].getCoveredFields();
for(var j = 0; j < cf.length; j++) {
if(cf[j].x == king.X && cf[j].y == king.Y) return true;
}
}
}
return false;
}
/**
* filters an array of legal moves for a given piece, restricting the movement of a piece in a such a way
* that King can not be atacked by opponent piece
* @method preventCheck
* @param {Object} piece piece, movement of which to be restricted
* @param {Array} legalMoves an array of legal moves of the piece
* @return {Array} filtered array of a legal moves
*/
function preventCheck(piece, legalMoves) {
var originalPosition = {x:piece.X, y:piece.Y};
var legalMove = null;
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].ID == piece.ID) {
//defend from check by capture the atacking piece
parentLoop:
for(var j = 0; j < legalMoves.length; j++) {
for(var k = 0; k < board.pieces.length; k++) {
if(board.pieces[k].X == legalMoves[j].x && board.pieces[k].Y == legalMoves[j].y) {
var pieceToKill = board.pieces[k];
board.pieces[i].X = legalMoves[j].x;
board.pieces[i].Y = legalMoves[j].y;
board.pieces.splice(k,1);
if(!isKingAttacked(piece.color)) {
board.pieces.splice(k, 0, pieceToKill);
board.pieces[i].X = originalPosition.x;
board.pieces[i].Y = originalPosition.y;
legalMove = legalMoves[j];
break parentLoop;
}
board.pieces.splice(k, 0, pieceToKill);
board.pieces[i].X = originalPosition.x;
board.pieces[i].Y = originalPosition.y;
}
}
}
//defend from check by blocking the atacking piece
for(var j = 0; j < legalMoves.length; j++) {
board.pieces[i].X = legalMoves[j].x;
board.pieces[i].Y = legalMoves[j].y;
if(isKingAttacked(piece.color)) {
board.pieces[i].X = originalPosition.x;
board.pieces[i].Y = originalPosition.y;
legalMoves[j] = null;
} else {
board.pieces[i].X = originalPosition.x;
board.pieces[i].Y = originalPosition.y;
}
}
}
}
legalMoves.push(legalMove);
return legalMoves.clean(null);
}
/**
* generates move summary of a given input data
* that King can not be atacked by opponent piece
* @method getMoveSummary
* @param {Object} piece a piece, which move is summarised
* @param {Object} alternativePieces an array of pieces if the same type, which can reach the same destination field
* @param {Object} origin coordinates of origin
* @param {Object} destination coordinates of destination
* @param {String} castling a string represents a castling, valid values are 'short' for king side castling,
* 'long' for queen side castling or empty string if the move was not a castling
* @param {Boolean} isCapture a boolean flag indicating if the move was a capture
* @param {Boolean} isCheck a boolean flag indicating if the move was a check against an opponent king
* @param {Boolean} isMate a boolean flag indicating if the move was a checkmate against the opponent
* @param {Boolean} isStalemate a boolean flag indicating if the move achieved a stalemate
* @param {String} promotion a string indicating that the move was a promotion of a pawn.
* Valid values are 'Q' for Queen, 'R' for the Rook, 'N' for the Knight, 'B' for the Bishop or 'none' if the move was not a promotion
* @param {String} enpassant notation coordinates of the pawn indicating that the pawn can be taken en passant
* @return {Object} an object represents a summary of the move
*/
function getMoveSummary(piece,
alternativePieces,
origin,
destination,
castling,
isCapture,
isCheck,
isMate,
isStalemate,
promotion,
enpassant) {
var move = '';
if(castling == "short") {
move = "0-0";
} else if(castling == "long") {
move = "0-0-0";
} else {
if(alternativePieces.length == 0) {
if(isCapture) {
move = piece + "x" + getNotationCoordinates(destination);
} else {
move = piece + getNotationCoordinates(destination);
}
if(promotion != "none") {
move = move + "=" + promotion;
}
} else {
var shareX = false;
var shareY = false;
for(var i = 0; i < alternativePieces.length; i++) {
for(var j = 0; j < alternativePieces.length; j++) {
if(alternativePieces[i].X == alternativePieces[j].X && alternativePieces[i].ID != alternativePieces[j].ID) {
shareX = true;
}
}
}
for(var i = 0; i < alternativePieces.length; i++) {
for(var j = 0; j < alternativePieces.length; j++) {
if(alternativePieces[i].Y == alternativePieces[j].Y && alternativePieces[i].ID != alternativePieces[j].ID) {
shareY = true;
}
}
}
if(!shareX && !shareY) piece = piece + getXNotationCoordinate(origin.x);
if(shareX && !shareY) piece = piece + getYNotationCoordinate(origin.y);
if(!shareX && shareY) piece = piece + getXNotationCoordinate(origin.x);
if(shareX && shareY) piece = piece + getNotationCoordinates(origin);
if(isCapture) {
move = piece + "x" + getNotationCoordinates(destination);
} else {
move = piece + getNotationCoordinates(destination);
}
}
}
if(isCheck && !isMate) move = move + "+";
if(isMate) move = move + "#";
return summary = {
move: move,
castling: castling,
isCapture: isCapture,
isCheck: isCheck,
isMate: isMate,
isStalemate: isStalemate,
promotion: promotion,
enpassant: enpassant
}
}
/**
* checks whatever the move is a legal move for a given piece
* @method confirmMove
* @param {Object} piece a piece to check the validity of the move
* @param {Number} x X coordinate of the destination field on the chess board
* @param {Number} y Y coordinate of the destination field on the chess board
* @return {Boolean} true if the move is a legal move for a given piece, false otherwise
*/
function confirmMove(x,y,piece) {
var legalMoves = piece.getLegalMoves();
var found = false;
for(var i = 0; i < legalMoves.length; i++) {
if(legalMoves[i].x == x && legalMoves[i].y == y) {
found = true;
break;
}
}
return found;
}
/**
* generates move summary of the move for a given piece
* @method finishMove
* @param {Object} piece a piece to move
* @param {Number} x X coordinate of the destination field on the chess board
* @param {Number} y Y coordinate of the destination field on the chess board
* @return {Object} an object represents a summary of the move
*/
function finishMove(x,y,piece) {
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].color != piece.color && board.pieces[i].type == "Pawn")
board.pieces[i].canBeTakenEnPassant = false;
}
var _isCapture = removeCapturedPiece(x,y);
board.pieces = board.pieces.clean(null);
var alternativePieces = [];
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].type == piece.type && board.pieces[i].color == piece.color && board.pieces[i].ID != piece.ID) {
var legalMoves = board.pieces[i].getLegalMoves();
for(var j = 0; j < legalMoves.length; j++) {
if(legalMoves[j].x == x && legalMoves[j].y == y) {
alternativePieces.push(board.pieces[i]);
}
}
}
}
if(alternativePieces.length > 0) alternativePieces.push(jQuery.extend(true, {}, piece));
var origin = {x:piece.X,y:piece.Y};
var destination = {x:x,y:y};
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].ID == piece.ID) {
board.pieces[i].X = x;
board.pieces[i].Y = y;
}
}
board.drawMove(origin, destination, piece);
board.changeSideToMove();
var targetColor;
if(piece.color == "white")
targetColor = "black";
else
targetColor = "white";
var _isCheck = isKingAttacked(targetColor);
var _isMate = isMate(targetColor);
var _isStalemate = isStalemate(targetColor);
if(_isCapture) {
board.drawMoves = 0;
} else {
board.drawMoves++;
}
return getMoveSummary(
piece.notationName,
alternativePieces,
origin,
destination,
"none",
_isCapture,
_isCheck,
_isMate,
_isStalemate,
"none",
"none");
}
/**
* converts chess coordinate to a notation coordinates string for a given field
* @method getNotationCoordinates
* @param {Object} coor coordinate of the field
* @return {String} notation coordinates string
*/
function getNotationCoordinates(coor) {
return getXNotationCoordinate(coor.x) + getYNotationCoordinate(coor.y);
}
/**
* generates pawn capture notation string for a given move
* @method getPawnCaptureNotation
* @param {Object} origin coordinate of origin field on the chess board
* @param {Object} destination coordinate of the destination field on the chess board
* @return {String} pawn capture notation string
*/
function getPawnCaptureNotation(origin, destination) {
return getXNotationCoordinate(origin.x) + "x" + getNotationCoordinates(destination);
}
/**
* converts notation coordinates to the chess board coordinates
* @method getXYfromNotationCoordinates
* @param {String} notation coordinates
* @return {Object} chess board coordinates
*/
function getXYfromNotationCoordinates(coor) {
if(coor == "none") return false;
var x;
var y;
if(coor[0] == "a") x = 0;
if(coor[0] == "b") x = 1;
if(coor[0] == "c") x = 2;
if(coor[0] == "d") x = 3;
if(coor[0] == "e") x = 4;
if(coor[0] == "f") x = 5;
if(coor[0] == "g") x = 6;
if(coor[0] == "h") x = 7;
if(coor[1] == "1") y = 7;
if(coor[1] == "2") y = 6;
if(coor[1] == "3") y = 5;
if(coor[1] == "4") y = 4;
if(coor[1] == "5") y = 3;
if(coor[1] == "6") y = 2;
if(coor[1] == "7") y = 1;
if(coor[1] == "8") y = 0;
return {x:x,y:y};
}
/**
* converts X coordinate of the chess board field to a notation-friendly rank coordinate
* @method getXNotationCoordinate
* @param {Number} x X coordinate
* @return {String} notation-friendly rank coordinate
*/
function getXNotationCoordinate(x) {
if(x == 0) return "a";
if(x == 1) return "b";
if(x == 2) return "c";
if(x == 3) return "d";
if(x == 4) return "e";
if(x == 5) return "f";
if(x == 6) return "g";
if(x == 7) return "h";
}
/**
* converts Y coordinate of the chess board field to a notation-friendly file coordinate
* @method getYNotationCoordinate
* @param {Number} y Y coordinate
* @return {String} notation-friendly file coordinate
*/
function getYNotationCoordinate(y) {
if(y == 0) return "8";
if(y == 1) return "7";
if(y == 2) return "6";
if(y == 3) return "5";
if(y == 4) return "4";
if(y == 5) return "3";
if(y == 6) return "2";
if(y == 7) return "1";
}
/**
* check whatever the position is checkmate for a given side
* @method isStalemate
* @param {String} color color of the side, valid values are 'black' and 'white'
* @return {Boolean} true if position is checkmate, false otherwise
*/
function isMate(color) {
var legalMoves = [];
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].color == color) {
legalMoves = legalMoves.concat(board.pieces[i].getLegalMoves());
}
}
if(legalMoves.length == 0 && isKingAttacked(color)) return true;
return false;
}
/**
* check whatever the position is stalemate for a given side
* @method isStalemate
* @param {String} color color of the side, valid values are 'black' and 'white'
* @return {Boolean} true if position is stalemate, false otherwise
*/
function isStalemate(color) {
var legalMoves = [];
for(var i = 0; i < board.pieces.length; i++) {
if(board.pieces[i].color == color) {
legalMoves.push(board.pieces[i].getLegalMoves());
}
}
if(legalMoves.length == 0 && !isKingAttacked(color)) return true;
return false;
}