From 46302e643947a66a5593a8eb1140d314effcea95 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 20 Nov 2018 01:26:02 +0100
Subject: [PATCH] Generalize code in VariantRules. Considerable speed-up for
 checkered bot. Prepare ground for Antiking

---
 public/javascripts/base_rules.js         | 239 +++++++++++------------
 public/javascripts/components/game.js    |  11 +-
 public/javascripts/variants/Antiking.js  | 101 ++--------
 public/javascripts/variants/Atomic.js    |  33 ++--
 public/javascripts/variants/Checkered.js | 188 +++++++-----------
 public/javascripts/variants/Zen.js       |  84 ++++----
 6 files changed, 264 insertions(+), 392 deletions(-)

diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 16304953..0868a367 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -106,7 +106,7 @@ class ChessRules
 	static GetBoard(fen)
 	{
 		let rows = fen.split(" ")[0].split("/");
-		let [sizeX,sizeY] = VariantRules.size;
+		const [sizeX,sizeY] = VariantRules.size;
 		let board = doubleArray(sizeX, sizeY, "");
 		for (let i=0; i<rows.length; i++)
 		{
@@ -190,10 +190,10 @@ class ChessRules
 		return undefined; //default
 	}
 
-	// can color1 take color2?
-	canTake(color1, color2)
+	// can thing on square1 take thing on square2
+	canTake([x1,y1], [x2,y2])
 	{
-		return color1 != color2;
+		return this.getColor(x1,y1) != this.getColor(x2,y2);
 	}
 
 	///////////////////
@@ -202,35 +202,33 @@ class ChessRules
 	// All possible moves from selected square (assumption: color is OK)
 	getPotentialMovesFrom([x,y])
 	{
-		let c = this.getColor(x,y);
-		// Fill possible moves according to piece type
 		switch (this.getPiece(x,y))
 		{
 			case VariantRules.PAWN:
-				return this.getPotentialPawnMoves(x,y,c);
+				return this.getPotentialPawnMoves([x,y]);
 			case VariantRules.ROOK:
-				return this.getPotentialRookMoves(x,y,c);
+				return this.getPotentialRookMoves([x,y]);
 			case VariantRules.KNIGHT:
-				return this.getPotentialKnightMoves(x,y,c);
+				return this.getPotentialKnightMoves([x,y]);
 			case VariantRules.BISHOP:
-				return this.getPotentialBishopMoves(x,y,c);
+				return this.getPotentialBishopMoves([x,y]);
 			case VariantRules.QUEEN:
-				return this.getPotentialQueenMoves(x,y,c);
+				return this.getPotentialQueenMoves([x,y]);
 			case VariantRules.KING:
-				return this.getPotentialKingMoves(x,y,c);
+				return this.getPotentialKingMoves([x,y]);
 		}
 	}
 
 	// Build a regular move from its initial and destination squares; tr: transformation
-	getBasicMove(sx, sy, ex, ey, tr)
+	getBasicMove([sx,sy], [ex,ey], tr)
 	{
 		var mv = new Move({
 			appear: [
 				new PiPo({
 					x: ex,
 					y: ey,
-					c: this.getColor(sx,sy),
-					p: !!tr ? tr : this.getPiece(sx,sy)
+					c: !!tr ? tr.c : this.getColor(sx,sy),
+					p: !!tr ? tr.p : this.getPiece(sx,sy)
 				})
 			],
 			vanish: [
@@ -259,35 +257,37 @@ class ChessRules
 	}
 
 	// Generic method to find possible moves of non-pawn pieces ("sliding or jumping")
-	getSlideNJumpMoves(x, y, color, steps, oneStep)
+	getSlideNJumpMoves([x,y], steps, oneStep)
 	{
+		const color = this.getColor(x,y);
 		var moves = [];
-		let [sizeX,sizeY] = VariantRules.size;
+		const [sizeX,sizeY] = VariantRules.size;
 		outerLoop:
 		for (let step of steps)
 		{
-			var i = x + step[0];
-			var j = y + step[1];
+			let i = x + step[0];
+			let j = y + step[1];
 			while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
 			{
-				moves.push(this.getBasicMove(x, y, i, j));
+				moves.push(this.getBasicMove([x,y], [i,j]));
 				if (oneStep !== undefined)
 					continue outerLoop;
 				i += step[0];
 				j += step[1];
 			}
-			if (i>=0 && i<8 && j>=0 && j<8 && this.canTake(color, this.getColor(i,j)))
-				moves.push(this.getBasicMove(x, y, i, j));
+			if (i>=0 && i<8 && j>=0 && j<8 && this.canTake([x,y], [i,j]))
+				moves.push(this.getBasicMove([x,y], [i,j]));
 		}
 		return moves;
 	}
 
 	// What are the pawn moves from square x,y considering color "color" ?
-	getPotentialPawnMoves(x, y, color)
+	getPotentialPawnMoves([x,y])
 	{
+		const color = this.getColor(x,y);
 		var moves = [];
 		var V = VariantRules;
-		let [sizeX,sizeY] = VariantRules.size;
+		const [sizeX,sizeY] = VariantRules.size;
 		let shift = (color == "w" ? -1 : 1);
 		let startRank = (color == "w" ? sizeY-2 : 1);
 		let lastRank = (color == "w" ? 0 : sizeY-1);
@@ -297,24 +297,18 @@ class ChessRules
 			// Normal moves
 			if (this.board[x+shift][y] == V.EMPTY)
 			{
-				moves.push(this.getBasicMove(x, y, x+shift, y));
+				moves.push(this.getBasicMove([x,y], [x+shift,y]));
 				if (x==startRank && this.board[x+2*shift][y] == V.EMPTY)
 				{
 					// Two squares jump
-					moves.push(this.getBasicMove(x, y, x+2*shift, y));
+					moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
 				}
 			}
 			// Captures
-			if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-				&& this.board[x+shift][y-1] != V.EMPTY)
-			{
-				moves.push(this.getBasicMove(x, y, x+shift, y-1));
-			}
-			if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-				&& this.board[x+shift][y+1] != V.EMPTY)
-			{
-				moves.push(this.getBasicMove(x, y, x+shift, y+1));
-			}
+			if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+				moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
+			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+				moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
 		}
 
 		if (x+shift == lastRank)
@@ -324,18 +318,12 @@ class ChessRules
 			promotionPieces.forEach(p => {
 				// Normal move
 				if (this.board[x+shift][y] == V.EMPTY)
-					moves.push(this.getBasicMove(x, y, x+shift, y, p));
+					moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
 				// Captures
-				if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-					&& this.board[x+shift][y-1] != V.EMPTY)
-				{
-					moves.push(this.getBasicMove(x, y, x+shift, y-1, p));
-				}
-				if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-					&& this.board[x+shift][y+1] != V.EMPTY)
-				{
-					moves.push(this.getBasicMove(x, y, x+shift, y+1, p));
-				}
+				if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+					moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
+				if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+					moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
 			});
 		}
 
