/**
* A Board class, represents a state of the game
* @class Board
* @constructor
*/
function Board() {
/**
* self reference
* @property that
* @type Object
*/
var that = this;
/**
* Board side, either 'black' or 'white'
* @property side
* @type String
*/
this.side;
/**
* Current side to move, either 'black' or 'white'
* @property sideToMove
* @type String
*/
this.sideToMove;
/**
* The color of the white fields
* @private
* @property whiteField
* @type String
*/
var whiteField = "rgb(221, 221, 221)";
/**
* The color of the black fields
* @private
* @property blackField
* @type String
*/
var blackField = "rgb(136, 173, 177)";
/**
* canvas context to draw pieces
* @private
* @property piecesContext
* @type Object
*/
piecesContext = $('#chess-pieces').get(0).getContext('2d');
/**
* canvas context to draw legal moves highlight
* @private
* @property lmovesContext
* @type Object
*/
lmovesContext = $('#legal-moves').get(0).getContext('2d');
/**
* canvas context to draw board
* @private
* @property boardContext
* @type Object
*/
boardContext = $('#chess-board').get(0).getContext('2d');
/**
* Counter of moves to achieve a draw by 50 moves rule
* @property drawMoves
* @type Number
*/
this.drawMoves = 0;
/**
* A flag, represents if white side has right to castle long, valid falues are 'true' and 'false'
* @property whiteCanCastleLong
* @type String
*/
this.whiteCanCastleLong = "true";
/**
* A flag, represents if white side has right to castle short, valid falues are 'true' and 'false'
* @property whiteCanCastleLong
* @type String
*/
this.whiteCanCastleShort = "true";
/**
* A flag, represents if black side has right to castle long, valid falues are 'true' and 'false'
* @property whiteCanCastleLong
* @type String
*/
this.blackCanCastleLong = "true";
/**
* A flag, represents if black side has right to castle short, valid falues are 'true' and 'false'
* @property whiteCanCastleLong
* @type String
*/
this.blackCanCastleShort = "true";
/**
* An array of pieces on the board
* @property pieces
* @type Array
*/
this.pieces = [];
/**
* draws chess board with out pieces
* @method drawBoard
*/
this.drawBoard = function() {
for(var i = 0; i < 8; i++) {
for(var j = 0; j < 8; j++) {
if((i+j)%2 == 0) {
boardContext.fillStyle = whiteField;
} else {
boardContext.fillStyle = blackField;
}
boardContext.fillRect(
i * this.getFieldSize(),
j * this.getFieldSize(),
this.getFieldSize(),
this.getFieldSize());
}
}
}
/**
* draws chess pieces
* @method drawPieces
*/
this.drawPieces = function() {
piecesContext.clearRect(0, 0, this.getBoardWidth(), this.getBoardWidth());
for(var i = 0; i < this.pieces.length; i++) {
var coord = getCanvasCoordinates(
this.pieces[i].X,
this.pieces[i].Y,
this.getFieldSize());
var img = new Image();
img.onload = (function(i, coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(
img,
coord.x,
coord.y,
fieldSize,
fieldSize);
}
})(i,coord,this.getFieldSize(), img, piecesContext);
img.src = this.pieces[i].getImageSource();
}
}
/**
* draws highlight of legal moves
* @method drawLegalMoves
*/
this.drawLegalMoves = function(moves) {
for(var i = 0; i < moves.length; i++) {
lmovesContext.beginPath();
var coor = getCanvasCoordinates(moves[i].x, moves[i].y, this.getFieldSize());
lmovesContext.arc(
coor.x + this.getFieldSize() * 0.5,
coor.y + this.getFieldSize() * 0.5,
this.getFieldSize()*0.1,0,2*Math.PI);
lmovesContext.closePath();
lmovesContext.lineWidth = 2;
lmovesContext.fillStyle = '#BC1B00';
lmovesContext.fill();
lmovesContext.strokeStyle = '#660E00';
lmovesContext.stroke();
}
}
/**
* draws highlight of selected piece
* @method highlightSelectedPiece
* @param {Object} piece selected piece
*/
this.highlightSelectedPiece = function(piece) {
this.clearSelection();
var coor = getCanvasCoordinates(piece.X, piece.Y, this.getFieldSize());
boardContext.fillStyle="rgb(199, 179, 125)";
boardContext.fillRect(coor.x, coor.y, this.getFieldSize(), this.getFieldSize());
this.drawLegalMoves(piece.getLegalMoves());
}
/**
* clears highlight of the selected piece
* @method clearSelection
*/
this.clearSelection = function() {
lmovesContext.clearRect(0, 0, this.getBoardWidth(), this.getBoardWidth());
for(var i = 0; i < 8; i++) {
for(var j = 0; j < 8; j++) {
if((i+j)%2 == 0) {
boardContext.fillStyle = whiteField;
} else {
boardContext.fillStyle = blackField;
}
boardContext.fillRect(
i * this.getFieldSize(),
j * this.getFieldSize(),
this.getFieldSize(),
this.getFieldSize());
}
}
}
/**
* chnages current side to move
* @method changeSideToMove
*/
this.changeSideToMove = function() {
if(this.sideToMove == "white") {
this.sideToMove = "black";
return;
}
if(this.sideToMove == "black") {
this.sideToMove = "white";
return;
}
}
/**
* gets width of the board
* @method getBoardWidth
*/
this.getBoardWidth = function() {
return $('#chess-pieces').get(0).width;
}
/**
* gets length of the side of the field
* @method getBoardWidth
*/
this.getFieldSize = function() {
return $('#chess-pieces').get(0).width / 8;
}
/**
* renders the move of a piece
* @method drawMove
* @param {Object} origin coordinates of origin
* @param {Object} destination coordinates of destination
* @param {Object} piece piece to move
*/
this.drawMove = function(origin, destination, piece) {
var originCanvasCoordinates = getCanvasCoordinates(origin.x, origin.y, this.getFieldSize());
var destinationCoordinates = getCanvasCoordinates(destination.x, destination.y, this.getFieldSize());
piecesContext.clearRect(
originCanvasCoordinates.x,
originCanvasCoordinates.y,
this.getFieldSize(),
this.getFieldSize());
piecesContext.clearRect(
destinationCoordinates.x,
destinationCoordinates.y,
this.getFieldSize(),
this.getFieldSize());
var img = new Image();
img.onload = (function(coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(img, coord.x, coord.y, fieldSize, fieldSize);
}
})(destinationCoordinates,this.getFieldSize(), img, piecesContext);
img.src = piece.getImageSource();
}
/**
* renders the castling
* @method drawCastling
* @param {Object} origin coordinates of origin
* @param {Object} destination coordinates of destination
* @param {Object} piece piece to castle
*/
this.drawCastling = function(origin, destination, piece) {
var originCanvasCoordinates = getCanvasCoordinates(origin.x, origin.y, this.getFieldSize());
var destinationCoordinates = getCanvasCoordinates(destination.x, destination.y, this.getFieldSize());
piecesContext.clearRect(
originCanvasCoordinates.x,
originCanvasCoordinates.y,
this.getFieldSize(),
this.getFieldSize());
if(destination.x == 2 && destination.y == 0) {
var rookField = getCanvasCoordinates(0, 0, this.getFieldSize());
piecesContext.clearRect(destinationCoordinates.x, destinationCoordinates.y, this.getFieldSize(), this.getFieldSize());
piecesContext.clearRect(rookField.x, rookField.y, this.getFieldSize(), this.getFieldSize());
}
if(destination.x == 6 && destination.y == 0) {
var rookField = getCanvasCoordinates(7, 0, this.getFieldSize());
piecesContext.clearRect(destinationCoordinates.x, destinationCoordinates.y, this.getFieldSize(), this.getFieldSize());
piecesContext.clearRect(rookField.x, rookField.y, this.getFieldSize(), this.getFieldSize());
}
if(destination.x == 2 && destination.y == 7) {
var rookField = getCanvasCoordinates(0, 7, this.getFieldSize());
piecesContext.clearRect(destinationCoordinates.x, destinationCoordinates.y, this.getFieldSize(), this.getFieldSize());
piecesContext.clearRect(rookField.x, rookField.y, this.getFieldSize(), this.getFieldSize());
}
if(destination.x == 6 && destination.y == 7) {
var rookField = getCanvasCoordinates(7, 7, this.getFieldSize());
piecesContext.clearRect(destinationCoordinates.x, destinationCoordinates.y, this.getFieldSize(), this.getFieldSize());
piecesContext.clearRect(rookField.x, rookField.y, this.getFieldSize(), this.getFieldSize());
}
var img = new Image();
img.onload = (function(coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(img, coord.x, coord.y, fieldSize, fieldSize);
}
})(destinationCoordinates,this.getFieldSize(), img, piecesContext);
img.src = piece.getImageSource();
if(destination.x == 2 && destination.y == 0) {
var img_rook = new Image();
var coord = getCanvasCoordinates(3,0,this.getFieldSize());
img_rook.onload = (function(coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(img, coord.x, coord.y, fieldSize, fieldSize);
}
})(coord,this.getFieldSize(), img_rook, piecesContext);
img_rook.src = "images/pieces/brook.png";
}
if(destination.x == 6 && destination.y == 0) {
var img_rook = new Image();
var coord = getCanvasCoordinates(5,0,this.getFieldSize());
img_rook.onload = (function(coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(img, coord.x, coord.y, fieldSize, fieldSize);
}
})(coord,this.getFieldSize(), img_rook, piecesContext);
img_rook.src = "images/pieces/brook.png";
}
if(destination.x == 2 && destination.y == 7) {
var img_rook = new Image();
var coord = getCanvasCoordinates(3,7,this.getFieldSize());
img_rook.onload = (function(coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(img, coord.x, coord.y, fieldSize, fieldSize);
}
})(coord,this.getFieldSize(), img_rook, piecesContext);
img_rook.src = "images/pieces/wrook.png";
}
if(destination.x == 6 && destination.y == 7) {
var img_rook = new Image();
var coord = getCanvasCoordinates(5,7,this.getFieldSize());
img_rook.onload = (function(coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(img, coord.x, coord.y, fieldSize, fieldSize);
}
})(coord,this.getFieldSize(), img_rook, piecesContext);
img_rook.src = "images/pieces/wrook.png";
}
}
/**
* renders the en passant capture
* @method drawEnpassant
* @param {Object} origin coordinates of origin
* @param {Object} destination coordinates of destination
* @param {Object} piece piece to take en passant
*/
this.drawEnpassant = function(origin, destination, piece) {
var originCanvasCoordinates = getCanvasCoordinates(origin.x, origin.y, this.getFieldSize());
var destinationCoordinates = getCanvasCoordinates(destination.x, destination.y, this.getFieldSize());
piecesContext.clearRect(
originCanvasCoordinates.x,
originCanvasCoordinates.y,
this.getFieldSize(),
this.getFieldSize());
piecesContext.clearRect(
destinationCoordinates.x,
destinationCoordinates.y,
this.getFieldSize(),
this.getFieldSize());
if(piece.color == "white") {
var clear = getCanvasCoordinates(destination.x, destination.y + 1, this.getFieldSize());
piecesContext.clearRect(
clear.x,
clear.y,
this.getFieldSize(),
this.getFieldSize());
}
if(piece.color == "black") {
var clear = getCanvasCoordinates(destination.x, destination.y - 1, this.getFieldSize());
piecesContext.clearRect(
clear.x,
clear.y,
this.getFieldSize(),
this.getFieldSize());
}
var img = new Image();
img.onload = (function(coord, fieldSize, img, ctx){
return function(){
ctx.drawImage(img, coord.x, coord.y, fieldSize, fieldSize);
}
})(destinationCoordinates,this.getFieldSize(), img, piecesContext);
img.src = piece.getImageSource();
}
/**
* generates Forsyth–Edwards notation of the current state of the chess game
* @method getFEN
* @return {String} string represnts a Forsyth–Edwards notation of the current state of the chess game
*/
this.getFEN = function() {
var boardFields = [];
for(var i = 0; i < 64; i++) boardFields.push(null);
for(var i = 0; i < this.pieces.length; i++) {
var piecePosition = this.pieces[i].Y * 8 + this.pieces[i].X;
boardFields[piecePosition] = this.pieces[i];
}
var fenString = '';
var spaces = 0;
for(var i = 0; i < 64; i++) {
if(boardFields[i] == null) {
spaces++;
} else {
if(spaces != 0) fenString = fenString + spaces;
fenString = fenString + boardFields[i].getNotationSymbol();
spaces = 0;
}
if((i + 1)%8 == 0 && i != 63) {
if(spaces != 0) {
fenString = fenString + spaces;
spaces = 0;
}
fenString = fenString + "/";
}
}
return fenString;
}
/**
* sets a pieces property according to a given Forsyth–Edwards notation value
* @method setPieces
* @private
* @param {String} fen string represnts a Forsyth–Edwards notation
*/
setPieces = function(fen) {
var boardFields = [];
for(var i = 0; i < 64; i++) boardFields.push(null);
var id = 0;
var pieces = [];
var fields = [];
var skip = 0;
for (var i = 0; i < fen.length; i++) {
if(fen[i] == "/") continue;
if(isNormalInteger(fen[i])) {
var skip = fen[i];
for(var j = 0; j < skip; j++) {
fields.push(null);
}
}
if(fen[i] == "K") fields.push("K");
if(fen[i] == "k") fields.push("k");
if(fen[i] == "Q") fields.push("Q");
if(fen[i] == "q") fields.push("q");
if(fen[i] == "R") fields.push("R");
if(fen[i] == "r") fields.push("r");
if(fen[i] == "B") fields.push("B");
if(fen[i] == "b") fields.push("b");
if(fen[i] == "N") fields.push("N");
if(fen[i] == "n") fields.push("n");
if(fen[i] == "P") fields.push("P");
if(fen[i] == "p") fields.push("p");
}
for (var i = 0; i < fields.length; i++) {
if(fields[i] == null) continue;
curX = i%8;
curY = Math.floor(i / 8);
var curPiece;
if(fields[i] == "K") curPiece = new King("white", "King", curX, curY, id++);
if(fields[i] == "k") curPiece = new King("black", "King", curX, curY, id++);
if(fields[i] == "Q") curPiece = new Queen("white", "Queen", curX, curY, id++);
if(fields[i] == "q") curPiece = new Queen("black", "Queen", curX, curY, id++);
if(fields[i] == "R") curPiece = new Rook("white", "Rook", curX, curY, id++);
if(fields[i] == "r") curPiece = new Rook("black", "Rook", curX, curY, id++);
if(fields[i] == "B") curPiece = new Bishop("white", "Bishop", curX, curY, id++);
if(fields[i] == "b") curPiece = new Bishop("black", "Bishop", curX, curY, id++);
if(fields[i] == "N") curPiece = new Knight("white", "Knight", curX, curY, id++);
if(fields[i] == "n") curPiece = new Knight("black", "Knight", curX, curY, id++);
if(fields[i] == "P") curPiece = new Pawn("white", "Pawn", curX, curY, id++);
if(fields[i] == "p") curPiece = new Pawn("black", "Pawn", curX, curY, id++);
pieces.push(curPiece);
}
that.pieces = pieces;
}
/**
* sets a board side of a player according to a given white player id
* @method setBoardSide
* @private
* @param {Number} wp_id white player id
*/
setBoardSide = function(wp_id) {
if(userId == wp_id) {
that.side = "white";
} else {
that.side = "black";
}
}
/**
* sets a boolean flag to pawn which can be taken en passant
* @method setEnPassant
* @private
* @param {Object} coor the coordinates of the pawn to be set
*/
setEnPassant = function(coor) {
var target = getXYfromNotationCoordinates(coor);
if(!target) return;
for(var i = 0; i < that.pieces.length; i++) {
var p = that.pieces[i];
if(p.X == target.x && p.Y == target.y && p.type == "Pawn") {
p.canBeTakenEnPassant = true;
return;
}
}
}
/**
* sets a chess board according to a given data
* @method setBoard
* @param {Object} data data represents the state of the game
*/
this.setBoard = function(data) {
this.sideToMove = data.side_to_move;
setPieces(data.fen);
setBoardSide(data.white_player_id);
setEnPassant(data.en_passant);
this.whiteCanCastleLong = data.white_long_castling;
this.whiteCanCastleShort = data.white_short_castling;
this.blackCanCastleLong = data.black_long_castling;
this.blackCanCastleShort = data.black_short_castling;
}
}