
// Oggetto che serve soprattutto come namespacing
var GSlib = {

	/* DEBUG */
	GSLog: function() {
		this.m = new Array();
		this.get = function() {
			return this.m.join("\n");
		}
	},
	
	Util: {
		getCapture: function (objFen, a, b, x, y) {
			return (	objFen.board[x][y] != ' ' ||
						(objFen.board[a][b].match(/^[Pp]$/) &&
							objFen.enPassant == GSlib.GSChessboard.coordToCol(y) + GSlib.GSChessboard.coordToRow(x))
					);
		}
	},
	
	/**
		Oggetto che mi permette di raggruppare quelle funzioni che servono
		per il Chess 960
	*/
	Chess960: {
		conv1: function (info, p, y) {
			if (	(p == 'K' && info.objFen.canWhiteShortCastle != null && y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteShortCastle.toLowerCase())) ||
					(p == 'k' && info.objFen.canBlackShortCastle != null && y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackShortCastle.toLowerCase()))
				) return 'O-O';
			if (	(p == 'K' && info.objFen.canWhiteLongCastle != null && y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteLongCastle.toLowerCase())) ||
					(p == 'k' && info.objFen.canBlackLongCastle != null && y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackLongCastle.toLowerCase()))
				) return 'O-O-O';
			return null;
		},
		
		convShortCastle: function (objFen) {
			if (objFen.who == 'w') {
				var king = GSlib.GSCheck.findKing(objFen.board, 'K');
				return GSlib.GSChessboard.coordToCol(parseInt(king.charAt(1))) + '1' + objFen.canWhiteShortCastle.toLowerCase() + '1';
			}
			else if (objFen.who == 'b') {
				var king = GSlib.GSCheck.findKing(objFen.board, 'k');
				return GSlib.GSChessboard.coordToCol(parseInt(king.charAt(1))) + '8' + objFen.canBlackShortCastle.toLowerCase() + '8';
			}
			return null;
		},
		
		convLongCastle: function (objFen) {
			if (objFen.who == 'w') {
				var king = GSlib.GSCheck.findKing(objFen.board, 'K');
				return GSlib.GSChessboard.coordToCol(parseInt(king.charAt(1))) + '1' + objFen.canWhiteLongCastle.toLowerCase() + '1';
			}
			else if (objFen.who == 'b') {
				var king = GSlib.GSCheck.findKing(objFen.board, 'k');
				return GSlib.GSChessboard.coordToCol(parseInt(king.charAt(1))) + '8' + objFen.canBlackLongCastle.toLowerCase() + '8';
			}
		}
	},

	/**
		Questo è l'oggetto che viene ricevuto dalla libreria quando
		viene eseguita una mossa. Basilarmente contiene la posizione
		in formato FEN e la mossa eseguita (casa di partenza, casa di arrivo)
		per esempio: e2e4
		
		La lunghezza della stringa della mossa è pertanto costante
		
		'prom' rappresenta il pezzo che deve essere sostituito al pedone
		in caso di promozione. Se non c'è promozione il suo valore è 'null'
	*/
	GSMoveMessage: function (strFen, move, prom, variant) {
		this.strFen = strFen;
		this.move = move;
		this.prom = prom;
		this.variant = variant;
		this.msg = null;				// Messaggio da inserire nella MessageBoard
	},
	
	/**
		Funzione che serve a convertire una mossa nella forma contratta
		come Cd7 oppure Txf1, ecc.. nella forma casaDiPartenza-casaDiArrivo
		come e2e4 e viceversa
		
		objFen rappresenta la posizione della scacchiera nella quale
		eseguire la mossa 'move'
	*/
	GSMoveConverter: function (objFen, move, variant) {
		if (move.match(/^[a-h][1-8][a-h][1-8]$/)) {
			// Devo convertire da g1f3 a Cf3
			
			var m = '';
			
			var a = GSlib.GSChessboard.rowToCoord(move.charAt(1));
			var b = GSlib.GSChessboard.colToCoord(move.charAt(0));
			
			var x = GSlib.GSChessboard.rowToCoord(move.charAt(3));
			var y = GSlib.GSChessboard.colToCoord(move.charAt(2));
			
			// Se non prendo alcun pezzo allora ritorno false
			// TODO: gestione dell'en passant
			if (objFen.board[a][b] == ' ') return false;
			
			var capture = GSlib.Util.getCapture(objFen, a, b, x, y);
			
			if (capture === true) m = 'x';
			
			m = m + move.charAt(2) + move.charAt(3);
			
			var p = objFen.board[a][b];
			if (p.match(/[pP]/)) {
				// Prendo un pedone
				if (capture === true) m = move.charAt(0) + m;
			}
			else {
				// Ho preso un pezzo
				
				// Controllo se altri pezzi dello stesso tipo giungono in 'move'
				var info = new GSlib.GSMoveInfo(objFen, a, b, x, y, capture);
				var piece = new GSlib.GSPiece(null, null);
				var coord = piece.getCoord(info);
				
				m = p.toUpperCase() + coord + m;
			}
			
			// Controllo speciale per gli arrocchi
			if (variant == 'chess 960') {
				var ret = GSlib.Chess960.conv1(info, p, y);
				if (ret != null) return ret;
			}
			else {
				if (p.match(/[kK]/) && b == 4) {
					if (y == 6) return 'O-O';
					if (y == 2) return 'O-O-O';
				}
			}
			
			return m;
		}
		else {
			// Devo convertire da Cf3 a g1f3
			var a = move.split(/[^a-zA-Z0-9\-]/);
			var m = a[0].replace('x', '');
			
			var x = GSlib.GSChessboard.rowToCoord(m.charAt(m.length-1));
			var y = GSlib.GSChessboard.colToCoord(m.charAt(m.length-2));
			
			if (m.match(/^[0|o]-[0|o]$/i)) {
				// Arrocco Corto
				if (variant == 'chess 960') {
					var ret = GSlib.Chess960.convShortCastle(objFen);
					if (ret != null) return ret;
				}
				else {
					// Scacchi classici (var = null)
					if (objFen.who == 'w') return 'e1g1';
					else return 'e8g8';
				}
			}
			else if (m.match(/^[0|o]-[0|o]-[0|o]$/i)) {
				// Arrocco Lungo
				if (variant == 'chess 960') {
					var ret = GSlib.Chess960.convLongCastle(objFen);
					if (ret != null) return ret;
				}
				else {
					// Scacchi classici (var = null)
					if (objFen.who == 'w') return 'e1c1';
					else return 'e8c8';
				}
			}
			else if (m.length == 2) {
				// E un pedone che avanza
				if (objFen.who == 'w') {
					if (objFen.board[x+1][y] == 'P') return m.charAt(0)+GSlib.GSChessboard.coordToRow(x+1)+m;
					if (objFen.board[x+2][y] == 'P') return m.charAt(0)+GSlib.GSChessboard.coordToRow(x+2)+m;
				}
				else {
					if (objFen.board[x-1][y] == 'p') return m.charAt(0)+GSlib.GSChessboard.coordToRow(x-1)+m;
					if (objFen.board[x-2][y] == 'p') return m.charAt(0)+GSlib.GSChessboard.coordToRow(x-2)+m;
				}
			}
			else if (isLower(m.charAt(0))) {
				// E' un pedone che mangia
				if (objFen.who == 'w') {
					if (objFen.board[x+1][y+1] == 'P' || objFen.board[x+1][y-1] == 'P')
						return m.charAt(0)+GSlib.GSChessboard.coordToRow(x+1)+m.charAt(1)+m.charAt(2);
				}
				else {
					if (objFen.board[x-1][y+1] == 'p' || objFen.board[x-1][y-1] == 'p')
						return m.charAt(0)+GSlib.GSChessboard.coordToRow(x-1)+m.charAt(1)+m.charAt(2);
				}
			}
			else if (isUpper(m.charAt(0))) {
				// Muove un pezzo
				var c = m.charAt(0);
				if (objFen.who == 'b') c = c.toLowerCase();
				
				var a = GSlib.GSCheck.findPiece(objFen.board, c);
				
				for (var i in a) {
					var info = new GSlib.GSMoveInfo(objFen, parseInt(a[i].charAt(0)), parseInt(a[i].charAt(1)), x, y, false);
					info.variant = variant;
					
					var piece = GSlib.GSChessboard.getPiece(info.objFen.board, info.a, info.b);
					
					// Se non prendo alcun pezzo ritorno false
					if (piece === null) continue;
					
					var regObj = piece.superr.isRegularMove(info);					
					if (regObj === false) continue;
					
					// Ora controllo se ci sono ulteriori coordinate che identificano
					// il pezzo da muovere.
					if (m.length == 4) {
						var co = m.charAt(1);
						
						if (Utility.isInteger(co)) {
							// Se la coordinata è un numero devo confrontarla con info.a
							if (co != GSlib.GSChessboard.coordToRow(info.a)) continue;
						}
						else {
							// Se la coordinata è una lettera devo confrontarla con info.b
							if (co != GSlib.GSChessboard.coordToCol(info.b)) continue;
						}
					}
					else if (m.length == 5) {
					
					}
					else if (m.length > 5) return false;
					
					return GSlib.GSChessboard.coordToCol(info.b) + GSlib.GSChessboard.coordToRow(info.a) + m.charAt(m.length-2) + m.charAt(m.length-1);
				}
				return false;
			}
			return false;
		}
	},
	
	GSMoveInfo: function (objFen, a, b, x, y, capture) {
		this.objFen = objFen;
		this.a = a;
		this.b = b;
		this.x = x;
		this.y = y;
		this.capture = capture;
		this.prom = null;				//
		this.variant = null;
	},
	
	GSFen: function () {	
		this.board = null;						// In forma di Array
		this.who = null;
		this.canWhiteShortCastle = null;
		this.canWhiteLongCastle = null;
		this.canBlackShortCastle = null;
		this.canBlackLongCastle = null;
		this.enPassant = null;
		this.semiMoves = null;
		this.totMoves = null;
		
		/**
			Il Fen in formato stringa passato come parametro viene analizzato
			e vengono settati correttamente tutte le variabili della classe.
		*/
		this.analyze = function(strFen) {
			//if (typeof(strFen) == 'undefined') return;
			
			var chessInfo = strFen.split(' ');
			
			this.board = GSlib.GSChessboard.fenToBoard(strFen);		// in forma di Array
			this.who = chessInfo[1];
			this.enPassant = chessInfo[3];
			
			this.semiMoves = chessInfo[4] ? parseInt(chessInfo[4]) : 0;
			this.totMoves = chessInfo[5] ? parseInt(chessInfo[5]) : 1;
			
			// Arrocco (esteso al Chess960)
			// Esempio di FEN 960: brqbknnr/pppppppp/8/8/8/8/PPPPPPPP/BRQBKNNR w HBhb - 0 1
			
			if (chessInfo[2] == '-') return;
			
			var wKing = GSlib.GSCheck.findKing(this.board, 'K');
			var bKing = GSlib.GSCheck.findKing(this.board, 'k');
			
			var len = chessInfo[2].length;
			for (var i=0; i<len; i++) {
				var c = chessInfo[2].charAt(i);
				
				if (isUpper(c)) {
					if (c.match(/[KQ]/) != null) {
						if (c == 'K') this.canWhiteShortCastle = c;
						else this.canWhiteLongCastle = c;
					}
					else {
						if ( GSlib.GSChessboard.colToCoord(c.toLowerCase()) > parseInt(wKing.charAt(1)) ) this.canWhiteShortCastle = c;
						else this.canWhiteLongCastle = c;
					}
				}
				else if (isLower(c)) {
					if (c.match(/[kq]/) != null) {
						if (c == 'k') this.canBlackShortCastle = c;
						else this.canBlackLongCastle = c;
					}
					else {
						if ( GSlib.GSChessboard.colToCoord(c.toLowerCase()) > parseInt(bKing.charAt(1)) ) this.canBlackShortCastle = c;
						else this.canBlackLongCastle = c;
					}
				}
			}
			
		}
		
		/**
			Dalle variabili della classe ottengo il Fen in formato stringa
		*/
		this.getFen = function() {
			var chessboard = "";
			if (this.board == null) return;
			
			var k = 0;
			for (var i=0; i<8; i++) {
				for (var j=0; j<8; j++) {
					if (this.board[i][j] != ' ') {
						if (k>0) chessboard = chessboard + k;
						chessboard = chessboard + this.board[i][j];
						k=0;
					}
					else k++;
				}
				if (k>0) chessboard = chessboard + k;
				chessboard = chessboard + "/";
				k=0;
			}
			chessboard = chessboard.substr(0, chessboard.length -1);		// Tolgo l'ultima "/"
			
			// ----------------
			
			var arrocco = '';
			if (this.canWhiteShortCastle != null) arrocco = arrocco + this.canWhiteShortCastle;
			if (this.canWhiteLongCastle != null) arrocco = arrocco + this.canWhiteLongCastle;
			if (this.canBlackShortCastle != null) arrocco = arrocco + this.canBlackShortCastle;
			if (this.canBlackLongCastle != null) arrocco = arrocco + this.canBlackLongCastle;
			if (arrocco == '') arrocco = '-';
			
			var fen = chessboard+" "+this.who+" "+arrocco+" "+this.enPassant+" "+this.semiMoves+" "+this.totMoves;
			return fen;
		}
	},
	
	/**
		Classe Principale che si viene chiamata dalle altre
		librerie Javascript. Il suo compito è quello di ricevere un
		oggetto GSMoveMessage, e ritornare la nuova posizione in formato
		FEN se la mossa è corretta.
	
		FALSE altrimenti
	*/
	GSMoveManager: function (message) {
		this.message = message;
		/* DEBUG */ this.log = null;
		
		/**
			Funzione che si occupa di analizzare il messaggio e di
			ritornare il nuovo FEN.
		*/
		this.play = function() {
			var move = this.message.move;			
			var objFen = new GSlib.GSFen();
			objFen.analyze(this.message.strFen);
			
			var a = GSlib.GSChessboard.rowToCoord(move.charAt(1));	// x di partenza
			var b = GSlib.GSChessboard.colToCoord(move.charAt(0));	// y di partenza
			
			var x = GSlib.GSChessboard.rowToCoord(move.charAt(3));	// x di arrivo
			var y = GSlib.GSChessboard.colToCoord(move.charAt(2));	// y di arrivo
			
			var capture = GSlib.Util.getCapture(objFen, a, b, x, y);
			
			var info = new GSlib.GSMoveInfo(objFen, a, b, x, y, capture);
			info.prom = this.message.prom;
			info.variant = this.message.variant;
			
			/* DEBUG */ info.log = new GSlib.GSLog();
			var response = this.execute(info);
			/* DEBUG */ this.log = info.log;
			
			return response;
		}
		
		this.execute = function(info) {
			var sourceObjFen = Utility.clone(info.objFen);

			if (GSlib.GSCheckRegularMove(info, null) === false) return false;
			
			// La seguente linea di codice deve essere prima del controllo dello scacco per
			// permettere al pedone di minacciare correttamente	
			info.objFen.who = (info.objFen.who == 'w') ? 'b' : 'w';
			
			// Ora che ho eseguito la mossa devo verificare che il re non sia sotto scacco
			var bCheck = GSlib.GSCheck.isCheckTo(info, 'K');			// Scacco al Bianco
			var wCheck = GSlib.GSCheck.isCheckTo(info, 'k');			// Scacco al Nero
			
			if (GSlib.GSCheckCheck(info, wCheck, bCheck) === true) return false;
			
			// Ci sono alcune variabili di objFen che devo trattare qui
			if (info.objFen.who == 'w') info.objFen.totMoves = info.objFen.totMoves+1;
			if (info.capture === true) info.objFen.semiMoves = 0;
			
			// Se è stata mangiata una torre devo aggiornare il FEN per gli arrocchi
			this.checkEatingRook(info, sourceObjFen);
			
			// Ricavo la mossa secondo la notazione standard PGN (es: Cbd7+)
			var m = GSlib.GSMoveConverter(sourceObjFen, this.message.move, info.variant);
			
			// Osservo se c'è stata promozione di un pedone
			if (info.prom != null) m = m + '=' + info.prom;
			
			var msgBoard = null;
			
			if (wCheck || bCheck) {
				if (GSlib.GSCheckMate(info)) {
					// Controllo se è scacco matto
					m = m + '#';
					msgBoard = 'Checkmate';
				}
				else {
					// E' solo scacco
					m = m+'+';
					msgBoard = 'Check';
				}
			}
			else {
				// Controllo se è stallo
				if (info.objFen.semiMoves >= 100) msgBoard = 'Draw 50 Moves';
				else if (GSlib.GSStaleMate(info)) msgBoard = 'Stalemate';
			}
			
			
			var response = new GSlib.GSMoveMessage(info.objFen.getFen(), m, null, info.variant);
			response.msg = msgBoard;
			
			return response;
		}
		
		/**
			Questa funzione serve a controllare se è stata mangiata una torre,
			se così fosse allora bisogna fare qualche controllo per aggiornare le
			possibilità di arrocco dei due giocatori
		*/
		this.checkEatingRook = function (info, sourceObjFen) {
			if (sourceObjFen.board[info.x][info.y] == 'R') {
				if (info.objFen.canWhiteShortCastle != null) {
					if (	info.variant == 'chess 960' && info.x == 7 &&
							info.y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteShortCastle.toLowerCase())
						) info.objFen.canWhiteShortCastle = null;
					else if (info.variant == null && info.x == 7 && info.y == 7) info.objFen.canWhiteShortCastle = null;
				}
				
				if (info.objFen.canWhiteLongCastle != null) {
					if (	info.variant == 'chess 960' && info.x == 7 &&
							info.y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteLongCastle.toLowerCase())
						) info.objFen.canWhiteLongCastle = null;
					else if (info.variant == null && info.x == 7 && info.y == 0) info.objFen.canWhiteLongCastle = null;
				}
			}
			
			else if (sourceObjFen.board[info.x][info.y] == 'r') {
				if (info.objFen.canBlackShortCastle != null) {
					if (	info.variant == 'chess 960' && info.x == 0 &&
							info.y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackShortCastle.toLowerCase())
						) info.objFen.canBlackShortCastle = null;
					else if (info.variant == null && info.x == 0 && info.y == 7) info.objFen.canBlackShortCastle = null;
				}
				
				if (info.objFen.canBlackLongCastle != null) {
					if (	info.variant == 'chess 960' && info.x == 0 &&
							info.y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackLongCastle.toLowerCase())
						) info.objFen.canBlackLongCastle = null;
					else if (info.variant == null && info.x == 0 && info.y == 0) info.objFen.canBlackLongCastle = null;
				}
			}
		}
	},
	
	/* ----------------------------------- */
	/*                                     */
	/* ----------------------------------- */
	
	/**
		Classe che contiene alcune utility per la scacchiera
	*/
	GSChessboard: {
		rows: ['8', '7', '6', '5', '4', '3', '2', '1'],
		cols: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
	
		/**
			Funzione che ritorna un array bidimensionale 8x8 vuoto.
			La scacchiera è pensata nel seguente modo:
			
			0		r n b q k b n r
			1     p p p p p p p p
			2
			3
			4
			5
			6  	P P P P P P P P
			7		R N B Q K B N R
			
			x/y	0 1 2 3 4 5 6 7
		*/
		getEmptyBoard: function() {
			var array = new Array();
			
			for (var i=0; i<8; i++) {
				var trArray = new Array();
				for (var j=0; j<8; j++) trArray.push(' ');
				array.push(trArray);
			}
			
			return array;
		},
	
		/**
			dato come input il FEN ritorna un array che
			rappresenta la scacchiera
		*/
		fenToBoard: function(fen) {
			var chessInfo = fen.split(" ");
			
			// Scacchiera
			var chessBoardFen = chessInfo[0];
			var rows = chessBoardFen.split('/');
			
			var k=0;	// Indice delle celle
			var arrayBoard = GSlib.GSChessboard.getEmptyBoard();	// Array bidimensionale vuoto
			for (var i=0; i<8; i++) {
				var charInfos = rows[i].length;
				
				for (var c=0; c<charInfos; c++) {
					if (!isInteger(rows[i].charAt(c))) {
						arrayBoard[i][k%8] = rows[i].charAt(c);
						k++;
					}
					else {
						k = k + parseInt(rows[i].charAt(c));
					}
				}
			}
			
			return arrayBoard;
		},
		
		/**
			Operazioni sulle coordinate di colonna
		*/
		coordToCol: function(c) {
			return this.cols[c];
		},
		
		colToCoord: function(c) {
			for (var index in this.cols) {
				if (this.cols[index] == c) return parseInt(index);
			}
			return null;
		},
		
		/**
			Operazioni sulle coordinate di riga
		*/
		coordToRow: function(c) {
			return this.rows[c];
		},
		
		rowToCoord: function(c) {
			for (var index in this.rows) {
				if (this.rows[index] == c) return parseInt(index);
			}
			return null;
		},
		
		/**
			Operazione sul Pezzo.
			
			Un interessante obiettivo sarebbe quello
			di limitare a questa funzione la gestione della scelta del Pezzo
			per cercare in futuro di ottimizzare questa parte di codice.
		*/
		getPiece: function(board, a, b) {
			switch (board[a][b]) {
				case 'R': return new GSlib.GSPieceRook('R'); 
				case 'N': return new GSlib.GSPieceKnight('N');
				case 'B': return new GSlib.GSPieceBishop('B');
				case 'Q': return new GSlib.GSPieceQueen('Q');
				case 'K': return new GSlib.GSPieceKing('K');
				case 'P': return new GSlib.GSPiecePawn('P');
				
				case 'r': return new GSlib.GSPieceRook('r'); 
				case 'n': return new GSlib.GSPieceKnight('n');
				case 'b': return new GSlib.GSPieceBishop('b');
				case 'q': return new GSlib.GSPieceQueen('q');
				case 'k': return new GSlib.GSPieceKing('k');
				case 'p': return new GSlib.GSPiecePawn('p');
				
				default: return null;
			}
			return null;
		}
	},
	
	
		
	/**
		Controlla completamente che la mossa sia regolare.
		
		Quindi controlla movimento ed eventuale scacco al Re nella nuova casa,
		compreso il controllo dell'arrocco.
	*/
	GSCheckRegularMove: function (info, piece) {
		if (piece === null) {
			piece = GSlib.GSChessboard.getPiece(info.objFen.board, info.a, info.b);
		
			// Se non prendo alcun pezzo ritorno false
			if (piece === null) return false;
			
			// Se prendo muove il Bianco e non sta a lui ritorno false,
			// stessa cosa per il Nero
			if (piece.superr.getColor() != info.objFen.who) return false;
		}
		
		var regObj = piece.superr.isRegularMove(info);
		
		// Se la mossa non è regolare ritorno false;
		/* DEBUG */ if (regObj === false) info.log.m.push('Mossa Non Regolare: '+info.a+' '+info.b+' '+info.x+' '+info.y);
		if (regObj === false) return false;
		
		// Se sto eseguendo l'arrocco allora controllo che le case necessarie siano libere
		if (regObj.m != null && regObj.m[0] == 'castle' &&
			GSlib.GSCheck.checkCastle(info, regObj.m[1], regObj.m[2], 1) === false) return false;
		
		// Eseguo la mossa
		piece.abstractt.move(info);
		
		// Ho un caso particolare da controllare. Se il giocatore sta facendo l'arrocco
		// e il re si muove in una delle case sotto scacco allora non può eseguire l'arrocco.
		if (regObj.m != null && regObj.m[0] == 'castle' &&
			GSlib.GSCheck.checkCastle(info, regObj.m[1], regObj.m[2], 2) === false) return false;
		
		return true;
	},
	
	/**
		La variabile check ritorna vero se il giocatore ha compiuto una mossa
		che non ripara il suo Re dallo scacco avversario.
	*/
	GSCheckCheck: function (info, wCheck, bCheck) {
		var check = false;
		if (info.objFen.who == 'b' && bCheck) check = true;
		else if (info.objFen.who == 'w' && wCheck) check = true;
		
		return check;
	},
	
	GSStaleMate: function (info) {
		// Proviamo a fare un algoritmo Brute Force
		var a,b,x,y;
		var sourceObjFen = Utility.clone(info.objFen);
		
		// i e j sono le coordinate di partenza
		for (a=0; a<8; a++) {
			for (b=0; b<8; b++) {
			
				var piece = GSlib.GSChessboard.getPiece(info.objFen.board, a, b);
				if (piece === null) continue;
				if (piece.superr.getColor() != info.objFen.who) continue;
			
				for (x=0; x<8; x++) {
					for (y=0; y<8; y++) {
						var capture = GSlib.Util.getCapture(sourceObjFen, a, b, x, y);
						var infoMate = new GSlib.GSMoveInfo(sourceObjFen, a, b, x, y, capture);
						infoMate.log = info.log;
						
						if (GSlib.GSCheckRegularMove(infoMate, piece) === false) continue;
						
						sourceObjFen = Utility.clone(info.objFen);
						infoMate.objFen.who = (infoMate.objFen.who == 'w') ? 'b' : 'w';
						
						var bCheck = GSlib.GSCheck.isCheckTo(infoMate, 'K');			// Scacco al Bianco
						var wCheck = GSlib.GSCheck.isCheckTo(infoMate, 'k');			// Scacco al Nero
						
						if (GSlib.GSCheckCheck(infoMate, wCheck, bCheck) === true) continue;
						
						return false;
					}
				}
			
			}
		}
		
		return true;
	},
	
	GSCheckMate: function (info) {
		// Proviamo a fare un algoritmo Brute Force
		var a,b,x,y;
		var sourceObjFen = Utility.clone(info.objFen);
		
		// i e j sono le coordinate di partenza
		for (a=0; a<8; a++) {
			for (b=0; b<8; b++) {
			
				var piece = GSlib.GSChessboard.getPiece(info.objFen.board, a, b);
				if (piece === null) continue;
				if (piece.superr.getColor() != info.objFen.who) continue;
			
				for (x=0; x<8; x++) {
					for (y=0; y<8; y++) {
						var capture = GSlib.Util.getCapture(sourceObjFen, a, b, x, y);
						var infoMate = new GSlib.GSMoveInfo(sourceObjFen, a, b, x, y, capture);
						infoMate.log = info.log;
						
						if (GSlib.GSCheckRegularMove(infoMate, piece) === false) continue;
						
						sourceObjFen = Utility.clone(info.objFen);
						infoMate.objFen.who = (infoMate.objFen.who == 'w') ? 'b' : 'w';
						
						var bCheck = GSlib.GSCheck.isCheckTo(infoMate, 'K');			// Scacco al Bianco
						var wCheck = GSlib.GSCheck.isCheckTo(infoMate, 'k');			// Scacco al Nero
						
						if (GSlib.GSCheckCheck(infoMate, wCheck, bCheck) === false) return false;
					}
				}
			
			}
		}
		
		return true;
	},
	
	/**
		Oggetto che mi serve per analizzare gli scacchi
	*/
	GSCheck: {
		i: 0,
		j: 0,
		
		findPiece: function(board, c) {
			var i,j;
			var a = new Array();		// contiene le coordinate del pezzo cercato. Es: 52 (c3)
			
			for (i=0; i<8; i++) {
				for (j=0; j<8; j++) {
					if (board[i][j] == c) a.push(''+i+j);
				}
			}
			
			return a;
		},
		
		/**
			Questa funzione cerca il pezzo desiderato (il primo)
			e ritorna le coordinate del pezzo nel formato xy,
			che di sicuro è un numero di 2 cifre.
		*/
		findKing: function(board, id) {
			var a = GSlib.GSCheck.findPiece(board, id);
			return a[0];
		},
	
		/**
			Questa funzione essendo richiamata ogni volta che viene
			eseguita una mossa può risultare pesante in termini
			di prestazione. Espandendo il codice si può cercare di
			velocizzare l'algoritmo.
		*/
		isCheckTo: function(info, id) {
			// cerco il pezzo
			var i,j;
			var board = info.objFen.board;
			var king = GSlib.GSCheck.findKing(board, id);
			var x = parseInt(king.charAt(0));
			var y = parseInt(king.charAt(1));		
			
			for (i=0; i<8; i++) {
				for (j=0; j<8; j++) {
					var piece = GSlib.GSChessboard.getPiece(board, i, j);
					if (piece === null || piece.superr.id == id) continue;
					var infoCheck = new GSlib.GSMoveInfo(info.objFen, i, j, x, y, true);
					infoCheck.variant = info.variant;
					
					var regObj = piece.superr.isRegularMove(infoCheck);
					/* DEBUG */ if (regObj !== false) info.log.m.push('Il pezzo: '+i+' '+j+' da scacco '+x+' '+y+' | '+infoCheck.objFen.who+' | '+board[i][j]);
					if (regObj !== false) return regObj.b;
				}
			}
			
			return false;
		},
		
		/**
			Questa funzione viene richiamata solo se deve essere eseguito un arrocco e si
			deve controllare che le case destinate all'arrocco siano esente dallo scacco e
			che le case necessarie siano libere.
			
			TODO: Da estendere per il Chess960
			
			PS. Il nome della funzione è da intendere: L'arrocco è corretto?
		*/
		checkCastle: function(info, checkCase, emptyCase, opt) {
			var i,j,k;								// i,j mi servono per raccogliere tutti i pezzi necessari
			var board = info.objFen.board;
			
			if (opt == 1) {
				// Controllo che le case siano libere
				for (k in emptyCase) {
					var x = parseInt(emptyCase[k].charAt(0));
					var y = parseInt(emptyCase[k].charAt(1));
					
					/* DEBUG */ if (board[x][y] != ' ') info.log.m.push('Casa occupata: '+board[x][y]+' '+x+' '+y);
					if (board[x][y] != ' ') return false;
				}
			}
			else if (opt == 2) {	
				// Controllo che le case non siano sotto scacco		
				for (i=0; i<8; i++) {
					for (j=0; j<8; j++) {
						var piece = GSlib.GSChessboard.getPiece(board, i, j);
						if (piece === null || piece.superr.getColor() == info.objFen.who) continue;
						
						// Ora che ho preso un pezzo utile controllo se questo può andare in una
						// delle case dello scacco
						for (k in checkCase) {
							var x = parseInt(checkCase[k].charAt(0));
							var y = parseInt(checkCase[k].charAt(1));
							var infoCheck = new GSlib.GSMoveInfo(info.objFen, i, j, x, y, true);
							infoCheck.variant = info.variant;
							
							var regObj = piece.superr.isRegularMove(infoCheck);
							/* DEBUG */ if (regObj !== false) info.log.m.push('Il pezzo: '+i+' '+j+' da scacco '+x+' '+y+' | '+infoCheck.objFen.who);
							if (regObj !== false) return false;
						}
					}
				}
			}
			
			return true;
		}
	},
	
	/* ----------------------------------- */
	/*                                     */
	/* ----------------------------------- */
	
	GSPiece: function (obj, id) {
		this.obj = obj;			// obj rappresenta l'oggetto del pezzo (WhiteKnight)
		this.id = id;				// id rappresenta l'id del pezzo: N, n, Q, ecc..
		this.hasMove = false;
		
		this.getColor = function() {
			if (isUpper(this.id)) return 'w';
			else return 'b';
		}
		
		/**
			Controlla se il pezzo in a,b può andare in x,y. Affinchè la mossa sia legale
			bisogna anche controllare che il re del pezzo mosso non sia sotto scacco, ma
			questo ulteriore controllo viene eseguito da un'altra parte.
		*/
		this.isRegularMove = function(info) {
			// Se il pezzo selezionato non e' quello interessato ritorno false
			if (info.objFen.board[info.a][info.b] != this.id) return false;
			
			// Guardo se e' una mossa corretta
			var regObj = this.obj.abstractt.isRightMove(info);
			if (regObj.b === false) return false;
		
			// Guardo se sto mangiando un alleato
			//
			// Ho divuto mettere questo if di controllo perchè negli scacchi960 quando
			// eseguo l'arrocco posiziono il Re sopra la torre, quindi virtualmente
			// mangio un alleato
			if (regObj.m == null || regObj.m[0] != 'castle')
				if (this.isAlly(info.objFen.board, info.a, info.b, info.x, info.y)) return false;
			
			return regObj;
		}
		
		/**
			Controlla se il pezzo in a,b è dello stesso colore di quello in x,y
		*/
		this.isAlly = function(board, a, b, x, y) {
			if (board[a][b] == ' ' || board[x][y] == ' ') return false;
			if ( (isUpper(board[a][b]) && isUpper(board[x][y])) || (isLower(board[a][b]) && isLower(board[x][y])) ) return true;
			else return false;
		},
		
		/**
			Funzione che controlla quanti pezzi dello stesso tipo di board[a][b]
			possono arrivare in x,y e ritorna un array con le loro coordinate.
		*/
		this.getCoord = function (info) {
			var i,c = '';
			var boola = false, boolb = false, more = false;
			var a = info.a, b = info.b, x = info.x, y = info.y;
			var board = info.objFen.board;
			
			var arr = GSlib.GSCheck.findPiece(board, board[a][b]);
			var len = (arr.length > 1) ? arr.length : 0;
			
			for (i=0; i<len; i++) {
				if (arr[i] == ''+a+b) continue;
				var piece = GSlib.GSChessboard.getPiece(board, a, b);
				var infoCheck = new GSlib.GSMoveInfo(info.objFen, parseInt(arr[i].charAt(0)), parseInt(arr[i].charAt(1)), x, y, info.capture);
				infoCheck.variant = info.variant;
				
				var regObj = piece.superr.isRegularMove(infoCheck);
				
				if (regObj === false) continue;
				more = true;
				
				// Confronto la colonna
				if (parseInt(arr[i].charAt(0)) == a) boolb = true;
				if (parseInt(arr[i].charAt(1)) == b) boola = true;
			}
			
			if (more === true) {
				if (boolb === false && boola === false) c = c + GSlib.GSChessboard.coordToCol(b);
				if (boolb === true) c = c + GSlib.GSChessboard.coordToCol(b);
				if (boola === true) c = c + GSlib.GSChessboard.coordToRow(a);
			}
			
			return c;
		}
	},
	
	/* ----------------------------------- */
	/*                                     */
	/* ----------------------------------- */
	
	/**
		Qui di seguito sono stati scritte le classe delle categorie dei Pezzi,
		ognuna di queste classi ha una funzione:
		
		I) isRightMove che serve per vedere se il pezzo può spostarsi da a,b in x,y secondo le sue regole
		II) move che serve per modificare la scacchiera. Di conseguenza anche objFen in modo appropriato
	*/
	
	/**
		Questa funzione è l'oggetto che deve inviato come ritorno quando si
		sta calcolando se un pezzo può eseguire quella mossa (isRightMove).
		
		Importante per la gestione di eventi particolari
	*/
	GSPieceResponse: function (b, m) {
		this.b = b;				// Rappresenta isRight
		this.m = m;				// Rappresenta il messaggio (enPassant, arrocco)
	},
	
	GSPawn: function () {
		this.isRightMove = function(info) {
			var board = info.objFen.board;
			var a = info.a; var b = info.b; var x = info.x; var y = info.y;
			var isRight = false, m = null;
			
			var piece = GSlib.GSChessboard.getPiece(board, a, b);
			if (piece !== null) {
			
				if (piece.superr.getColor() == 'w') {
					// Muove il Bianco
					if (info.capture === true && abs(b-y) == 1 && (x-a) == -1) {
					
						if (board[x][y] != ' ') isRight = true;
						else {
							// Controllo Presa en Passant
							var en = info.objFen.enPassant;
							if (en != '-') {
								var xe = 2;
								var ye = GSlib.GSChessboard.colToCoord(en.charAt(0));
								if (xe == x && ye == y) isRight = true;
							}
						}
						
					}
					else {
						// Avanzamento
						if ( b == y && (x-a) == -1 && board[x][y] == ' ') isRight = true;			// Avanzamento di una casa
						if ( b == y && a == 6 && x == 4 && board[5][b] == ' ' && board[4][b] == ' ') isRight = true;				// Avanzamento di due case
					}
				}
				
				else if (piece.superr.getColor() == 'b') {
					// Muove il Nero
					if (info.capture === true && abs(b-y) == 1 && (x-a) == 1) {
					
						if (board[x][y] != ' ') isRight = true;
						else {
							// Controllo Presa en Passant
							var en = info.objFen.enPassant;
							if (en != '-') {
								var xe = 5;
								var ye = GSlib.GSChessboard.colToCoord(en.charAt(0));
								if (xe == x && ye == y) isRight = true;
							}
						}
						
					}
					else {
						// Avanzamento
						if ( b == y && (x-a) == 1 && board[x][y] == ' ') isRight = true;			// Avanzamento di una casa
						if ( b == y && a == 1 && x == 3 && board[2][b] == ' ' && board[3][b] == ' ') isRight = true;				// Avanzamento di due case
					}
				}
				
			}
			
			return new GSlib.GSPieceResponse(isRight, m);
		}
		
		this.move = function(info) {
			// Sposto il Pezzo
			var objFen = info.objFen;
			objFen.board[info.x][info.y] = objFen.board[info.a][info.b];
			objFen.board[info.a][info.b] = ' ';

			// Devo modificare qualche cosa perchè sono a promozione
			if (info.prom != null) {
				if (objFen.who == 'w') objFen.board[info.x][info.y] = info.prom;
				else if (objFen.who == 'b') objFen.board[info.x][info.y] = info.prom.toLowerCase();
			}			
			
			if (info.enPassant != '-') {
				var xe = GSlib.GSChessboard.rowToCoord(objFen.enPassant.charAt(1));
				var ye = GSlib.GSChessboard.colToCoord(objFen.enPassant.charAt(0));
				
				if (xe == info.x && ye == info.y) {
					// Presa en Passant
					if (objFen.who == 'w') objFen.board[3][info.y] = ' ';
					else if (objFen.who == 'b') objFen.board[4][info.y] = ' ';
				}
			}
			
			// Aggiorno enPassant
			objFen.enPassant = '-';
			
			// Aggiorno semi mosse
			objFen.semiMoves = 0;
			
			// Aggiungo l'enPassant
			if ( info.b == info.y && info.a == 1 && info.x == 3 ) objFen.enPassant = GSlib.GSChessboard.coordToCol(info.b)+'6';
			else if ( info.b == info.y && info.a == 6 && info.x == 4) objFen.enPassant = GSlib.GSChessboard.coordToCol(info.b)+'3';
		}
	},
	
	GSKnight: function () {
		this.isRightMove = function(info) {
			var isRight = ((abs(info.a-info.x) == 2) && (abs(info.b-info.y) == 1)) ||
						((abs(info.a-info.x) == 1) && (abs(info.b-info.y) == 2));
			return new GSlib.GSPieceResponse(isRight, null);
		}
		
		this.move = function(info) {
			// Sposto il Pezzo
			var objFen = info.objFen;
			objFen.board[info.x][info.y] = objFen.board[info.a][info.b];
			objFen.board[info.a][info.b] = ' ';
			
			// Aggiorno enPassant
			objFen.enPassant = '-';
			
			// Aggiorno semi mosse
			objFen.semiMoves = objFen.semiMoves+1;
		}
	},
	
	GSBishop: function () {
		this.isRightMove = function(info) {
			var board = info.objFen.board;
			var a = info.a; var b = info.b; var x = info.x; var y = info.y;
			
			var isEmpty = true;
			var done = false;
			var i,j;
			
			// Guardo se i punti sono in diagonale
			if (abs(a-x) != abs(b-y)) return new GSlib.GSPieceResponse(false, null);
			
			// Mi muovo di una casa
			if ( (a+1 == x && b+1 == y) || (x == a+1 && y == b-1) ||
				(x == a-1 && y == b+1) || (x == a-1 && y == b-1) ) done = true;
			
			i=a+1; j=b+1;
			while(i<x && j<y) {
				done = true;
				if (board[i][j] != ' ') isEmpty = false;
				i++; j++;
			}
			
			i=a+1; j=b-1;
			while(i<x && j>y) {
				done = true;
				if (board[i][j] != ' ') isEmpty = false;
				i++; j--;
			}
			
			i=a-1; j=b+1;
			while(i>x && j<y) {
				done = true;
				if (board[i][j] != ' ') isEmpty = false;
				i--; j++;
			}
			
			i=a-1; j=b-1;
			while(i>x && j>y) {
				done = true;
				if (board[i][j] != ' ') isEmpty = false;
				i--; j--;
			}
			
			return new GSlib.GSPieceResponse((done && isEmpty), null);
		}
		
		this.move = function(info) {
			// Sposto il Pezzo
			var objFen = info.objFen;
			objFen.board[info.x][info.y] = objFen.board[info.a][info.b];
			objFen.board[info.a][info.b] = ' ';
			
			// Aggiorno enPassant
			objFen.enPassant = '-';
			
			// Aggiorno semi mosse
			objFen.semiMoves = objFen.semiMoves+1;
		}
	},
	
	GSRook: function () {
		this.isRightMove = function(info) {
			var board = info.objFen.board;
			var a = info.a; var b = info.b; var x = info.x; var y = info.y;
			
			var isEmpty = true;
			var done = false;
			
			// Mi muovo di una casa
			if ( (a+1 == x && b == y) || (x == a-1 && y == b) ||
				(x == a && y == b+1) || (x == a && y == b-1) ) done = true;
			
			if (a == x) {
				for (var i=b+1; i<y; i++) {
					done = true;
					if (board[a][i] != ' ') isEmpty = false;
				}
				for (var i=b-1; i>y; i--) {
					done = true;
					if (board[a][i] != ' ') isEmpty = false;
				}
			}
			else if (b == y) {
				for (var i=a+1; i<x; i++) {
					done = true;
					if (board[i][b] != ' ') isEmpty = false;
				}
				for (var i=a-1; i>x; i--) {
					done = true;
					if (board[i][b] != ' ') isEmpty = false;
				}
			}
			
			return new GSlib.GSPieceResponse((done && isEmpty), null);
		}
		
		this.move = function(info) {
			// Sposto il Pezzo
			var objFen = info.objFen;
			objFen.board[info.x][info.y] = objFen.board[info.a][info.b];
			objFen.board[info.a][info.b] = ' ';
			
			// Aggiorno enPassant
			objFen.enPassant = '-';
			
			// Aggiorno semi mosse
			objFen.semiMoves = objFen.semiMoves+1;
			
			// Siccome è stata mossa una torre devo controllare quale e
			// Modificare opportunamente il FEN per l'arrocco.
			// >>> Compatibile con Chess960
			if (objFen.who == 'w') {
				// Se non può arroccare nè corto nè lungo non sto a fare i calcoli
				if (objFen.canWhiteShortCastle == null && objFen.canWhiteLongCastle == null) return;
				
				var king = GSlib.GSCheck.findKing(objFen.board, 'K');
				var y = parseInt(king.charAt(1));
				
				if (y > info.b) objFen.canWhiteLongCastle = null;
				if (y < info.b) objFen.canWhiteShortCastle = null;
			}
			else if (objFen.who == 'b') {
				// Se non può arroccare nè corto nè lungo non sto a fare i calcoli
				if (objFen.canBlackShortCastle == null && objFen.canBlackLongCastle == null) return;
				
				var king = GSlib.GSCheck.findKing(objFen.board, 'k');
				var y = parseInt(king.charAt(1));
				
				if (y > info.b) objFen.canBlackLongCastle = null;
				if (y < info.b) objFen.canBlackShortCastle = null;
			}
		}
	},
	
	GSQueen: function () {
		this.isRightMove = function(info) {
			var r = new GSlib.GSRook();
			var b = new GSlib.GSBishop();
			return new GSlib.GSPieceResponse((r.isRightMove(info).b || b.isRightMove(info).b), null);
		}
		
		this.move = function(info) {
			// Sposto il Pezzo
			var objFen = info.objFen;
			objFen.board[info.x][info.y] = objFen.board[info.a][info.b];
			objFen.board[info.a][info.b] = ' ';
			
			// Aggiorno enPassant
			objFen.enPassant = '-';
			
			// Aggiorno semi mosse
			objFen.semiMoves = objFen.semiMoves+1;
		}
	},
	
	/**
		Non è aggiornato per il Chess960, ma è programmato solo
		per gli scacchi classici.
		
		Il controllo delle case dell'arrocco sotto scacco non può
		essere effuato all'interno di questa funzione perchè
		altrimenti si cadrebbe in un loop infinto.
	*/
	GSKing: function () {
		this.isRightMove = function(info) {
			var board = info.objFen.board;
			var i, a = info.a; var b = info.b; var x = info.x; var y = info.y;
			var m = null;
			
			var isRight = false;
				
			if (info.objFen.who == 'w') {
				if (info.variant == 'chess 960') {
					if (info.objFen.canWhiteShortCastle != null && x == 7 && y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteShortCastle.toLowerCase())) {
						isRight = true;
						var arrEmpty = new Array('75', '76');
						var arrCheck = new Array('76');
						for (i=b+1; i<5; i++) arrEmpty.push('7'+i);
						for (i=b; i<6; i++) arrCheck.push('7'+i);

						for (i in arrEmpty) {
							if (arrEmpty[i] == '7'+y || arrEmpty[i] == '7'+b) delete arrEmpty[i];
						}
						
						m = new Array('castle', arrCheck, arrEmpty);
					}
					else if (info.objFen.canWhiteLongCastle != null && x == 7 && y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteLongCastle.toLowerCase())) {
						isRight = true;
						var arrEmpty = new Array('72', '73');
						var arrCheck = new Array('72', '7'+b);
						for (i=b-1; i>3; i--) arrEmpty.push('7'+i);
						for (i=b-1; i>2; i--) arrCheck.push('7'+i);
						if (y == 0) arrEmpty.push('71');

						for (i in arrEmpty) {
							if (arrEmpty[i] == '7'+y || arrEmpty[i] == '7'+b) delete arrEmpty[i];
						}
						
						m = new Array('castle', arrCheck, arrEmpty);
					}
				}
				else {
					// Scacchi classici
					if (info.objFen.canWhiteShortCastle != null && x == 7 && y == 6) {
						isRight = true;
						m = new Array('castle', new Array('74', '75', '76'), new Array('75', '76'));
					}
					else if (info.objFen.canWhiteLongCastle != null && x == 7 && y == 2) {
						isRight = true;
						m = new Array('castle', new Array('74', '73', '72'), new Array('73', '72', '71'));
					}
				}
							
			}
			else if (info.objFen.who == 'b') {
				if (info.variant == 'chess 960') {
					
					if (info.objFen.canBlackShortCastle != null && x == 0 && y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackShortCastle.toLowerCase())) {
						isRight = true;
						var arrEmpty = new Array('05', '06');
						var arrCheck = new Array('06');
						for (i=b+1; i<5; i++) arrEmpty.push('0'+i);
						for (i=b; i<6; i++) arrCheck.push('0'+i);

						for (i in arrEmpty) {
							if (arrEmpty[i] == '0'+y || arrEmpty[i] == '0'+b) delete arrEmpty[i];
						}
						
						m = new Array('castle', arrCheck, arrEmpty);
					}
					else if (info.objFen.canBlackLongCastle != null && x == 0 && y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackLongCastle.toLowerCase())) {
						isRight = true;
						var arrEmpty = new Array('02', '03');
						var arrCheck = new Array('02', '0'+b);
						for (i=b-1; i>3; i--) arrEmpty.push('0'+i);
						for (i=b-1; i>2; i--) arrCheck.push('0'+i);
						if (y == 0) arrEmpty.push('01');

						for (i in arrEmpty) {
							if (arrEmpty[i] == '0'+y || arrEmpty[i] == '0'+b) delete arrEmpty[i];
						}
						
						m = new Array('castle', arrCheck, arrEmpty);
					}
				}
				else {
					// Arrocco per Nero
					if (info.objFen.canBlackShortCastle != null && x == 0 && y == 6) {
						isRight = true;
						m = new Array('castle', new Array('04', '05', '06'), new Array('05', '06'));
					}
					else if (info.objFen.canBlackLongCastle != null && x == 0 && y == 2) {
						isRight = true;
						m = new Array('castle', new Array('04', '03', '02'), new Array('03', '02', '01'));
					}
				}
			}
			
			if (isRight === false) {
				if ( ((abs(a-x) == 1) && (b == y)) ||
				((abs(b-y) == 1) && (a == x)) ||
				((abs(b-y) == 1) && (abs(a-x) == 1))) isRight = true;
			}
				
			return new GSlib.GSPieceResponse(isRight, m);
		}
		
		this.move = function(info) {
			
			// Sposto il Pezzo
			var objFen = info.objFen;
			objFen.board[info.x][info.y] = ' ';
			objFen.board[info.a][info.b] = ' ';
			
			// Aggiorno enPassant
			objFen.enPassant = '-';
			
			// Aggiorno semi mosse
			objFen.semiMoves = objFen.semiMoves+1;
			
			// Gestione dell'arrocco
			if (info.objFen.who == 'w') {
			
				if (	info.objFen.canWhiteShortCastle != null && info.x == 7 &&
						(	(info.variant == 'chess 960' && info.y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteShortCastle.toLowerCase())) ||
							(info.variant == null && info.y == 6)
						)
					) {
					if (info.variant == null) objFen.board[7][7] = ' ';
					objFen.board[7][6] = 'K';
					objFen.board[7][5] = 'R';
				}
				else if (	info.objFen.canWhiteLongCastle != null && info.x == 7 &&
								(	(info.variant == 'chess 960' && info.y == GSlib.GSChessboard.colToCoord(info.objFen.canWhiteLongCastle.toLowerCase())) ||
									(info.variant == null && info.y == 2)
								)
					) {
					if (info.variant == null) objFen.board[7][0] = ' ';
					objFen.board[7][2] = 'K';
					objFen.board[7][3] = 'R';
				}
				else objFen.board[info.x][info.y] = 'K';
				
				info.objFen.canWhiteShortCastle = null;
				info.objFen.canWhiteLongCastle = null;
			}
			else if (info.objFen.who == 'b') {
			
				if (	info.objFen.canBlackShortCastle != null && info.x == 0 &&
						(	(info.variant == 'chess 960' && info.y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackShortCastle.toLowerCase())) ||
							(info.variant == null && info.y == 6)
						)
					) {
					if (info.variant == null) objFen.board[0][7] = ' ';
					objFen.board[0][6] = 'k';
					objFen.board[0][5] = 'r';
				}
				else if (	info.objFen.canBlackLongCastle != null && info.x == 0 &&
								(	(info.variant == 'chess 960' && info.y == GSlib.GSChessboard.colToCoord(info.objFen.canBlackLongCastle.toLowerCase())) ||
									(info.variant == null && info.y == 2)
								)
					) {
					if (info.variant == null) objFen.board[0][0] = ' ';
					objFen.board[0][2] = 'k';
					objFen.board[0][3] = 'r';
				}
				else objFen.board[info.x][info.y] = 'k';
				
				info.objFen.canBlackShortCastle = null;
				info.objFen.canBlackLongCastle = null;
			}
		}
	},
	
	/* ----------------------------------- */
	/*                                     */
	/* ----------------------------------- */
	
	GSPieceKnight: function (id) {
		this.superr = new GSlib.GSPiece(this, id);		// super rappresenta l'oggetto Piece
		this.abstractt = new GSlib.GSKnight();				// abstract rappresenta la categoria del pezzo
	},
	
	GSPieceRook: function (id) {
		this.superr = new GSlib.GSPiece(this, id);		// super rappresenta l'oggetto Piece
		this.abstractt = new GSlib.GSRook();				// abstract rappresenta la categoria del pezzo
	},
	
	GSPieceBishop: function (id) {
		this.superr = new GSlib.GSPiece(this, id);		// super rappresenta l'oggetto Piece
		this.abstractt = new GSlib.GSBishop();				// abstract rappresenta la categoria del pezzo
	},
	
	GSPieceQueen: function (id) {
		this.superr = new GSlib.GSPiece(this, id);		// super rappresenta l'oggetto Piece
		this.abstractt = new GSlib.GSQueen();				// abstract rappresenta la categoria del pezzo
	},
	
	GSPieceKing: function (id) {
		this.superr = new GSlib.GSPiece(this, id);		// super rappresenta l'oggetto Piece
		this.abstractt = new GSlib.GSKing();				// abstract rappresenta la categoria del pezzo
	},
	
	GSPiecePawn: function (id) {
		this.superr = new GSlib.GSPiece(this, id);		// super rappresenta l'oggetto Piece
		this.abstractt = new GSlib.GSPawn();				// abstract rappresenta la categoria del pezzo
	}
	
}