@@ -345,7 +333,7 @@ class ChessRules
 		if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1)
 		{
 			let epStep = epSquare.y - y;
-			var enpassantMove = this.getBasicMove(x, y, x+shift, y+epStep);
+			var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
 			enpassantMove.vanish.push({
 				x: x,
 				y: y+epStep,
@@ -359,45 +347,40 @@ class ChessRules
 	}
 
 	// What are the rook moves from square x,y ?
-	getPotentialRookMoves(x, y, color)
+	getPotentialRookMoves(sq)
 	{
-		return this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.ROOK]);
+		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.ROOK]);
 	}
 
 	// What are the knight moves from square x,y ?
-	getPotentialKnightMoves(x, y, color)
+	getPotentialKnightMoves(sq)
 	{
-		return this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
 	}
 
 	// What are the bishop moves from square x,y ?
-	getPotentialBishopMoves(x, y, color)
+	getPotentialBishopMoves(sq)
 	{
-		return this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.BISHOP]);
+		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.BISHOP]);
 	}
 
 	// What are the queen moves from square x,y ?
-	getPotentialQueenMoves(x, y, color)
+	getPotentialQueenMoves(sq)
 	{
-		return this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.QUEEN]);
+		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN]);
 	}
 
 	// What are the king moves from square x,y ?
-	getPotentialKingMoves(x, y, c)
+	getPotentialKingMoves(sq)
 	{
 		// Initialize with normal moves
-		var moves = this.getSlideNJumpMoves(x, y, c,
-			VariantRules.steps[VariantRules.QUEEN], "oneStep");
-
-		return moves.concat(this.getCastleMoves(x,y,c));
+		let moves = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+		return moves.concat(this.getCastleMoves(sq));
 	}
 
-	getCastleMoves(x,y,c)
+	getCastleMoves([x,y])
 	{
+		const c = this.getColor(x,y);
 		if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c])
 			return []; //x isn't first rank, or king has moved (shortcut)
 
@@ -467,11 +450,10 @@ class ChessRules
 	///////////////////
 	// MOVES VALIDATION
 
-	canIplay(color, sq)
+	canIplay(side, [x,y])
 	{
-		return ((color=='w' && this.moves.length%2==0)
-				|| (color=='b' && this.moves.length%2==1))
-			&& this.getColor(sq[0], sq[1]) == color;
+		return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1))
+			&& this.getColor(x,y) == side;
 	}
 
 	getPossibleMovesFrom(sq)
@@ -485,15 +467,14 @@ class ChessRules
 	{
 		if (moves.length == 0)
 			return [];
-		let color = this.getColor( moves[0].start.x, moves[0].start.y );
-		return moves.filter(m => {
-			return !this.underCheck(m, color);
-		});
+		let color = this.turn;
+		return moves.filter(m => { return !this.underCheck(m, color); });
 	}
 
 	// Search for all valid moves considering current turn (for engine and game end)
-	getAllValidMoves(color)
+	getAllValidMoves()
 	{
+		const color = this.turn;
 		const oppCol = this.getOppCol(color);
 		var potentialMoves = [];
 		let [sizeX,sizeY] = VariantRules.size;
@@ -512,8 +493,9 @@ class ChessRules
 	}
 	
 	// Stop at the first move found
-	atLeastOneMove(color)
+	atLeastOneMove()
 	{
+		const color = this.turn;
 		const oppCol = this.getOppCol(color);
 		let [sizeX,sizeY] = VariantRules.size;
 		for (var i=0; i<sizeX; i++)
@@ -537,29 +519,32 @@ class ChessRules
 		return false;
 	}
 
-	// Check if pieces of color 'color' are attacking square x,y
-	isAttacked(sq, color)
+	// Check if pieces of color 'colors' are attacking square x,y
+	isAttacked(sq, colors)
 	{
-		return (this.isAttackedByPawn(sq, color)
-			|| this.isAttackedByRook(sq, color)
-			|| this.isAttackedByKnight(sq, color)
-			|| this.isAttackedByBishop(sq, color)
-			|| this.isAttackedByQueen(sq, color)
-			|| this.isAttackedByKing(sq, color));
+		return (this.isAttackedByPawn(sq, colors)
+			|| this.isAttackedByRook(sq, colors)
+			|| this.isAttackedByKnight(sq, colors)
+			|| this.isAttackedByBishop(sq, colors)
+			|| this.isAttackedByQueen(sq, colors)
+			|| this.isAttackedByKing(sq, colors));
 	}
 
 	// Is square x,y attacked by pawns of color c ?
-	isAttackedByPawn([x,y], c)
+	isAttackedByPawn([x,y], colors)
 	{
-		let pawnShift = (c=="w" ? 1 : -1);
-		if (x+pawnShift>=0 && x+pawnShift<8)
+		for (let c of colors)
 		{
-			for (let i of [-1,1])
+			let pawnShift = (c=="w" ? 1 : -1);
+			if (x+pawnShift>=0 && x+pawnShift<8)
 			{
-				if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
-					&& this.getColor(x+pawnShift,y+i)==c)
+				for (let i of [-1,1])
 				{
-					return true;
+					if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
+						&& this.getColor(x+pawnShift,y+i)==c)
+					{
+						return true;
+					}
 				}
 			}
 		}
