/** Version: 0.6.7 **/
/**
* Copyright 2008 Toomas Römer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/*
Convert PGN format to an easier format. The problem
with PGN is that it is really difficult and ugly to
accomplish backward moves.
Let's say we have a move "e4" and we need to go one
move back. The only info is that we have placed a pawn
to e4. We also have to remember from where did we place
the pawn. To make it easier and to have less calculations
the PGN is converted into a format where the from square
with contents is explicit and to square also. There are
other problems also regarding backward moving and remembering
which piece was taken.
*/
// AH: These comments (AH:) are made by Adrian Hanft, Overall changes: replaced all gif with png
function Converter(pgn) {
this.pgn = pgn;
this.vBoard = new Array(8);
this.initialBoard = new Array(8);
this.moves = new Array();
this.iteIndex = 0;
this.whiteToMove = true;
this.startMoveNum = 1;
this.flippedI = false;
this.flippedV = false;
this.wKing = new Array();
this.bKing = new Array();
this.wQueens = new Array();
this.bQueens = new Array();
this.wBishops = new Array();
this.bBishops = new Array();
this.wRooks = new Array();
this.bRooks = new Array();
for(var i = 0; i < 8; i++) {
this.vBoard[i] = new Array(8);
for (var j = 0; j < 8; j++) {
this.vBoard[i][j] = new vSquare();
}
}
if (pgn.props['FEN']) {
var val = pgn.props['FEN'].split(/\/| /g);
for (var i=0;i<8;i++) {
var file = 0;
for (var j=0;jthis.iteIndex)
return this.moves[this.iteIndex];
return null;
};
this.getCurMoveNo = function() {
return this.iteIndex;
};
this.nextMove = function() {
if (this.moves.length>this.iteIndex)
return this.moves[this.iteIndex++];
return null;
};
this.prevMove = function() {
if (this.iteIndex>0)
return this.moves[--this.iteIndex];
return null;
};
this.resetToEnd = function() {
this.iteIndex = this.moves.length;
};
this.resetToStart = function() {
this.iteIndex = 0;
};
/*
EOF Result Iterator
*/
this.getStartPos = function(flipped) {
if (flipped!=this.flippedI) {
this.flipBoard(this.initialBoard);
this.flippedI = !this.flippedI;
}
return this.initialBoard;
};
this.getEndPos = function(flipped) {
if (flipped!=this.flippedV) {
this.flipBoard(this.vBoard);
this.flippedV = !this.flippedV;
}
return this.vBoard;
};
this.flipBoard = function(board) {
this.flipped = !this.flipped;
for (var i = 0;i<8;i++) {
for (var j = 0;j<4;j++) {
var tmp = board[i][j];
board[i][j] = board[7-i][7-j];
board[7-i][7-j] = tmp;
}
}
};
/*
Convert a move.
*/
this.convertMove = function(board) {
var to = this.pgn.nextMove();
var oldTo = to;
if (to == null)
return;
var color = to[1];
to = to[0];
/*
Check which piece has to move.
Find the location of the piece.
*/
var pawnre = /^[a-z]+[1-8]/;
var knightre = /^N[0-9]?[a-z]+[1-8]/i;
var bishre = /^B[a-z]+[1-8]/;
var queenre = /^Q([a-z]|[0-9])?[a-z]+[1-8]/i;
var rookre = /^R([a-z]|[0-9])?[a-z]+[1-8]/i;
var lCastlere = /^(0|O)-(0|O)-(0|O)/i;
var sCastlere = /^(0|O)-(0|O)/i;
var kingre = /^K[a-z]+[1-8]/i;
var prom = "";
var toCoords = getSquare(to);
var fromCoords, from, to, result, myMove = null, pawnM = false;
if (knightre.test(to)) {
fromCoords = findFromKnight(this, to, toCoords, color);
}
else if (bishre.test(to)) {
fromCoords = findFromBish(this, this.vBoard, to, toCoords, color);
}
else if (queenre.test(to)) {
fromCoords = findFromQueen(this, this.vBoard, to, toCoords, color);
}
else if (rookre.test(to)) {
fromCoords = findFromRook(this, this.vBoard, to, toCoords, color);
}
else if (kingre.test(to)) {
fromCoords = findFromKing(this, this.vBoard, color);
}
else if (sCastlere.test(to)) {
var bCoords = new Array('e8','g8','h8','f8');
var wCoords = new Array('e1','g1','h1','f1');
if (lCastlere.test(to)) {
bCoords = new Array('e8', 'c8', 'a8', 'd8');
wCoords = new Array('e1', 'c1', 'a1', 'd1');
}
var coords = color=='white'?wCoords:bCoords;
fromCoords = getSquare(coords[0]);
toCoords = getSquare(coords[1]);
from = this.vBoard[fromCoords[0]][fromCoords[1]];
to = this.vBoard[toCoords[0]][toCoords[1]];
// update king location
if ('king' == from.piece && 'white' == from.color)
this.wKingX = toCoords[0], this.wKingY = toCoords[1];
else if ('king' == from.piece && 'black' == from.color)
this.bKingX = toCoords[0], this.bKingY = toCoords[1];
result = movePiece(this, from, to, prom);
myMove = new MyMove();
myMove.moveStr = oldTo[0];
myMove.oPiece = result[2].piece;
myMove.oColor = result[2].color;
myMove.pPiece = result[3];
myMove.add(new MySquare(fromCoords[0], fromCoords[1]
,result[0].piece, result[0].color));
myMove.add(new MySquare(toCoords[0], toCoords[1]
,result[1].piece, result[1].color));
fromCoords = getSquare(coords[2]);
toCoords = getSquare(coords[3]);
}
else if (pawnre.test(to)) {
// let see if it is a promotional move
if (/^[a-z]+[1-8]=[A-Z]/.test(to))
prom = to.charAt(to.indexOf('=')+1);
fromCoords = findFromPawn(this.vBoard, to, toCoords, color);
pawnM = true;
}
else {
throw("Can't figure out which piece to move '"+oldTo+"'");
}
from = this.vBoard[fromCoords[0]][fromCoords[1]];
to = this.vBoard[toCoords[0]][toCoords[1]];
// update king location
if ('king' == from.piece && 'white' == from.color) {
this.wKingX = toCoords[0], this.wKingY = toCoords[1];
} else if ('king' == from.piece && 'black' == from.color) {
this.bKingX = toCoords[0], this.bKingY = toCoords[1];
// update bishops location
} else if ('bishop' == from.piece) {
var idx;
if ('white' == from.color) {
idx = findPieceIdx(this.wBishops,fromCoords);
this.wBishops[idx][0] = toCoords[0];
this.wBishops[idx][1] = toCoords[1];
}
else {
idx = findPieceIdx(this.bBishops,fromCoords);
this.bBishops[idx][0] = toCoords[0];
this.bBishops[idx][1] = toCoords[1];
}
} else if ('queen' == from.piece) {
var idx;
if ('white' == from.color) {
idx = findPieceIdx(this.wQueens,fromCoords);
this.wQueens[idx][0] = toCoords[0];
this.wQueens[idx][1] = toCoords[1];
}
else {
idx = findPieceIdx(this.bQueens,fromCoords);
this.bQueens[idx][0] = toCoords[0];
this.bQueens[idx][1] = toCoords[1];
}
} else if ('rook' == from.piece) {
var idx;
if ('white' == from.color) {
idx = findPieceIdx(this.wRooks,fromCoords);
this.wRooks[idx][0] = toCoords[0];
this.wRooks[idx][1] = toCoords[1];
}
else {
idx = findPieceIdx(this.bRooks,fromCoords);
this.bRooks[idx][0] = toCoords[0];
this.bRooks[idx][1] = toCoords[1];
}
}
if ('queen' == to.piece) {
if ('white' == to.color) {
idx = findPieceIdx(this.wQueens,toCoords);
this.wQueens.splice(idx,1);
}
else {
idx = findPieceIdx(this.bQueens,toCoords);
this.bQueens.splice(idx,1);
}
}
else if ('bishop' == to.piece) {
if ('white' == to.color) {
idx = findPieceIdx(this.wBishops,toCoords);
this.wBishops.splice(idx,1);
}
else {
idx = findPieceIdx(this.bBishops,toCoords);
this.bBishops.splice(idx,1);
}
}
else if ('rook' == to.piece) {
if ('white' == to.color) {
idx = findPieceIdx(this.wRooks,toCoords);
this.wRooks.splice(idx,1);
}
else {
idx = findPieceIdx(this.bRooks,toCoords);
this.bRooks.splice(idx,1);
}
}
// in case of castling we don't have a null value
if (!myMove)
myMove = new MyMove();
var enPassante = null;
if (pawnM)
enPassante = getEnPassante(this, fromCoords[0], fromCoords[1],
toCoords[0], toCoords[1]);
if (enPassante) {
var sq = this.vBoard[enPassante[0]][enPassante[1]];
var enP = new MySquare(enPassante[0], enPassante[1]
,sq.piece, sq.color);
myMove.enP = enP;
this.vBoard[enPassante[0]][enPassante[1]].color = null;
this.vBoard[enPassante[0]][enPassante[1]].piece = null;
this.vBoard[enPassante[0]][enPassante[1]].type = null;
}
result = movePiece(this, from, to ,prom);
myMove.oPiece = result[2].piece;
myMove.oColor = result[2].color;
myMove.pPiece = result[3];
myMove.moveStr = oldTo[0];
if (prom) {
if ("queen" == result[1].piece) {
if ('white' == result[1].color) {
this.wQueens[this.wQueens.length] = [toCoords[0],toCoords[1]];
}
else {
this.bQueens[this.bQueens.length] = [toCoords[0],toCoords[1]];
}
}
else if ("bishop" == result[1].piece) {
if ('white' == result[1].color) {
this.wBishops[this.wBishops.length] = [toCoords[0],toCoords[1]];
}
else {
this.bBishops[this.bBishops.length] = [toCoords[0],toCoords[1]];
}
}
else if ("rook" == result[1].piece) {
if ('white' == result[1].color) {
this.wRooks[this.wRooks.length] = [toCoords[0],toCoords[1]];
}
else {
this.bRooks[this.bRooks.length] = [toCoords[0],toCoords[1]];
}
}
}
myMove.add(new MySquare(fromCoords[0], fromCoords[1]
,result[0].piece, result[0].color));
myMove.add(new MySquare(toCoords[0], toCoords[1]
,result[1].piece, result[1].color));
return myMove;
};
/* FINDING FROM LOCATION FUNCTIONS
When a SAN (Standard Algebraic Notation) move is given
we need to figure out from where the move is made. Lets
say the SAN is "e4" - pawn moves to e4. The from location
can be e2, e3 or e5. This depends on the color of the player
and on where the pawn was located. All pieces have different
logic on finding which piece exactly has to move to the location.
*/
findPieceIdx = function(arr, coords) {
for (var i=0;i froms[i][0] && color == "white")
continue;
return new Array(froms[i][0], froms[i][1]);
}
//else
// return new Array(froms[i][0], froms[i][1])
}
}
catch (e) {}
}
}
else {
// non-taking move
try {
var j;
for(var i = 0; i < 8; i++) {
j = (color == 'white')?7-i:i;
if (pos[j][x].piece == 'pawn'
&& pos[j][x].color == color) {
if (Math.abs(j-y)>2) {
continue;
}
// we might be looking at the wrong pawn
// there can be one between src and dst
if (2 == Math.abs(j-y)) {
var j2 = (color == 'white')?(j-1):j+1;
if (pos[j2][x].piece == 'pawn'
&& pos[j2][x].color == color) {
return new Array(j2, x);
}
}
return new Array(j, x);
}
}
}
catch (e) {}
}
throw("Could not find a move with a pawn '"+to+"'");
};
/*
Find the bishop from location.
*/
function findFromBish(board, pos, toSAN, toCoords, color) {
if (toCoords[2][0] != -1 && toCoords[2][1] != -1) {
return new Array(toCoords[2][1], toCoords[2][0]);
}
var arr;
if (color == 'white') {
arr = board.wBishops;
}
else {
arr = board.bBishops;
}
for (var i=0;i 0) {
rdx = 1;
}
else if (rdx < 0) {
rdx = -1;
}
if (rdy > 0) {
rdy = 1;
}
else if (rdy < 0) {
rdy = -1;
}
if (dx == dy || dx == 0 || dy == 0) { //bishop-like move or rook-like move
var x = arr[i][0];
var y = arr[i][1];
while (true) {
x += rdx;
y += rdy;
if (x == to[0] && y == to[1]) {
if (extra[0] != -1 || extra[1] != -1) {
if (extra[0] != arr[i][1] && extra[1] != arr[i][0]) {
break;
}
return new Array(arr[i][0],arr[i][1]);
}
rtrns[rtrns.length] = new Array(arr[i][0],arr[i][1]);
break;
}
var tmp = pos[x][y];
if (tmp && tmp.piece) { //ran into another piece
break;
}
}
}
}
if (rtrns.length>1) {
for (var i = 0; i< rtrns.length;i++) {
var from = pos[rtrns[i][0]][rtrns[i][1]];
var oldTo = pos[to[0]][to[1]];
pos[rtrns[i][0]][rtrns[i][1]] = new vSquare();
pos[to[0]][to[1]] = from;
var checked = isKingChecked(board,from.color, pos);
pos[rtrns[i][0]][rtrns[i][1]] = from;
pos[to[0]][to[1]] = oldTo;
if (checked)
continue;
else
return rtrns[i];
}
}
else if (rtrns.length == 1)
return rtrns[0];
throw("No queen move found '"+toSAN+"'");
};
/*
Find the rook's from location.
*/
function findFromRook(board, pos, toSAN, to, color) {
var extra = to[2];
var rtrns = new Array();
var arr;
if (color == 'white') {
arr = board.wRooks;
}
else {
arr = board.bRooks;
}
for (var i=0;i 0) {
rdx = 1;
}
else if (rdx < 0) {
rdx = -1;
}
if (rdy > 0) {
rdy = 1;
}
else if (rdy < 0) {
rdy = -1;
}
if (dx == 0 || dy == 0) {
var x = arr[i][0];
var y = arr[i][1];
while (true) {
x += rdx;
y += rdy;
if (x == to[0] && y == to[1]) {
if (extra[0] != -1 || extra[1] != -1) {
if (extra[0] != arr[i][1] && extra[1] != arr[i][0]) {
break;
}
return new Array(arr[i][0],arr[i][1]);
}
rtrns[rtrns.length] = new Array(arr[i][0],arr[i][1]);
break;
}
tmp = pos[x][y];
if (tmp && tmp.piece) { //ran into another piece
break;
}
}
}
}
if (rtrns.length>1) {
for (var i = 0; i< rtrns.length;i++) {
var from = pos[rtrns[i][0]][rtrns[i][1]];
var oldTo = pos[to[0]][to[1]];
pos[rtrns[i][0]][rtrns[i][1]] = new vSquare();
pos[to[0]][to[1]] = from;
var checked = isKingChecked(board,from.color, pos);
pos[rtrns[i][0]][rtrns[i][1]] = from;
pos[to[0]][to[1]] = oldTo;
if (checked)
continue;
else
return rtrns[i];
}
}
else if (rtrns.length == 1)
return rtrns[0];
throw("No rook move found '"+toSAN+"'");
};
/*
Find the knight's from location.
*/
findFromKnight = function(brd, toSAN, toCoords, color) {
var to = toCoords;
var extra = to[2];
if (toCoords[2][0] != -1 && toCoords[2][1] != -1) {
return new Array(toCoords[2][1], toCoords[2][0]);
}
var pos = brd.vBoard;
var rtrns = new Array();
var froms = new Array(
new Array(to[0]+2, to[1]+1),
new Array(to[0]+2, to[1]-1),
new Array(to[0]-2, to[1]+1),
new Array(to[0]-2, to[1]-1),
new Array(to[0]+1, to[1]+2),
new Array(to[0]-1, to[1]+2),
new Array(to[0]+1, to[1]-2),
new Array(to[0]-1, to[1]-2)
);
for (var i = 0;i1) {
for (var i = 0; i< rtrns.length;i++){
var from = pos[rtrns[i][0]][rtrns[i][1]];
pos[rtrns[i][0]][rtrns[i][1]] = new vSquare();
var checked = isKingChecked(brd, from.color, pos);
pos[rtrns[i][0]][rtrns[i][1]] = from;
if (checked)
continue;
else
return rtrns[i];
}
return rtrns[0];
}
else if (rtrns.length == 1)
return rtrns[0];
throw("No knight move found. '"+toSAN+"'");
};
/*
* Converts a SAN (Standard Algebraic Notation) into
* board coordinates. The SAN is in the format of
* eg e4, dxe4, R2b7. When SAN contains extra information
* "taking move", "en passante", "check", "piece from a
* specific file or rank" it is also extracted.
*/
function getSquare(coord) {
if (arguments.length != 1) {
throw "Wrong number of arguments";
}
var map = new Object();
// if only from certain file we can make the move
var extra = new Array(-1,-1);
var taking = -1;
map['a'] = 7, map['b'] = 6, map['c'] = 5;
map['d'] = 4, map['e'] = 3, map['f'] = 2;
map['g'] = 1, map['h'] = 0;
// trim the everything from +
if (coord.indexOf("+") != -1)
coord = coord.substring(0, coord.indexOf("+"));
// let's trim the piece prefix
if (/^[A-Z]/.test(coord) || /^[nbrqk]{1,1}[abcdefgh]{1,1}/.test(coord)) {
coord = coord.substr(1);
}
// the move is a taking move, we have to look for different
// files then with pawns
if (/x/.test(coord)) {
var tmp = coord.split("x");
if (tmp[0].length) {
if (/[a-z][0-9]/.test(tmp[0])) {
extra[0] = 7-map[tmp[0].charAt(0)];
extra[1] = 8-tmp[0].charAt(1);
}
else if (/[a-z]/.test(tmp[0]))
extra[0] = 7-map[tmp[0]];
else if (/[0-9]/.test(tmp[0]))
extra[1] = 8-tmp[0];
}
coord = tmp[1];
taking = 7-map[tmp[0]];
}
// we have extra information on the from file
// eg Rbd7
if (/^[a-z]{2,2}/.test(coord)) {
extra[0] = 7-map[coord.substring(0,1)];
coord = coord.substring(1);
}
// we have the row no
// eg R8d5
if (/^[0-9][a-z][0-9]/.test(coord)) {
extra[1] = 8-coord.substring(0,1);
coord = coord.substring(1);
}
// we have both Ng8e7
if (/^([a-z][0-9])[a-z][0-9]/.test(coord)) {
var tmp = coord.match(/^([a-z][0-9])[a-z][0-9]/);
extra[0] = 7-map[tmp[1].charAt(0)];
extra[1] = 8-tmp[1].charAt(1);
coord = coord.replace(/[a-z][0-9]/,"");
}
var rtrn = new Array(8-coord.charAt(1),
7-map[coord.charAt(0)],
extra, taking);
return rtrn;
};
getEnPassante = function(brd, x1, y1, x2, y2) {
var from = brd.vBoard[x1][y1];
var to = brd.vBoard[x2][y2];
// pawn move
if ("pawn" != from.piece)
return null;
// taking move
if ((y1-y2) == 0)
return null;
// destination should be null
if ( null != to.piece )
return null;
// the piece we are looking for
return new Array(x1, y2);
};
getOppColor = function(color) {
return "white"==color?"black":"white";
};
movePiece = function(board, from, to, prom) {
var hist = to.clone();
var tmpPiece = from.piece;
var pPiece = null;
to.piece = from.piece;
to.color = from.color;
to.type = from.type;
from.piece = null;
from.color = null;
from.type = null;
// promoting the piece
if (prom.length>0) {
pPiece = tmpPiece;
switch(prom) {
case 'R':
to.piece = 'rook';
break;
case 'B':
to.piece = 'bishop';
break;
case 'N':
to.piece = 'knight';
break;
case 'Q':
to.piece = 'queen';
break;
default:
throw('Unknown promotion');
}
}
return new Array(from, to, hist, pPiece);
};
isKingChecked = function(brd, col) {
var op = getOppColor(col);
var x = brd.wKingX, y = brd.wKingY;
if ("black" == col) {
x = brd.bKingX, y = brd.bKingY;
}
// diagonals, looking for bishops, queens
var tmp;
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x-i][y-i];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("bishop" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x+i][y+i];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("bishop" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x+i][y-i];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("bishop" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x-i][y+i];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("bishop" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
// horizontals, verticals - looking for rooks and queens
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x][y+i];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("rook" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x][y-i];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("rook" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x+i][y];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("rook" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
try {
for (var i = 1;i < 7; i++) {
tmp = brd.vBoard[x-i][y];
if (tmp.color == col)
break;
if (tmp.color == op) {
if("rook" == tmp.piece || "queen" == tmp.piece) {
return true;
}
break;
}
}
}
catch (e) {}
return false;
};
};
function MyMove() {
this.actions = new Array();
this.oPiece = null;
this.oColor = null;
// in case of promotion have to remember the prev
// piece
this.pPiece = null;
//
this.enP = null;
//
this.moveStr = null;
this.add = function(action) {
this.actions[this.actions.length] = action;
};
this.toString = function() {
return "MyMove -- no. actions "+this.actions.length;
};
};
function MySquare(x, y, piece, color) {
this.x = x;
this.y = y;
this.color = color;
this.piece = piece;
this.toString = function() {
return "MySquare -- x = "+this.x+" y="+this.y
+" color="+this.color
+ " piece="+this.piece;
};
this.clone = function() {
var sq = new MySquare(this.x, this.y,
this.piece, this.color);
return sq;
};
};
function vSquare() {
this.piece = null;
this.color = null;
this.type = "";
this.toString = function() {
return "vSquare -- piece = "+this.piece+" color="+this.color
+" type="+this.type;
};
this.clone = function() {
var sq = new vSquare();
sq.piece = this.piece;
sq.color = this.color;
sq.type = this.type;
return sq;
};
};
/**
* Copyright 2008 Toomas Römer
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/*
Representation of the PGN format. Different meta information
about the actual game(s) plus the moves and result of the game.
*/
function Pgn(pgn) {
// properties of the game eg players, ELOs etc
this.props = new Object();
this.validProps = ['Event','Site','Date','Round',
'White','Black','Result','FEN',
'WhiteElo','BlackElo','TimeControl'];
// the moves, one move contains the black and white move
this.moves = new Array();
// the current move in the game
this.currentMove = 0;
// for outputting white and black moves separately
this.skip = 0;
// strip newlines
this.pgnOrig = pgn;
pgn = pgn.replace(/\n/g," ");
// replace dollar signs
//"!", "?", "!!", "!?", "?!", and "??"
pgn = pgn.replace(/\ \$1[0-9]*/g, "!");
pgn = pgn.replace(/\ \$2[0-9]*/g, "?");
pgn = pgn.replace(/\ \$3[0-9]*/g, "!!");
pgn = pgn.replace(/\ \$4[0-9]*/g, "??");
pgn = pgn.replace(/\ \$5[0-9]*/g, "!?");
pgn = pgn.replace(/\ \$6[0-9]*/g, "?!");
pgn = pgn.replace(/\ \$[0-9]+/g, "");
// make double spaces to single spaces
pgn = pgn.replace(/\s+/g,' ');
this.pgn = pgn;
this.pgnRaw = pgn;
if (isPGNBroken(pgn))
this.pgnStripped = stripItBroken(pgn);
else
this.pgnStripped = stripIt(pgn);
/* constructor */
// strip comments
if (isPGNBroken(pgn))
this.pgn = stripItBroken(pgn,true);
else
this.pgn = stripIt(pgn,true);
// Match all properties
var reprop = /\[([^\]]*)\]/gi;
var matches = this.pgn.match(reprop);
if (matches) {
// extract information from each matched property
for(var i = 0;i < matches.length; i++) {
// lose the brackets
tmpMatches = matches[i].substring(1, matches[i].length-1);
// split by the first space
var key = tmpMatches.substring(0, tmpMatches.indexOf(" "));
var value = tmpMatches.substring(tmpMatches.indexOf(" ")+1);
if (value.charAt(0) == '"')
value = value.substr(1);
if (value.charAt(value.length-1) == '"')
value = value.substr(0, value.length-1);
this.props[key] = value;
this.pgn = this.pgn.replace(matches[i], "");
}
}
// remove the properties
this.pgn = this.pgn.replace(/\[[^\]]*\]/g,'');
//trim
this.pgn = this.pgn.replace(/^\s+|\s+$/g, '');
var gameOverre = new Array(
/1\/2-1\/2/,
/0-1/,
/1-0/,
/\*/
);
// the moves;
var themoves = this.pgn.split(" ");
var tmp = new Array();
tmp[1] = null;
var tmpidx = 0; //make this 1 if FEN and black to move
if (this.props["FEN"]) {
var fen = this.props['FEN'].split(/\/| /g);
if (fen[8] == 'b') {
tmpidx = 1;
this.skip = 1;
}
}
if (themoves.length>0 && themoves[themoves.length-1] == "...") {
themoves = themoves.slice(0, themoves.length-1);
}
var sizeOfTheMoves = themoves.length;
if (themoves.length>0) {
for (var i=0;i= '1' && c <= '9') { //move number
c = themoves[i].charAt(themoves[i].length-1);
if (c == '.') { //ends with . so nothing but a move
continue;
}
var found = false;
for (var j=0;j= '0' && c <= '9') {
continue;
}
else {
found = true;
var idx = j;
// 6.0-0 goes wrong as 0 is used for castling
if (!(themoves[i].charAt(j) >= '0' && themoves[i].charAt(j)<='9')) {
idx = j+1;
}
themoves[i] = themoves[i].substring(idx); //strip move number
break;
}
}
if (!found) {
continue;
}
}
tmp[tmpidx] = themoves[i];
if (tmpidx == 1) { //black's move or last move
var move = new Move(tmp[0], tmp[1]);
this.moves[this.moves.length] = move;
tmpidx = 0;
tmp = new Array();
tmp[1] = null;
}
else {
tmpidx = 1;
}
}
if (tmp[0] || tmp[1]) {
var move = new Move(tmp[0], tmp[1]);
this.moves[this.moves.length] = move;
}
this.nextMove = function() {
var rtrn = null;
try{
if (this.skip) {
this.skip = 0;
rtrn = new Array(this.moves[this.currentMove].black,
'black');
this.currentMove++;
}
else {
this.skip = 1;
rtrn = new Array(this.moves[this.currentMove].white,
'white');
}
if (rtrn[0] == null || rtrn[0].length == 0)
rtrn = null;
return rtrn;
}
catch (e) {
return null;
}
};
this.getComment = function(move, idx) {
var i = this.pgnStripped.indexOf(move,idx);
if (i == -1) {
//throw("getComment error, could not find move '"
// +move+"'"+", with index '"+idx+"'");
return [null,idx];
}
for (var j=i+move.length;jk+1
&& this.pgnStripped.charAt(k+1) == '_') {
continue;
}
return [this.pgnRaw.substring(j,k),k];
}
}
break;
default: //no comment
return [null,idx];
}
}
return [null,idx];
};
};
function Move(white, black) {
this.white = white;
this.black = black;
this.toString = function() {
return this.white+" "+this.black;
};
};
/*
Strip game comments from a PGN string. If second
parameter set false true then comments will be replaced
by an underscore.
*/
function stripIt(val, strip) {
var count = 0;
var out = new Array();
for (var i=0;i 0) {
if (!strip) {
out[out.length] = '_';
}
}
else {
out[out.length] = c;
}
}
}
return out.join("");
};
function isPGNBroken(val) {
var pCount = 0;
var cCount = 0;
var lastOne = "";
for (var i=0;i 0) {
if (!strip) {
out[out.length] = '_';
}
}
else {
out[out.length] = c;
}
}
}
return out.join("");
};
/**
* Copyright 2008 Toomas Römer
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
function Board(divId, options) {
var pgn = new Pgn(document.getElementById(divId).firstChild.nodeValue);
this.conv = new Converter(pgn);
this.conv.convert();
this.movesOnPane = new Array();
this.flipped = false;
this.id = (new Date()).getTime();
window[this.id] = this;
if (!options)
options={};
this.moveInput = null;
this.lastBold = null;
this.lastBoldIdx = null;
this.lastSquare = null;
this.visuals = {"pgn":{}};
this.opts = [];
this.opts['imagePrefix'] = "img/default/";
this.opts['buttonPrefix'] = "img/default/buttons/";
this.opts['imageSuffix'] = 'png';
this.opts['moveFontSize'] = "8pt";
this.opts['moveFontColor'] = "#537c3a";
this.opts['moveFont'] = 'Tahoma, Arial, sans-serif';
this.opts['commentFontSize'] = "8pt";
this.opts['commentFontColor'] = "#6060df";
this.opts['commentFont'] = 'Tahoma, Arial, sans-serif';
this.opts['boardSize'] = '257px';
this.opts['squareSize'] = '31px';
this.opts['blackSqColor'] = "#4b4b4b";
// AH: the white square must be blank in order to use background images
this.opts['whiteSqColor'] = "transparent";
this.opts['squareBorder'] = "0px solid #000000";
this.opts['move_highlight_color'] = "#ff9900";
// AH: the board_background_image below would look something like this: url(http://yoururl.com/wp-content/plugins/chess-game-viewer-control-panel/images/boards/32/bamboo.jpg)
this.opts['board_background_image'] = "";
this.opts['flipped'] = false;
this.opts['showMovesPane'] = true;
this.opts['showComments'] = true;
this.opts['markLastMove'] = true;
this.opts['altRewind'] = "Rewind to the beginning";
this.opts['altBack'] = "One move back";
this.opts['altFlip'] = "Flip the board";
this.opts['altShowMoves'] = "Show moves pane";
this.opts['altComments'] = "Show comments";
this.opts['altPlayMove'] = "Play one move";
this.opts['altFastForward'] = "Fast-forward to the end";
this.opts['moveBorder'] = "0px solid #000000";
this.opts['downloadURL'] = "http://www.chesspastebin.com/asPgn.php?PGN=";
this.opts['skipToMove'] = null;
var optionNames = ['flipped', 'moveFontSize', 'moveFontColor',
'moveFont', 'commentFontSize',
'commentFontColor', 'commentFont',
'boardSize', 'squareSize',
'blackSqColor', 'whiteSqColor',
'imagePrefix', 'showMovesPane',
'movesPaneWidth','imageSuffix',
'comments','squareBorder',
'markLastMove','altRewind',
'altBack','altFlip','altShowMoves',
'altComments','altPlayMove',
'altFastForward','moveBorder',
'skipToMove', 'downloadURL',
'buttonPrefix', 'move_highlight_color',
'board_background_image'];
// AH: added move_highlight_color, and board_background_image options above
// if keys in options define new values then
// set the this.opts for that key with the
// custom value
for (var i=0;i2) {
var color2 = tmp2%2==0?1:0;
tmp2 = Math.round(tmp2/2);
this.skipToMove(tmp2-1,color2);
}
else if (tmp2 == 1) {
this.skipToMove(0,0);
}
else if (tmp2 == 2) {
this.skipToMove(0,1);
}
}catch(e){}
}
};
flipBoard = function(board) {
board.deMarkLastMove(true);
var frst, snd, tmp;
board.flipped = !board.flipped;
for (var i = 0;i<8;i++) {
for (var j = 0;j<4;j++){
frst = board.pos[i][j];
snd = board.pos[7-i][7-j];
try {
tmp = frst.removeChild(frst.firstChild);
}
catch (e) {tmp=null}
try{
frst.appendChild(snd.removeChild(snd.firstChild));
}
catch (e) {}
if (tmp)
snd.appendChild(tmp);
}
}
};
this.skipToMove = function(no, color) {
var rNo = no*2+color+1;
if (this.conv.getCurMoveNo()rNo) {
var i = 0;
while(this.conv.getCurMoveNo()>rNo && i < 200) {
makeBwMove(this, true);
i++;
};
updateMoveInfo(this);
updateMovePane(this);
this.deMarkLastMove();
this.markLastMove();
}
};
endPosition = function(board) {
board.deMarkLastMove();
var vBoard = board.conv.getEndPos(board.flipped);
board.syncBoard(vBoard);;
board.conv.resetToEnd();
updateMoveInfo(board);
updateMovePane(board, true);
board.markLastMove();
};
this.startPosition = function() {
startPosition(this)
};
startPosition = function(board) {
board.deMarkLastMove(true);
var vBoard = board.conv.getStartPos(board.flipped);
board.syncBoard(vBoard);
board.conv.resetToStart();
updateMoveInfo(board);
updateMovePane(board);
};
makeBwMove = function(board, noUpdate) {
var move = board.conv.prevMove();
if (move == null)
return;
if (!noUpdate) {
board.deMarkLastMove(true);
board.markLastMove();
updateMoveInfo(board);
updateMovePane(board, true);
}
for(var i=move.actions.length;i > 1;i-=2) {
var frst = move.actions[i-1].clone();
var snd = move.actions[i-2].clone();
var tmpM = new MySquare();
tmpM.piece = frst.piece;
tmpM.color = frst.color;
frst.piece = snd.piece;
frst.color = snd.color;
snd.piece = tmpM.piece;
snd.color = tmpM.color;
frst.piece = move.oPiece;
frst.color = move.oColor;
if (move.pPiece)
snd.piece = move.pPiece;
board.drawSquare(frst);
board.drawSquare(snd);
}
if (move.enP) {
var x = move.enP.x, y = move.enP.y;
if (board.flipped) {
x=7-x;
y=7-y;
}
var sq = board.pos[x][y];
sq.appendChild(board.getImg(move.enP.piece, move.enP.color));
}
};
this.markLastMove = function() {
if (!this.opts['markLastMove'])
return;
try {
var move = this.conv.moves[this.conv.iteIndex-1].actions[1];;
var piece = this.pos[move.x][move.y];
if (this.flipped) {
piece = this.pos[7-move.x][7-move.y];
}
// on konq the bg contains "initial initial initial "
// i guess xtra information. Anyways setting the
// background to a color containing the "initial"
// parts fails. Go figure
piece.lastBg = piece.style.backgroundColor.replace(/initial/g, "");
// AH: Changed highlight color below to the variable.
piece.style.backgroundColor = this.opts['move_highlight_color'];
this.lastSquare = piece;
}
catch (e) {}
};
this.deMarkLastMove = function() {
var move = this.conv.moves[this.conv.iteIndex-2];
if (arguments.length && arguments[0]) {
move = this.conv.moves[this.conv.iteIndex-1];
}
if (this.conv.iteIndex+1 == this.conv.moves.length)
move = this.conv.getCurMove();
if (move) {
move = move.actions[1];
var piece = this.pos[move.x][move.y];
if (this.flipped)
piece = this.pos[7-move.x][7-move.y];
if (piece.lastBg)
piece.style.background = piece.lastBg;
}
if (this.lastSquare && this.lastSquare.lastBg) {
this.lastSquare.style.backgroundColor = this.lastSquare.lastBg;
this.lastSquare = null;
}
};
/*
Toggle moves pane, actually not toggle but
showing it depending the 'flag'.
*/
this.toggleMoves = function(flag) {
if (flag == "flip")
flag = this.movesTd.style.visibility=="hidden";
if (flag) {
this.movesTd.style.display = "block";
this.movesTd.style.visibility = "visible";
}
else {
this.movesTd.style.display = "none";
this.movesTd.style.visibility = "hidden";
}
};
this.toggleComments = function(flag) {
if (flag == "flip")
flag = !this.opts['showComments'];
if (flag) {
this.opts['showComments'] = true;
}
else {
this.opts['showComments'] = false;
}
var list = this.movesTd.getElementsByTagName("span");
if (list) {
for (var i=0;i0)
set = arguments[0];
if (arguments.length>1)
pref = arguments[1];
var img;
for (var i in this.imageNames[set]) {
for (var j in this.imageNames[set][i]) {
img = new Image();
img.src = this.imageNames[set][i][j];
}
}
};
};