@@ -567,42 +552,42 @@ class ChessRules
 	}
 
 	// Is square x,y attacked by rooks of color c ?
-	isAttackedByRook(sq, color)
+	isAttackedByRook(sq, colors)
 	{
-		return this.isAttackedBySlideNJump(sq, color,
+		return this.isAttackedBySlideNJump(sq, colors,
 			VariantRules.ROOK, VariantRules.steps[VariantRules.ROOK]);
 	}
 
 	// Is square x,y attacked by knights of color c ?
-	isAttackedByKnight(sq, color)
+	isAttackedByKnight(sq, colors)
 	{
-		return this.isAttackedBySlideNJump(sq, color,
+		return this.isAttackedBySlideNJump(sq, colors,
 			VariantRules.KNIGHT, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
 	}
 
 	// Is square x,y attacked by bishops of color c ?
-	isAttackedByBishop(sq, color)
+	isAttackedByBishop(sq, colors)
 	{
-		return this.isAttackedBySlideNJump(sq, color,
+		return this.isAttackedBySlideNJump(sq, colors,
 			VariantRules.BISHOP, VariantRules.steps[VariantRules.BISHOP]);
 	}
 
 	// Is square x,y attacked by queens of color c ?
-	isAttackedByQueen(sq, color)
+	isAttackedByQueen(sq, colors)
 	{
-		return this.isAttackedBySlideNJump(sq, color,
+		return this.isAttackedBySlideNJump(sq, colors,
 			VariantRules.QUEEN, VariantRules.steps[VariantRules.QUEEN]);
 	}
 
 	// Is square x,y attacked by king of color c ?
-	isAttackedByKing(sq, color)
+	isAttackedByKing(sq, colors)
 	{
-		return this.isAttackedBySlideNJump(sq, color,
+		return this.isAttackedBySlideNJump(sq, colors,
 			VariantRules.KING, VariantRules.steps[VariantRules.QUEEN], "oneStep");
 	}
 
 	// Generic method for non-pawn pieces ("sliding or jumping"): is x,y attacked by piece != color ?
-	isAttackedBySlideNJump([x,y], c,piece,steps,oneStep)
+	isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
 	{
 		for (let step of steps)
 		{
@@ -614,7 +599,7 @@ class ChessRules
 				ry += step[1];
 			}
 			if (rx>=0 && rx<8 && ry>=0 && ry<8 && this.board[rx][ry] != VariantRules.EMPTY
-				&& this.getPiece(rx,ry) == piece && this.getColor(rx,ry) == c)
+				&& this.getPiece(rx,ry) == piece && colors.includes(this.getColor(rx,ry)))
 			{
 				return true;
 			}
@@ -623,20 +608,22 @@ class ChessRules
 	}
 
 	// Is color c under check after move ?
-	underCheck(move, c)
+	underCheck(move)
 	{
+		const color = this.turn;
 		this.play(move);
-		let res = this.isAttacked(this.kingPos[c], this.getOppCol(c));
+		let res = this.isAttacked(this.kingPos[color], this.getOppCol(color));
 		this.undo(move);
 		return res;
 	}
 
 	// On which squares is color c under check (after move) ?
-	getCheckSquares(move, c)
+	getCheckSquares(move)
 	{
 		this.play(move);
-		let res = this.isAttacked(this.kingPos[c], this.getOppCol(c))
-			? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+		const color = this.turn;
+		let res = this.isAttacked(this.kingPos[color], this.getOppCol(color))
+			? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate!
 			: [ ];
 		this.undo(move);
 		return res;
@@ -723,7 +710,7 @@ class ChessRules
 	//////////////
 	// END OF GAME
 
-	checkGameOver(color)
+	checkGameOver()
 	{
 		// Check for 3 repetitions
 		if (this.moves.length >= 8)
@@ -739,19 +726,20 @@ class ChessRules
 			}
 		}
 
-		if (this.atLeastOneMove(color))
+		if (this.atLeastOneMove())
 		{
 			// game not over
 			return "*";
 		}
 
 		// Game over
-		return this.checkGameEnd(color);
+		return this.checkGameEnd();
 	}
 
-	// Useful stand-alone for engine
-	checkGameEnd(color)
+	// No moves are possible: compute score
+	checkGameEnd()
 	{
+		const color = this.turn;
 		// No valid move: stalemate or checkmate?
 		if (!this.isAttacked(this.kingPos[color], this.getOppCol(color)))
 			return "1/2";
@@ -775,12 +763,12 @@ class ChessRules
 	}
 
 	// Assumption: at least one legal move
-	getComputerMove(color)
+	getComputerMove()
 	{
-		const oppCol = this.getOppCol(color);
+		const color = this.turn;
 
 		// Rank moves using a min-max at depth 2
-		let moves1 = this.getAllValidMoves(color);
+		let moves1 = this.getAllValidMoves();
 
 		for (let i=0; i<moves1.length; i++)
 		{
@@ -788,10 +776,10 @@ class ChessRules
 			let eval2 = (color=="w" ? 1 : -1) * 1000; //initialized with very high (checkmate) value
 			this.play(moves1[i]);
 			// Second half-move:
-			let moves2 = this.getAllValidMoves(oppCol);
+			let moves2 = this.getAllValidMoves();
 			// If no possible moves AND underCheck, eval2 is correct.
 			// If !underCheck, eval2 is 0 (stalemate).
-			if (moves2.length == 0 && this.checkGameEnd(oppCol) == "1/2")
+			if (moves2.length == 0 && this.checkGameEnd() == "1/2")
 				eval2 = 0;
 			for (let j=0; j<moves2.length; j++)
 			{
@@ -812,7 +800,7 @@ class ChessRules
 		{
 			this.play(moves1[i]);
 			// 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
-			moves1[i].eval = 0.1*moves1[i].eval + this.alphabeta(oppCol, color, 2, -1000, 1000);
+			moves1[i].eval = 0.1*moves1[i].eval + this.alphabeta(2, -1000, 1000);
 			this.undo(moves1[i]);
 		}
 		moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
@@ -825,11 +813,12 @@ class ChessRules
 		return moves1[_.sample(candidates, 1)];
 	}
 
-	alphabeta(color, oppCol, depth, alpha, beta)
+	alphabeta(depth, alpha, beta)
   {
-		if (!this.atLeastOneMove(color))
+		const color = this.turn;
+		if (!this.atLeastOneMove())
 		{
-			switch (this.checkGameEnd(color))
+			switch (this.checkGameEnd())
 			{
 				case "1/2": return 0;
 				default: return color=="w" ? -1000 : 1000;
@@ -837,14 +826,14 @@ class ChessRules
 		}
 		if (depth == 0)
       return this.evalPosition();
-		const moves = this.getAllValidMoves(color);
+		const moves = this.getAllValidMoves();
     let v = color=="w" ? -1000 : 1000;
 		if (color == "w")
 		{
 			for (let i=0; i<moves.length; i++)
       {
 				this.play(moves[i]);
-				v = Math.max(v, this.alphabeta(oppCol, color, depth-1, alpha, beta));
+				v = Math.max(v, this.alphabeta(depth-1, alpha, beta));
 				this.undo(moves[i]);
 				alpha = Math.max(alpha, v);
 				if (alpha >= beta)
@@ -856,7 +845,7 @@ class ChessRules
 			for (let i=0; i<moves.length; i++)
 			{
 				this.play(moves[i]);
-				v = Math.min(v, this.alphabeta(oppCol, color, depth-1, alpha, beta));
+				v = Math.min(v, this.alphabeta(depth-1, alpha, beta));
 				this.undo(moves[i]);
 				beta = Math.min(beta, v);
 				if (alpha >= beta)
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 147aeafe..8e8e2770 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -618,10 +618,9 @@ Vue.component('my-game', {
 				this.seek = false;
 				if (!!moves && moves.length > 0) //imply continuation
 				{
-					const oppCol = this.vr.turn;
 					const lastMove = moves[moves.length-1];
 					this.vr.undo(lastMove);
-					this.incheck = this.vr.getCheckSquares(lastMove, oppCol);
+					this.incheck = this.vr.getCheckSquares(lastMove);
 					this.vr.play(lastMove, "ingame");
 				}
 				delete localStorage["newgame"];
@@ -635,8 +634,7 @@ Vue.component('my-game', {
 			}
 		},
 		playComputerMove: function() {
-			const compColor = this.mycolor=='w' ? 'b' : 'w';
-			const compMove = this.vr.getComputerMove(compColor);
+			const compMove = this.vr.getComputerMove();
 			// HACK: avoid selecting elements before they appear on page:
 			setTimeout(() => this.play(compMove, "animate"), 500);
 		},
@@ -751,8 +749,7 @@ Vue.component('my-game', {
 				this.animateMove(move);
 				return;
 			}
-			const oppCol = this.vr.getOppCol(this.vr.turn);
-			this.incheck = this.vr.getCheckSquares(move, oppCol); //is opponent in check?
+			this.incheck = this.vr.getCheckSquares(move); //is opponent in check?
 			// Not programmatic, or animation is over
 			if (this.mode == "human" && this.vr.turn == this.mycolor)
 				this.conn.send(JSON.stringify({code:"newmove", move:move, oppid:this.oppid}));
@@ -760,7 +757,7 @@ Vue.component('my-game', {
 			this.vr.play(move, "ingame");
 			if (this.mode == "human")
 				this.updateStorage(); //after our moves and opponent moves
-			const eog = this.vr.checkGameOver(this.vr.turn);
+			const eog = this.vr.checkGameOver();
 			if (eog != "*")
 				this.endGame(eog);
 			else if (this.mode == "computer" && this.vr.turn != this.mycolor)
diff --git a/public/javascripts/variants/Antiking.js b/public/javascripts/variants/Antiking.js
index ac25c736..360deafa 100644
--- a/public/javascripts/variants/Antiking.js
+++ b/public/javascripts/variants/Antiking.js
@@ -7,6 +7,12 @@ class AntikingRules
 	}
 
 	static get ANTIKING() { return 'a'; }
+	
+	initVariables(fen)
+	{
+		super.initVariables(fen);
+		// TODO: initialize this.antikingPos[...]
+	}
 
 	canTake(color1, color2, [x,y])
 	{
@@ -26,23 +32,14 @@ class AntikingRules
 		}
 	}
 
-// TODO: generaliser (à moindre coût) base_rules ? Ou spécialiser variantes ?
-
-	getPotentialAntikingMoves(x, y, c)
+	getPotentialAntikingMoves([x,y])
 	{
 		// TODO
 	}
 
-// TODO: need to re-think some logic, since antikings capture same color
-
-	isAttacked(sq, color)
+	isAttacked(sq, colors)
 	{
-		return (this.isAttackedByPawn(sq, color)
-			|| this.isAttackedByRook(sq, color)
-			|| this.isAttackedByKnight(sq, color)
-			|| this.isAttackedByBishop(sq, color)
-			|| this.isAttackedByQueen(sq, color)
-			|| this.isAttackedByKing(sq, color)); //...
+		return (super.isAttacked(sq, colors) || this.isAttackedByAntiking(sq, colors));
 	}
 
 	isAttackedByAntiking(sq, color)
@@ -50,8 +47,9 @@ class AntikingRules
 		// TODO
 	}
 
-	underCheck(move, c)
+	underCheck(move)
 	{
+		const c = this.turn;
 		this.play(move);
 		let res = this.isAttacked(this.kingPos[c], this.getOppCol(c));
 		// TODO: also check that antiking is still in check
@@ -59,34 +57,18 @@ class AntikingRules
 		return res;
 	}
 
-	getCheckSquares(move, c)
+	getCheckSquares(move)
 	{
 		this.play(move);
+		const c = this.turn;
 		// TODO
 		let res = this.isAttacked(this.kingPos[c], this.getOppCol(c))
-			? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+			? [ JSON.parse(JSON.stringify(this.kingPos[c])) ]
 			: [ ];
 		this.undo(move);
 		return res;
 	}
 
-	// Apply a move on board
-	static PlayOnBoard(board, move)
-	{
-		for (let psq of move.vanish)
-			board[psq.x][psq.y] = VariantRules.EMPTY;
-		for (let psq of move.appear)
-			board[psq.x][psq.y] = psq.c + psq.p;
-	}
-	// Un-apply the played move
-	static UndoOnBoard(board, move)
-	{
-		for (let psq of move.appear)
-			board[psq.x][psq.y] = VariantRules.EMPTY;
-		for (let psq of move.vanish)
-			board[psq.x][psq.y] = psq.c + psq.p;
-	}
-
 	// TODO: need antikingPos as well
 	updateVariables(move)
 	{
@@ -106,7 +88,7 @@ class AntikingRules
 		return color == "w" ? "0-1" : "1-0";
 	}
 
-	// Pieces values
+	// Pieces values (TODO: use Object.assign() + ChessRules.VALUES ?)
 	static get VALUES() {
 		return {
 			'p': 1,
@@ -121,55 +103,8 @@ class AntikingRules
 
 	static GenRandInitFen()
 	{
-		// TODO: no need all code, just add an antiking at rondom on 3rd ranks
-		let pieces = [new Array(8), new Array(8)];
-		// Shuffle pieces on first and last rank
-		for (let c = 0; c <= 1; c++)
-		{
-			let positions = _.range(8);
-
-			// Get random squares for bishops
-			let randIndex = 2 * _.random(3);
-			let bishop1Pos = positions[randIndex];
-			// The second bishop must be on a square of different color
-			let randIndex_tmp = 2 * _.random(3) + 1;
-			let bishop2Pos = positions[randIndex_tmp];
-			// Remove chosen squares
-			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
-			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
-
-			// Get random squares for knights
-			randIndex = _.random(5);
-			let knight1Pos = positions[randIndex];
-			positions.splice(randIndex, 1);
-			randIndex = _.random(4);
-			let knight2Pos = positions[randIndex];
-			positions.splice(randIndex, 1);
-
-			// Get random square for queen
-			randIndex = _.random(3);
-			let queenPos = positions[randIndex];
-			positions.splice(randIndex, 1);
-
-			// Rooks and king positions are now fixed, because of the ordering rook-king-rook
-			let rook1Pos = positions[0];
-			let kingPos = positions[1];
-			let rook2Pos = positions[2];
-
-			// Finally put the shuffled pieces in the board array
-			pieces[c][rook1Pos] = 'r';
-			pieces[c][knight1Pos] = 'n';
-			pieces[c][bishop1Pos] = 'b';
-			pieces[c][queenPos] = 'q';
-			pieces[c][kingPos] = 'k';
-			pieces[c][bishop2Pos] = 'b';
-			pieces[c][knight2Pos] = 'n';
-			pieces[c][rook2Pos] = 'r';
-		}
-		let fen = pieces[0].join("") +
-			"/pppppppp/8/8/8/8/PPPPPPPP/" +
-			pieces[1].join("").toUpperCase() +
-			" 1111"; //add flags
-		return fen;
+		let randFen = ChessRules.GenRandInitFen();
+		// TODO: just add an antiking at random on 3rd ranks
+		return randFen;
 	}
 }
diff --git a/public/javascripts/variants/Atomic.js b/public/javascripts/variants/Atomic.js
index 0fd7647e..d86ed0e1 100644
--- a/public/javascripts/variants/Atomic.js
+++ b/public/javascripts/variants/Atomic.js
@@ -1,8 +1,8 @@
 class AtomicRules extends ChessRules
 {
-	getPotentialMovesFrom([x,y], c, lastMove)
+	getPotentialMovesFrom([x,y])
 	{
-		let moves = super.getPotentialMovesFrom([x,y], c, lastMove);
+		let moves = super.getPotentialMovesFrom([x,y]);
 
 		// Handle explosions
 		moves.forEach(m => {
@@ -28,7 +28,7 @@ class AtomicRules extends ChessRules
 		return moves;
 	}
 
-	getPotentialKingMoves(x, y, c)
+	getPotentialKingMoves([x,y])
 	{
 		// King cannot capture:
 		let moves = [];
@@ -39,20 +39,20 @@ class AtomicRules extends ChessRules
 			var i = x + step[0];
 			var j = y + step[1];
 			if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
-				moves.push(this.getBasicMove(x, y, i, j));
+				moves.push(this.getBasicMove([x,y], [i,j]));
 		}
-		return moves.concat(this.getCastleMoves(x,y,c));
+		return moves.concat(this.getCastleMoves([x,y]));
 	}
 
-	isAttacked(sq, color)
+	isAttacked(sq, colors)
 	{
-		if (this.getPiece(sq[0],sq[1]) == VariantRules.KING && this.isAttackedByKing(sq, color))
+		if (this.getPiece(sq[0],sq[1]) == VariantRules.KING && this.isAttackedByKing(sq, colors))
 			return false; //king cannot take...
-		return (this.isAttackedByPawn(sq, color)
-			|| this.isAttackedByRook(sq, color)
-			|| this.isAttackedByKnight(sq, color)
-			|| this.isAttackedByBishop(sq, color)
-			|| this.isAttackedByQueen(sq, color));
+		return (this.isAttackedByPawn(sq, colors)
+			|| this.isAttackedByRook(sq, colors)
+			|| this.isAttackedByKnight(sq, colors)
+			|| this.isAttackedByBishop(sq, colors)
+			|| this.isAttackedByQueen(sq, colors));
 	}
 
 	updateVariables(move)
@@ -112,8 +112,9 @@ class AtomicRules extends ChessRules
 		}
 	}
 
-	underCheck(move, c)
+	underCheck(move)
 	{
+		const c = this.turn;
 		const oppCol = this.getOppCol(c);
 		this.play(move);
 		let res = undefined;
@@ -130,8 +131,9 @@ class AtomicRules extends ChessRules
 		return res;
 	}
 
-	getCheckSquares(move, c)
+	getCheckSquares(move)
 	{
+		const c = this.getOppCol(this.turn);
 		const saveKingPos = this.kingPos[c]; //king might explode
 		this.play(move);
 		let res = [ ];
@@ -143,8 +145,9 @@ class AtomicRules extends ChessRules
 		return res;
 	}
 
-	checkGameEnd(color)
+	checkGameEnd()
 	{
+		const color = this.turn;
 		const kp = this.kingPos[color];
 		if (kp[0] < 0) //king disappeared
 			return color == "w" ? "0-1" : "1-0";
diff --git a/public/javascripts/variants/Checkered.js b/public/javascripts/variants/Checkered.js
index a74143b9..6ed73a8b 100644
--- a/public/javascripts/variants/Checkered.js
+++ b/public/javascripts/variants/Checkered.js
@@ -50,87 +50,58 @@ class CheckeredRules extends ChessRules
 		return flags;
 	}
 
-	// can color1 take color2?
-	canTake(color1, color2)
+	canTake([x1,y1], [x2,y2])
 	{
+		const color1 = this.getColor(x1,y1);
+		const color2 = this.getColor(x2,y2);
 		// Checkered aren't captured
 		return color1 != color2 && color2 != 'c' && (color1 != 'c' || color2 != this.turn);
 	}
 
-	// Build regular move(s) from its initial and destination squares; tr: transformation
-	getBasicMove(sx, sy, ex, ey, tr)
+	addCaptures([sx,sy], [ex,ey], moves)
 	{
-		if (this.board[ex][ey] == VariantRules.EMPTY)
+		const piece = this.getPiece(sx,sy);
+		if (piece != VariantRules.KING)
 		{
-			// No capture, standard move construction
-			return [super.getBasicMove(sx,sy,ex,ey,tr)];
+			moves.push(this.getBasicMove([sx,sy], [ex,ey], {c:'c',p:piece}));
+			const takePiece = this.getPiece(ex,ey);
+			if (takePiece != piece)
+				moves.push(this.getBasicMove([sx,sy], [ex,ey], {c:'c',p:takePiece}));
 		}
-		let moves = []; //captures: generally 2 choices, unless 'tr' is specified or piece==king
-		const startPiece = this.getPiece(sx,sy);
-		const endPiece = this.getPiece(ex,ey);
-		const startColor = this.getColor(sx,sy);
-		const endColor = this.getColor(ex,ey);
-		for (let piece of !!tr ? [tr] :
-			(startPiece==VariantRules.KING ? VariantRules.KING : _.uniq([startPiece,endPiece])))
-		{
-			var mv = new Move({
-				appear: [
-					new PiPo({
-						x: ex,
-						y: ey,
-						c: startPiece==VariantRules.KING ? startColor : 'c',
-						p: piece
-					})
-				],
-				vanish: [
-					new PiPo({
-						x: sx,
-						y: sy,
-						c: startColor,
-						p: startPiece
-					}),
-					new PiPo({
-						x: ex,
-						y: ey,
-						c: endColor,
-						p: endPiece
-					})
-				]
-			});
-			moves.push(mv);
-		}
-		return moves;
+		else
+			moves.push(this.getBasicMove([sx,sy], [ex,ey]));
 	}
 
 	// Generic method to find possible moves of non-pawn pieces ("sliding or jumping")
-	getSlideNJumpMoves(x, y, color, steps, oneStep)
+	getSlideNJumpMoves([x,y], steps, oneStep)
 	{
-		var moves = [];
-		let [sizeX,sizeY] = VariantRules.size;
+		const color = this.getColor(x,y);
+		let moves = [];
+		const [sizeX,sizeY] = VariantRules.size;
 		outerLoop:
 		for (var loop=0; loop<steps.length; loop++)
 		{
-			var step = steps[loop];
-			var i = x + step[0];
-			var j = y + step[1];
-			while (i>=0 && i<sizeX && j>=0 && j<sizeY
-				&& this.board[i][j] == VariantRules.EMPTY)
+			let step = steps[loop];
+			let i = x + step[0];
+			let j = y + step[1];
+			while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
 			{
-				moves.push(this.getBasicMove(x, y, i, j)[0]); //no capture
+				moves.push(this.getBasicMove([x,y], [i,j])); //no capture
 				if (oneStep !== undefined)
 					continue outerLoop;
 				i += step[0];
 				j += step[1];
 			}
-			if (i>=0 && i<8 && j>=0 && j<8 && this.canTake(color, this.getColor(i,j)))
-				moves = moves.concat(this.getBasicMove(x, y, i, j));
+			if (i>=0 && i<8 && j>=0 && j<8 && this.canTake([x,y], [i,j]))
+				this.addCaptures([x,y], [i,j], moves);
 		}
 		return moves;
 	}
 
 	// What are the pawn moves from square x,y considering color "color" ?
-	getPotentialPawnMoves(x, y, color)
+	getPotentialPawnMoves([x,y])
 	{
+		const color = this.getColor(x,y);
 		var moves = [];
 		var V = VariantRules;
 		let [sizeX,sizeY] = VariantRules.size;
@@ -144,24 +115,18 @@ class CheckeredRules extends ChessRules
 			// Normal moves
 			if (this.board[x+shift][y] == V.EMPTY)
 			{
-				moves.push(this.getBasicMove(x, y, x+shift, y)[0]);
+				moves.push(this.getBasicMove([x,y], [x+shift,y]));
 				if (x==startRank && this.board[x+2*shift][y] == V.EMPTY && this.flags[1][c][y])
 				{
 					// Two squares jump
-					moves.push(this.getBasicMove(x, y, x+2*shift, y)[0]);
+					moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
 				}
 			}
 			// Captures
-			if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-				&& this.board[x+shift][y-1] != V.EMPTY)
-			{
-				moves = moves.concat(this.getBasicMove(x, y, x+shift, y-1));
-			}
-			if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-				&& this.board[x+shift][y+1] != V.EMPTY)
-			{
-				moves = moves.concat(this.getBasicMove(x, y, x+shift, y+1));
-			}
+			if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+				this.addCaptures([x,y], [x+shift,y-1], moves);
+			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+				this.addCaptures([x,y], [x+shift,y+1], moves);
 		}
 
 		if (x+shift == lastRank)
@@ -171,18 +136,12 @@ class CheckeredRules extends ChessRules
 			promotionPieces.forEach(p => {
 				// Normal move
 				if (this.board[x+shift][y] == V.EMPTY)
-					moves.push(this.getBasicMove(x, y, x+shift, y, p)[0]);
+					moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
 				// Captures
-				if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1))
-					&& this.board[x+shift][y-1] != V.EMPTY)
-				{
-					moves = moves.concat(this.getBasicMove(x, y, x+shift, y-1, p));
-				}
-				if (y<sizeY-1 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y+1))
-					&& this.board[x+shift][y+1] != V.EMPTY)
-				{
-					moves = moves.concat(this.getBasicMove(x, y, x+shift, y+1, p));
-				}
+				if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+					moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:'c',p:p}));
+				if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+					moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:'c',p:p}));
 			});
 		}
 
@@ -192,7 +151,7 @@ class CheckeredRules extends ChessRules
 		if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1)
 		{
 			let epStep = epSquare.y - y;
-			var enpassantMove = this.getBasicMove(x, y, x+shift, y+epStep)[0];
+			var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
 			enpassantMove.vanish.push({
 				x: x,
 				y: y+epStep,
@@ -206,8 +165,9 @@ class CheckeredRules extends ChessRules
 		return moves;
 	}
 
-	getCastleMoves(x,y,c)
+	getCastleMoves([x,y])
 	{
+		const c = this.getColor(x,y);
 		if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c])
 			return []; //x isn't first rank, or king has moved (shortcut)
 
@@ -274,11 +234,10 @@ class CheckeredRules extends ChessRules
 		return moves;
 	}
 
-	canIplay(color, sq)
+	canIplay(side, [x,y])
 	{
-		return ((color=='w' && this.moves.length%2==0) || color=='c'
-				|| (color=='b' && this.moves.length%2==1))
-			&& [color,'c'].includes(this.getColor(sq[0], sq[1]));
+		return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1))
+			&& [side,'c'].includes(this.getColor(x,y));
 	}
 
 	// Does m2 un-do m1 ? (to disallow undoing checkered moves)
@@ -286,8 +245,6 @@ class CheckeredRules extends ChessRules
 	{
 		return m1.appear.length == 1 && m2.appear.length == 1
 			&& m1.vanish.length == 1 && m2.vanish.length == 1
-//			&& _.isEqual(m1.appear[0], m2.vanish[0]) //fails in HH case
-//			&& _.isEqual(m1.vanish[0], m2.appear[0]);
 			&& m1.start.x == m2.end.x && m1.end.x == m2.start.x
 			&& m1.start.y == m2.end.y && m1.end.y == m2.start.y
 			&& m1.appear[0].c == m2.vanish[0].c && m1.appear[0].p == m2.vanish[0].p
@@ -298,51 +255,53 @@ class CheckeredRules extends ChessRules
 	{
 		if (moves.length == 0)
 			return [];
-		let color = this.getColor( moves[0].start.x, moves[0].start.y );
+		const color = this.turn;
 		return moves.filter(m => {
 			const L = this.moves.length;
 			if (L > 0 && this.oppositeMoves(this.moves[L-1], m))
 				return false;
-			return !this.underCheck(m, color);
+			return !this.underCheck(m);
 		});
 	}
 
-  isAttackedByPawn([x,y], c)
-  {
-		const color = (c=="c" ? this.turn : c);
-    let pawnShift = (color=="w" ? 1 : -1);
-    if (x+pawnShift>=0 && x+pawnShift<8)
-    {
-      for (let i of [-1,1])
-      {
-        if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
-          && this.getColor(x+pawnShift,y+i)==c)
-        {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-
-	underCheck(move, c)
+	isAttackedByPawn([x,y], colors)
+	{
+		for (let c of colors)
+		{
+			const color = (c=="c" ? this.turn : c);
+			let pawnShift = (color=="w" ? 1 : -1);
+			if (x+pawnShift>=0 && x+pawnShift<8)
+			{
+				for (let i of [-1,1])
+				{
+					if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
+						&& this.getColor(x+pawnShift,y+i)==c)
+					{
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	underCheck(move)
 	{
-		const color = c == 'c' ? this.turn : c;
+		const color = this.turn;
 		this.play(move);
-		let res = this.isAttacked(this.kingPos[color], this.getOppCol(color))
-			|| this.isAttacked(this.kingPos[color], 'c'); //TODO: quite inefficient...
+		let res = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']);
 		this.undo(move);
 		return res;
 	}
 
-	getCheckSquares(move, c)
+	getCheckSquares(move)
 	{
 		this.play(move);
+		const color = this.turn;
 		this.moves.push(move); //artifically change turn, for checkered pawns (TODO)
-		const kingAttacked = this.isAttacked(this.kingPos[c], this.getOppCol(c))
-			|| this.isAttacked(this.kingPos[c], 'c');
+		const kingAttacked = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']);
 		let res = kingAttacked
-			? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+			? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate!
 			: [ ];
 		this.moves.pop();
 		this.undo(move);
@@ -387,8 +346,9 @@ class CheckeredRules extends ChessRules
 			this.flags[1][move.start.x==6 ? "w" : "b"][move.start.y] = false;
 	}
 
-	checkGameEnd(color)
+	checkGameEnd()
 	{
+		const color = this.turn;
 		if (!this.isAttacked(this.kingPos[color], this.getOppCol(color))
 			&& !this.isAttacked(this.kingPos[color], 'c'))
 		{
diff --git a/public/javascripts/variants/Zen.js b/public/javascripts/variants/Zen.js
index bfc0e0ab..b7f229c3 100644
--- a/public/javascripts/variants/Zen.js
+++ b/public/javascripts/variants/Zen.js
@@ -7,8 +7,9 @@ class ZenRules extends ChessRules
 	}
 
 	// TODO: some duplicated code in 2 next functions
-	getSlideNJumpMoves(x, y, color, steps, oneStep)
+	getSlideNJumpMoves([x,y], steps, oneStep)
 	{
+		const color = this.getColor(x,y);
 		var moves = [];
 		let [sizeX,sizeY] = VariantRules.size;
 		outerLoop:
@@ -20,8 +21,8 @@ class ZenRules extends ChessRules
 			while (i>=0 && i<sizeX && j>=0 && j<sizeY
 				&& this.board[i][j] == VariantRules.EMPTY)
 			{
-				moves.push(this.getBasicMove(x, y, i, j));
-				if (oneStep !== undefined)
+				moves.push(this.getBasicMove([x,y], [i,j]));
+				if (!!oneStep)
 					continue outerLoop;
 				i += step[0];
 				j += step[1];
@@ -33,8 +34,9 @@ class ZenRules extends ChessRules
 
 	// follow steps from x,y until something is met.
 	// if met piece is opponent and same movement (asA): eat it!
-	findCaptures_aux(x, y, color, asA)
+	findCaptures_aux([x,y], asA)
 	{
+		const color = this.getColor(x,y);
 		var moves = [];
 		var V = VariantRules;
 		var steps = asA != V.PAWN
@@ -66,13 +68,13 @@ class ZenRules extends ChessRules
 				{
 					// Special case of promotion:
 					promotionPieces.forEach(p => {
-						moves.push(this.getBasicMove(x, y, i, j, p));
+						moves.push(this.getBasicMove([x,y], [i,j], {c:color,p:p}));
 					});
 				}
 				else
 				{
 					// All other cases
-					moves.push(this.getBasicMove(x, y, i, j));
+					moves.push(this.getBasicMove([x,y], [i,j]));
 				}
 			}
 		}
@@ -80,35 +82,31 @@ class ZenRules extends ChessRules
 	}
 
 	// Find possible captures from a square: look in every direction!
-	findCaptures(x, y, color)
+	findCaptures(sq)
 	{
 		var moves = [];
 
 		// PAWN
-		Array.prototype.push.apply(moves,
-			this.findCaptures_aux(x, y, color, VariantRules.PAWN));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.PAWN));
 
 		// ROOK
-		Array.prototype.push.apply(moves,
-			this.findCaptures_aux(x, y, color, VariantRules.ROOK));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.ROOK));
 
 		// KNIGHT
-		Array.prototype.push.apply(moves,
-			this.findCaptures_aux(x, y, color, VariantRules.KNIGHT));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.KNIGHT));
 
 		// BISHOP
-		Array.prototype.push.apply(moves,
-			this.findCaptures_aux(x, y, color, VariantRules.BISHOP));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.BISHOP));
 
 		// QUEEN
-		Array.prototype.push.apply(moves,
-			this.findCaptures_aux(x, y, color, VariantRules.QUEEN));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.QUEEN));
 
 		return moves;
 	}
 
-	getPotentialPawnMoves(x, y, color)
+	getPotentialPawnMoves([x,y])
 	{
+		const color = this.getColor(x,y);
 		var moves = [];
 		var V = VariantRules;
 		let [sizeX,sizeY] = VariantRules.size;
@@ -122,11 +120,11 @@ class ZenRules extends ChessRules
 			// Normal moves
 			if (this.board[x+shift][y] == V.EMPTY)
 			{
-				moves.push(this.getBasicMove(x, y, x+shift, y));
+				moves.push(this.getBasicMove([x,y], [x+shift,y]));
 				if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
 				{
 					//two squares jump
-					moves.push(this.getBasicMove(x, y, x+2*shift, y));
+					moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
 				}
 			}
 		}
@@ -138,62 +136,52 @@ class ZenRules extends ChessRules
 			promotionPieces.forEach(p => {
 				// Normal move
 				if (this.board[x+shift][y] == V.EMPTY)
-					moves.push(this.getBasicMove(x, y, x+shift, y, p));
+					moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
 			});
 		}
 
 		// No en passant here
 
 		// Add "zen" captures
-		Array.prototype.push.apply(moves, this.findCaptures(x, y, color));
+		Array.prototype.push.apply(moves, this.findCaptures([x,y]));
 
 		return moves;
 	}
 
-	getPotentialRookMoves(x, y, color)
+	getPotentialRookMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.ROOK]);
-		let captures = this.findCaptures(x, y, color);
+		let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.ROOK]);
+		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
-	getPotentialKnightMoves(x, y, color)
+	getPotentialKnightMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
-		let captures = this.findCaptures(x, y, color);
+		let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
-	getPotentialBishopMoves(x, y, color)
+	getPotentialBishopMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.BISHOP]);
-		let captures = this.findCaptures(x, y, color);
+		let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.BISHOP]);
+		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
-	getPotentialQueenMoves(x, y, color)
+	getPotentialQueenMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(
-			x, y, color, VariantRules.steps[VariantRules.QUEEN]);
-		let captures = this.findCaptures(x, y, color);
+		let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN]);
+		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
-	getPotentialKingMoves(x, y, c)
+	getPotentialKingMoves(sq)
 	{
 		// Initialize with normal moves
-		let noCaptures = this.getSlideNJumpMoves(
-			x, y, c, VariantRules.steps[VariantRules.QUEEN], "oneStep");
-		let captures = this.findCaptures(x, y, c);
-
-		let moves = noCaptures
-			.concat(captures)
-			.concat(this.getCastleMoves(x, y, c));
-
-		return moves;
+		let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+		let captures = this.findCaptures(sq);
+		return noCaptures.concat(captures).concat(this.getCastleMoves(sq));
 	}
 
 	getNotation(move)
-- 
2.44.0