From: Benjamin Auder <benjamin.auder@somewhere>
Date: Fri, 14 Dec 2018 18:30:57 +0000 (+0100)
Subject: Code simplification + a few fixes
X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/images/img/doc/html/index.html?a=commitdiff_plain;h=0b7d99ecbb5dedc02cd96c457b5fc2962db9b297;p=vchess.git

Code simplification + a few fixes
---

diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 63b535da..51c9dda8 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -55,7 +55,7 @@ class ChessRules
 	{
 		this.moves = moves;
 		// Use fen string to initialize variables, flags and board
-		this.board = VariantRules.GetBoard(fen);
+		this.board = V.GetBoard(fen);
 		this.setFlags(fen);
 		this.initVariables(fen);
 	}
@@ -102,7 +102,7 @@ class ChessRules
 				k++;
 			}
 		}
-		const epSq = this.moves.length > 0 ? this.getEpSquare(this.lastMove) : undefined;
+		const epSq = (this.moves.length > 0 ? this.getEpSquare(this.lastMove) : undefined);
 		this.epSquares = [ epSq ];
 	}
 
@@ -110,8 +110,7 @@ class ChessRules
 	static GetBoard(fen)
 	{
 		let rows = fen.split(" ")[0].split("/");
-		const [sizeX,sizeY] = VariantRules.size;
-		let board = doubleArray(sizeX, sizeY, "");
+		let board = doubleArray(V.size.x, V.size.y, "");
 		for (let i=0; i<rows.length; i++)
 		{
 			let j = 0;
@@ -122,7 +121,7 @@ class ChessRules
 				if (!isNaN(num))
 					j += num; //just shift j
 				else //something at position i,j
-					board[i][j++] = VariantRules.fen2board(character);
+					board[i][j++] = V.fen2board(character);
 			}
 		}
 		return board;
@@ -141,20 +140,22 @@ class ChessRules
 	///////////////////
 	// GETTERS, SETTERS
 
-	static get size() { return [8,8]; }
+	static get size() { return {x:8, y:8}; }
+
 	// Two next functions return 'undefined' if called on empty square
 	getColor(i,j) { return this.board[i][j].charAt(0); }
 	getPiece(i,j) { return this.board[i][j].charAt(1); }
 
 	// Color
-	getOppCol(color) { return color=="w" ? "b" : "w"; }
+	getOppCol(color) { return (color=="w" ? "b" : "w"); }
 
 	get lastMove() {
 		const L = this.moves.length;
-		return L>0 ? this.moves[L-1] : null;
+		return (L>0 ? this.moves[L-1] : null);
 	}
+
 	get turn() {
-		return this.moves.length%2==0 ? 'w' : 'b';
+		return (this.moves.length%2==0 ? 'w' : 'b');
 	}
 
 	// Pieces codes
@@ -192,7 +193,7 @@ class ChessRules
 	getEpSquare(move)
 	{
 		const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
-		if (this.getPiece(sx,sy) == VariantRules.PAWN && Math.abs(sx - ex) == 2)
+		if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) == 2)
 		{
 			return {
 				x: (sx + ex)/2,
@@ -205,7 +206,7 @@ class ChessRules
 	// Can thing on square1 take thing on square2
 	canTake([x1,y1], [x2,y2])
 	{
-		return this.getColor(x1,y1) != this.getColor(x2,y2);
+		return this.getColor(x1,y1) !== this.getColor(x2,y2);
 	}
 
 	///////////////////
@@ -216,17 +217,17 @@ class ChessRules
 	{
 		switch (this.getPiece(x,y))
 		{
-			case VariantRules.PAWN:
+			case V.PAWN:
 				return this.getPotentialPawnMoves([x,y]);
-			case VariantRules.ROOK:
+			case V.ROOK:
 				return this.getPotentialRookMoves([x,y]);
-			case VariantRules.KNIGHT:
+			case V.KNIGHT:
 				return this.getPotentialKnightMoves([x,y]);
-			case VariantRules.BISHOP:
+			case V.BISHOP:
 				return this.getPotentialBishopMoves([x,y]);
-			case VariantRules.QUEEN:
+			case V.QUEEN:
 				return this.getPotentialQueenMoves([x,y]);
-			case VariantRules.KING:
+			case V.KING:
 				return this.getPotentialKingMoves([x,y]);
 		}
 	}
@@ -254,7 +255,7 @@ class ChessRules
 		});
 
 		// The opponent piece disappears if we take it
-		if (this.board[ex][ey] != VariantRules.EMPTY)
+		if (this.board[ex][ey] != V.EMPTY)
 		{
 			mv.vanish.push(
 				new PiPo({
@@ -268,19 +269,23 @@ class ChessRules
 		return mv;
 	}
 
+	// Is (x,y) on the chessboard?
+	static OnBoard(x,y)
+	{
+		return (x>=0 && x<V.size.x && y>=0 && y<V.size.y);
+	}
+
 	// Generic method to find possible moves of non-pawn pieces ("sliding or jumping")
 	getSlideNJumpMoves([x,y], steps, oneStep)
 	{
 		const color = this.getColor(x,y);
 		let moves = [];
-		const [sizeX,sizeY] = VariantRules.size;
 		outerLoop:
 		for (let step of steps)
 		{
 			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)
+			while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
 			{
 				moves.push(this.getBasicMove([x,y], [i,j]));
 				if (oneStep !== undefined)
@@ -288,7 +293,7 @@ class ChessRules
 				i += step[0];
 				j += step[1];
 			}
-			if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.canTake([x,y], [i,j]))
+			if (V.OnBoard(i,j) && this.canTake([x,y], [i,j]))
 				moves.push(this.getBasicMove([x,y], [i,j]));
 		}
 		return moves;
@@ -299,8 +304,7 @@ class ChessRules
 	{
 		const color = this.turn;
 		let moves = [];
-		const V = VariantRules;
-		const [sizeX,sizeY] = V.size;
+		const [sizeX,sizeY] = [V.size.x,V.size.y];
 		const shift = (color == "w" ? -1 : 1);
 		const firstRank = (color == 'w' ? sizeX-1 : 0);
 		const startRank = (color == "w" ? sizeX-2 : 1);
@@ -320,13 +324,13 @@ class ChessRules
 				}
 			}
 			// Captures
-			if (y>0 && this.canTake([x,y], [x+shift,y-1])
-				&& this.board[x+shift][y-1] != V.EMPTY)
+			if (y>0 && this.board[x+shift][y-1] != V.EMPTY
+				&& this.canTake([x,y], [x+shift,y-1]))
 			{
 				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)
+			if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
+				&& this.canTake([x,y], [x+shift,y+1]))
 			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
 			}
@@ -342,13 +346,13 @@ class ChessRules
 				if (this.board[x+shift][y] == V.EMPTY)
 					moves.push(this.getBasicMove([x,y], [x+shift,y], {c:pawnColor,p:p}));
 				// Captures
-				if (y>0 && this.canTake([x,y], [x+shift,y-1])
-					&& this.board[x+shift][y-1] != V.EMPTY)
+				if (y>0 && this.board[x+shift][y-1] != V.EMPTY
+					&& this.canTake([x,y], [x+shift,y-1]))
 				{
 					moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:pawnColor,p:p}));
 				}
-				if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
-					&& this.board[x+shift][y+1] != V.EMPTY)
+				if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
+					&& this.canTake([x,y], [x+shift,y+1]))
 				{
 					moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:pawnColor,p:p}));
 				}
@@ -357,11 +361,11 @@ class ChessRules
 
 		// En passant
 		const Lep = this.epSquares.length;
-		const epSquare = Lep>0 ? this.epSquares[Lep-1] : undefined;
+		const epSquare = (Lep>0 ? this.epSquares[Lep-1] : undefined);
 		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]);
+			const epStep = epSquare.y - y;
+			let enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
 			enpassantMove.vanish.push({
 				x: x,
 				y: y+epStep,
@@ -377,33 +381,30 @@ class ChessRules
 	// What are the rook moves from square x,y ?
 	getPotentialRookMoves(sq)
 	{
-		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.ROOK]);
+		return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
 	}
 
 	// What are the knight moves from square x,y ?
 	getPotentialKnightMoves(sq)
 	{
-		return this.getSlideNJumpMoves(
-			sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+		return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
 	}
 
 	// What are the bishop moves from square x,y ?
 	getPotentialBishopMoves(sq)
 	{
-		return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.BISHOP]);
+		return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
 	}
 
 	// What are the queen moves from square x,y ?
 	getPotentialQueenMoves(sq)
 	{
-		const V = VariantRules;
 		return this.getSlideNJumpMoves(sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
 	}
 
 	// What are the king moves from square x,y ?
 	getPotentialKingMoves(sq)
 	{
-		const V = VariantRules;
 		// Initialize with normal moves
 		let moves = this.getSlideNJumpMoves(sq,
 			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
@@ -413,17 +414,14 @@ class ChessRules
 	getCastleMoves([x,y])
 	{
 		const c = this.getColor(x,y);
-		const [sizeX,sizeY] = VariantRules.size;
-		if (x != (c=="w" ? sizeX-1 : 0) || y != this.INIT_COL_KING[c])
+		if (x != (c=="w" ? V.size.x-1 : 0) || y != this.INIT_COL_KING[c])
 			return []; //x isn't first rank, or king has moved (shortcut)
 
-		const V = VariantRules;
-
 		// Castling ?
 		const oppCol = this.getOppCol(c);
 		let moves = [];
 		let i = 0;
-		const finalSquares = [ [2,3], [sizeY-2,sizeY-3] ]; //king, then rook
+		const finalSquares = [ [2,3], [V.size.y-2,V.size.y-3] ]; //king, then rook
 		castlingCheck:
 		for (let castleSide=0; castleSide < 2; castleSide++) //large, then small
 		{
@@ -510,13 +508,12 @@ class ChessRules
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
 		let potentialMoves = [];
-		const [sizeX,sizeY] = VariantRules.size;
-		for (let i=0; i<sizeX; i++)
+		for (let i=0; i<V.size.x; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
 				// Next condition "!= oppCol" = harmless hack to work with checkered variant
-				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
+				if (this.board[i][j] != V.EMPTY && this.getColor(i,j) != oppCol)
 					Array.prototype.push.apply(potentialMoves, this.getPotentialMovesFrom([i,j]));
 			}
 		}
@@ -530,12 +527,11 @@ class ChessRules
 	{
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
-		const [sizeX,sizeY] = VariantRules.size;
-		for (let i=0; i<sizeX; i++)
+		for (let i=0; i<V.size.x; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
+				if (this.board[i][j] != V.EMPTY && this.getColor(i,j) != oppCol)
 				{
 					const moves = this.getPotentialMovesFrom([i,j]);
 					if (moves.length > 0)
@@ -566,15 +562,14 @@ class ChessRules
 	// Is square x,y attacked by 'colors' pawns ?
 	isAttackedByPawn([x,y], colors)
 	{
-		const [sizeX,sizeY] = VariantRules.size;
 		for (let c of colors)
 		{
 			let pawnShift = (c=="w" ? 1 : -1);
-			if (x+pawnShift>=0 && x+pawnShift<sizeX)
+			if (x+pawnShift>=0 && x+pawnShift<V.size.x)
 			{
 				for (let i of [-1,1])
 				{
-					if (y+i>=0 && y+i<sizeY && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
+					if (y+i>=0 && y+i<V.size.y && this.getPiece(x+pawnShift,y+i)==V.PAWN
 						&& this.getColor(x+pawnShift,y+i)==c)
 					{
 						return true;
@@ -588,28 +583,25 @@ class ChessRules
 	// Is square x,y attacked by 'colors' rooks ?
 	isAttackedByRook(sq, colors)
 	{
-		return this.isAttackedBySlideNJump(sq, colors,
-			VariantRules.ROOK, VariantRules.steps[VariantRules.ROOK]);
+		return this.isAttackedBySlideNJump(sq, colors, V.ROOK, V.steps[V.ROOK]);
 	}
 
 	// Is square x,y attacked by 'colors' knights ?
 	isAttackedByKnight(sq, colors)
 	{
 		return this.isAttackedBySlideNJump(sq, colors,
-			VariantRules.KNIGHT, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+			V.KNIGHT, V.steps[V.KNIGHT], "oneStep");
 	}
 
 	// Is square x,y attacked by 'colors' bishops ?
 	isAttackedByBishop(sq, colors)
 	{
-		return this.isAttackedBySlideNJump(sq, colors,
-			VariantRules.BISHOP, VariantRules.steps[VariantRules.BISHOP]);
+		return this.isAttackedBySlideNJump(sq, colors, V.BISHOP, V.steps[V.BISHOP]);
 	}
 
 	// Is square x,y attacked by 'colors' queens ?
 	isAttackedByQueen(sq, colors)
 	{
-		const V = VariantRules;
 		return this.isAttackedBySlideNJump(sq, colors, V.QUEEN,
 			V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
 	}
@@ -617,7 +609,6 @@ class ChessRules
 	// Is square x,y attacked by 'colors' king(s) ?
 	isAttackedByKing(sq, colors)
 	{
-		const V = VariantRules;
 		return this.isAttackedBySlideNJump(sq, colors, V.KING,
 			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
 	}
@@ -626,19 +617,16 @@ class ChessRules
 	// is x,y attacked by a piece of color in array 'colors' ?
 	isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
 	{
-		const [sizeX,sizeY] = VariantRules.size;
 		for (let step of steps)
 		{
 			let rx = x+step[0], ry = y+step[1];
-			while (rx>=0 && rx<sizeX && ry>=0 && ry<sizeY
-				&& this.board[rx][ry] == VariantRules.EMPTY && !oneStep)
+			while (V.OnBoard(rx,ry) && this.board[rx][ry] == V.EMPTY && !oneStep)
 			{
 				rx += step[0];
 				ry += step[1];
 			}
-			if (rx>=0 && rx<sizeX && ry>=0 && ry<sizeY
-				&& this.board[rx][ry] != VariantRules.EMPTY
-				&& this.getPiece(rx,ry) == piece && colors.includes(this.getColor(rx,ry)))
+			if (V.OnBoard(rx,ry) && this.getPiece(rx,ry) === piece
+				&& colors.includes(this.getColor(rx,ry)))
 			{
 				return true;
 			}
@@ -672,7 +660,7 @@ class ChessRules
 	static PlayOnBoard(board, move)
 	{
 		for (let psq of move.vanish)
-			board[psq.x][psq.y] = VariantRules.EMPTY;
+			board[psq.x][psq.y] = V.EMPTY;
 		for (let psq of move.appear)
 			board[psq.x][psq.y] = psq.c + psq.p;
 	}
@@ -680,7 +668,7 @@ class ChessRules
 	static UndoOnBoard(board, move)
 	{
 		for (let psq of move.appear)
-			board[psq.x][psq.y] = VariantRules.EMPTY;
+			board[psq.x][psq.y] = V.EMPTY;
 		for (let psq of move.vanish)
 			board[psq.x][psq.y] = psq.c + psq.p;
 	}
@@ -689,12 +677,11 @@ class ChessRules
 	updateVariables(move)
 	{
 		const piece = this.getPiece(move.start.x,move.start.y);
-		const c = this.getColor(move.start.x,move.start.y);
-		const [sizeX,sizeY] = VariantRules.size;
-		const firstRank = (c == "w" ? sizeX-1 : 0);
+		const c = this.turn;
+		const firstRank = (c == "w" ? V.size.x-1 : 0);
 
 		// Update king position + flags
-		if (piece == VariantRules.KING && move.appear.length > 0)
+		if (piece == V.KING && move.appear.length > 0)
 		{
 			this.kingPos[c][0] = move.appear[0].x;
 			this.kingPos[c][1] = move.appear[0].y;
@@ -702,7 +689,7 @@ class ChessRules
 			return;
 		}
 		const oppCol = this.getOppCol(c);
-		const oppFirstRank = (sizeX-1) - firstRank;
+		const oppFirstRank = (V.size.x-1) - firstRank;
 		if (move.start.x == firstRank //our rook moves?
 			&& this.INIT_COL_ROOK[c].includes(move.start.y))
 		{
@@ -723,7 +710,7 @@ class ChessRules
 	{
 		// (Potentially) Reset king position
 		const c = this.getColor(move.start.x,move.start.y);
-		if (this.getPiece(move.start.x,move.start.y) == VariantRules.KING)
+		if (this.getPiece(move.start.x,move.start.y) == V.KING)
 			this.kingPos[c] = [move.start.x, move.start.y];
 	}
 
@@ -746,7 +733,7 @@ class ChessRules
 		this.updateVariables(move);
 		this.moves.push(move);
 		this.epSquares.push( this.getEpSquare(move) );
-		VariantRules.PlayOnBoard(this.board, move);
+		V.PlayOnBoard(this.board, move);
 
 		if (!!ingame)
 			move.hash = this.getHashState();
@@ -754,7 +741,7 @@ class ChessRules
 
 	undo(move)
 	{
-		VariantRules.UndoOnBoard(this.board, move);
+		V.UndoOnBoard(this.board, move);
 		this.epSquares.pop();
 		this.moves.pop();
 		this.unupdateVariables(move);
@@ -834,7 +821,7 @@ class ChessRules
 
 	static get THRESHOLD_MATE() {
 		// At this value or above, the game is over
-		return VariantRules.INFINITY;
+		return V.INFINITY;
 	}
 
 	static get SEARCH_DEPTH() {
@@ -845,7 +832,7 @@ class ChessRules
 	// NOTE: works also for extinction chess because depth is 3...
 	getComputerMove()
 	{
-		const maxeval = VariantRules.INFINITY;
+		const maxeval = V.INFINITY;
 		const color = this.turn;
 		// Some variants may show a bigger moves list to the human (Switching),
 		// thus the argument "computer" below (which is generally ignored)
@@ -855,7 +842,14 @@ class ChessRules
 		for (let i of _.shuffle(_.range(moves1.length)))
 		{
 			this.play(moves1[i]);
-			const finish = (Math.abs(this.evalPosition()) >= VariantRules.THRESHOLD_MATE);
+			let finish = (Math.abs(this.evalPosition()) >= V.THRESHOLD_MATE);
+			if (!finish && !this.atLeastOneMove())
+			{
+				// Try mate (for other variants)
+				const score = this.checkGameEnd();
+				if (score != "1/2")
+					finish = true;
+			}
 			this.undo(moves1[i]);
 			if (finish)
 				return moves1[i];
@@ -913,8 +907,7 @@ class ChessRules
 		const timeStart = Date.now();
 
 		// Skip depth 3+ if we found a checkmate (or if we are checkmated in 1...)
-		if (VariantRules.SEARCH_DEPTH >= 3
-			&& Math.abs(moves1[0].eval) < VariantRules.THRESHOLD_MATE)
+		if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE)
 		{
 			for (let i=0; i<moves1.length; i++)
 			{
@@ -923,7 +916,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(VariantRules.SEARCH_DEPTH-1, -maxeval, maxeval);
+					this.alphabeta(V.SEARCH_DEPTH-1, -maxeval, maxeval);
 				this.undo(moves1[i]);
 			}
 			moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
@@ -940,7 +933,7 @@ class ChessRules
 
 	alphabeta(depth, alpha, beta)
   {
-		const maxeval = VariantRules.INFINITY;
+		const maxeval = V.INFINITY;
 		const color = this.turn;
 		if (!this.atLeastOneMove())
 		{
@@ -986,17 +979,16 @@ class ChessRules
 
 	evalPosition()
 	{
-		const [sizeX,sizeY] = VariantRules.size;
 		let evaluation = 0;
 		// Just count material for now
-		for (let i=0; i<sizeX; i++)
+		for (let i=0; i<V.size.x; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] != VariantRules.EMPTY)
+				if (this.board[i][j] != V.EMPTY)
 				{
 					const sign = this.getColor(i,j) == "w" ? 1 : -1;
-					evaluation += sign * VariantRules.VALUES[this.getPiece(i,j)];
+					evaluation += sign * V.VALUES[this.getPiece(i,j)];
 				}
 			}
 		}
@@ -1069,13 +1061,12 @@ class ChessRules
 	getBaseFen()
 	{
 		let fen = "";
-		let [sizeX,sizeY] = VariantRules.size;
-		for (let i=0; i<sizeX; i++)
+		for (let i=0; i<V.size.x; i++)
 		{
 			let emptyCount = 0;
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] == VariantRules.EMPTY)
+				if (this.board[i][j] == V.EMPTY)
 					emptyCount++;
 				else
 				{
@@ -1085,7 +1076,7 @@ class ChessRules
 						fen += emptyCount;
 						emptyCount = 0;
 					}
-					fen += VariantRules.board2fen(this.board[i][j]);
+					fen += V.board2fen(this.board[i][j]);
 				}
 			}
 			if (emptyCount > 0)
@@ -1093,7 +1084,7 @@ class ChessRules
 				// "Flush remainder"
 				fen += emptyCount;
 			}
-			if (i < sizeX - 1)
+			if (i < V.size.x - 1)
 				fen += "/"; //separate rows
 		}
 		return fen;
@@ -1115,15 +1106,14 @@ class ChessRules
 	// Context: just before move is played, turn hasn't changed
 	getNotation(move)
 	{
-		if (move.appear.length == 2 && move.appear[0].p == VariantRules.KING) //castle
+		if (move.appear.length == 2 && move.appear[0].p == V.KING) //castle
 			return (move.end.y < move.start.y ? "0-0-0" : "0-0");
 
 		// Translate final square
-		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+		const finalSquare = String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 
 		const piece = this.getPiece(move.start.x, move.start.y);
-		if (piece == VariantRules.PAWN)
+		if (piece == V.PAWN)
 		{
 			// Pawn move
 			let notation = "";
@@ -1152,9 +1142,8 @@ class ChessRules
 	getLongNotation(move)
 	{
 		const startSquare =
-			String.fromCharCode(97 + move.start.y) + (VariantRules.size[0]-move.start.x);
-		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+			String.fromCharCode(97 + move.start.y) + (V.size.x-move.start.x);
+		const finalSquare = String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 		return startSquare + finalSquare; //not encoding move. But short+long is enough
 	}
 
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index deccd5da..63bf675c 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -24,7 +24,7 @@ Vue.component('my-game', {
 		};
 	},
 	render(h) {
-		const [sizeX,sizeY] = VariantRules.size;
+		const [sizeX,sizeY] = [V.size.x,V.size.y];
 		const smallScreen = (window.innerWidth <= 420);
 		// Precompute hints squares to facilitate rendering
 		let hintSquares = doubleArray(sizeX, sizeY, false);
diff --git a/public/javascripts/components/rules.js b/public/javascripts/components/rules.js
index 25f8a3bd..c3107a67 100644
--- a/public/javascripts/components/rules.js
+++ b/public/javascripts/components/rules.js
@@ -21,7 +21,7 @@ Vue.component('my-rules', {
 	},
 	methods: {
 		drawDiag: function(fen) {
-			let [sizeX,sizeY] = VariantRules.size;
+			let [sizeX,sizeY] = [V.size.x,V.size.y];
 			let fenParts = fen.split(" ");
 			// Obtain array of pieces images names
 			let board = VariantRules.GetBoard(fenParts[0]);
diff --git a/public/javascripts/variants/Alice.js b/public/javascripts/variants/Alice.js
index 220fbd40..5e10a059 100644
--- a/public/javascripts/variants/Alice.js
+++ b/public/javascripts/variants/Alice.js
@@ -65,7 +65,6 @@ class AliceRules extends ChessRules
 	getSquareOccupation(i, j, mirrorSide)
 	{
 		const piece = this.getPiece(i,j);
-		const V = VariantRules;
 		if (mirrorSide==1 && Object.keys(V.ALICE_CODES).includes(piece))
 			return this.board[i][j];
 		else if (mirrorSide==2 && Object.keys(V.ALICE_PIECES).includes(piece))
@@ -77,11 +76,10 @@ class AliceRules extends ChessRules
 	getSideBoard(mirrorSide)
 	{
 		// Build corresponding board from complete board
-		const [sizeX,sizeY] = VariantRules.size;
-		let sideBoard = doubleArray(sizeX, sizeY, "");
-		for (let i=0; i<sizeX; i++)
+		let sideBoard = doubleArray(V.size.x, V.size.y, "");
+		for (let i=0; i<V.size.x; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 				sideBoard[i][j] = this.getSquareOccupation(i, j, mirrorSide);
 		}
 		return sideBoard;
@@ -90,8 +88,8 @@ class AliceRules extends ChessRules
 	// NOTE: castle & enPassant https://www.chessvariants.com/other.dir/alice.html
 	getPotentialMovesFrom([x,y], sideBoard)
 	{
-		const pieces = Object.keys(VariantRules.ALICE_CODES);
-		const codes = Object.keys(VariantRules.ALICE_PIECES);
+		const pieces = Object.keys(V.ALICE_CODES);
+		const codes = Object.keys(V.ALICE_PIECES);
 		const mirrorSide = (pieces.includes(this.getPiece(x,y)) ? 1 : 2);
 
 		// Search valid moves on sideBoard
@@ -107,11 +105,11 @@ class AliceRules extends ChessRules
 				// appear[i] must be an empty square on the other board
 				for (let psq of m.appear)
 				{
-					if (this.getSquareOccupation(psq.x,psq.y,3-mirrorSide) != VariantRules.EMPTY)
+					if (this.getSquareOccupation(psq.x,psq.y,3-mirrorSide) != V.EMPTY)
 						return false;
 				}
 			}
-			else if (this.board[m.end.x][m.end.y] != VariantRules.EMPTY)
+			else if (this.board[m.end.x][m.end.y] != V.EMPTY)
 			{
 				// Attempt to capture
 				const piece = this.getPiece(m.end.x,m.end.y);
@@ -125,18 +123,18 @@ class AliceRules extends ChessRules
 			if (mirrorSide==1)
 			{
 				m.appear.forEach(psq => { //forEach: castling taken into account
-					psq.p = VariantRules.ALICE_CODES[psq.p]; //goto board2
+					psq.p = V.ALICE_CODES[psq.p]; //goto board2
 				});
 			}
 			else //move on board2: mark vanishing pieces as Alice
 			{
 				m.vanish.forEach(psq => {
-					psq.p = VariantRules.ALICE_CODES[psq.p];
+					psq.p = V.ALICE_CODES[psq.p];
 				});
 			}
 			// Fix en-passant captures
-			if (m.vanish[0].p == VariantRules.PAWN
-				&& m.vanish.length == 2 && this.board[m.end.x][m.end.y] == VariantRules.EMPTY)
+			if (m.vanish[0].p == V.PAWN && m.vanish.length == 2
+				&& this.board[m.end.x][m.end.y] == V.EMPTY)
 			{
 				m.vanish[1].c = this.getOppCol(this.getColor(x,y));
 				// In the special case of en-passant, if
@@ -144,9 +142,9 @@ class AliceRules extends ChessRules
 				//  - board2 takes board1 : vanish[1] --> normal
 				let van = m.vanish[1];
 				if (mirrorSide==1 && codes.includes(this.getPiece(van.x,van.y)))
-					van.p = VariantRules.ALICE_CODES[van.p];
+					van.p = V.ALICE_CODES[van.p];
 				else if (mirrorSide==2 && pieces.includes(this.getPiece(van.x,van.y)))
-					van.p = VariantRules.ALICE_PIECES[van.p];
+					van.p = V.ALICE_PIECES[van.p];
 			}
 			return true;
 		});
@@ -166,16 +164,15 @@ class AliceRules extends ChessRules
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
 		var potentialMoves = [];
-		let [sizeX,sizeY] = VariantRules.size;
 		let sideBoard = [this.getSideBoard(1), this.getSideBoard(2)];
-		for (var i=0; i<sizeX; i++)
+		for (var i=0; i<V.size.x; i++)
 		{
-			for (var j=0; j<sizeY; j++)
+			for (var j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) == color)
+				if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == color)
 				{
 					const mirrorSide =
-						Object.keys(VariantRules.ALICE_CODES).includes(this.getPiece(i,j))
+						Object.keys(V.ALICE_CODES).includes(this.getPiece(i,j))
 							? 1
 							: 2;
 					Array.prototype.push.apply(potentialMoves,
@@ -189,16 +186,16 @@ class AliceRules extends ChessRules
 	// Play on sideboards [TODO: only one sideBoard required]
 	playSide(move, sideBoard)
 	{
-		const pieces = Object.keys(VariantRules.ALICE_CODES);
+		const pieces = Object.keys(V.ALICE_CODES);
 		move.vanish.forEach(psq => {
 			const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-			sideBoard[mirrorSide-1][psq.x][psq.y] = VariantRules.EMPTY;
+			sideBoard[mirrorSide-1][psq.x][psq.y] = V.EMPTY;
 		});
 		move.appear.forEach(psq => {
 			const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-			const piece = (mirrorSide == 1 ? psq.p : VariantRules.ALICE_PIECES[psq.p]);
+			const piece = (mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p]);
 			sideBoard[mirrorSide-1][psq.x][psq.y] = psq.c + piece;
-			if (piece == VariantRules.KING)
+			if (piece == V.KING)
 				this.kingPos[psq.c] = [psq.x,psq.y];
 		});
 	}
@@ -206,16 +203,16 @@ class AliceRules extends ChessRules
 	// Undo on sideboards
 	undoSide(move, sideBoard)
 	{
-		const pieces = Object.keys(VariantRules.ALICE_CODES);
+		const pieces = Object.keys(V.ALICE_CODES);
 		move.appear.forEach(psq => {
 			const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-			sideBoard[mirrorSide-1][psq.x][psq.y] = VariantRules.EMPTY;
+			sideBoard[mirrorSide-1][psq.x][psq.y] = V.EMPTY;
 		});
 		move.vanish.forEach(psq => {
 			const mirrorSide = (pieces.includes(psq.p) ? 1 : 2);
-			const piece = (mirrorSide == 1 ? psq.p : VariantRules.ALICE_PIECES[psq.p]);
+			const piece = (mirrorSide == 1 ? psq.p : V.ALICE_PIECES[psq.p]);
 			sideBoard[mirrorSide-1][psq.x][psq.y] = psq.c + piece;
-			if (piece == VariantRules.KING)
+			if (piece == V.KING)
 				this.kingPos[psq.c] = [psq.x,psq.y];
 		});
 	}
@@ -225,7 +222,7 @@ class AliceRules extends ChessRules
 		const color = this.turn;
 		this.playSide(move, sideBoard); //no need to track flags
 		const kp = this.kingPos[color];
-		const mirrorSide = sideBoard[0][kp[0]][kp[1]] != VariantRules.EMPTY ? 1 : 2;
+		const mirrorSide = (sideBoard[0][kp[0]][kp[1]] != V.EMPTY ? 1 : 2);
 		let saveBoard = this.board;
 		this.board = sideBoard[mirrorSide-1];
 		let res = this.isAttacked(kp, [this.getOppCol(color)]);
@@ -238,7 +235,7 @@ class AliceRules extends ChessRules
 	{
 		this.play(move);
 		const color = this.turn; //opponent
-		const pieces = Object.keys(VariantRules.ALICE_CODES);
+		const pieces = Object.keys(V.ALICE_CODES);
 		const kp = this.kingPos[color];
 		const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
 		let sideBoard = this.getSideBoard(mirrorSide);
@@ -276,7 +273,7 @@ class AliceRules extends ChessRules
 
 	checkGameEnd()
 	{
-		const pieces = Object.keys(VariantRules.ALICE_CODES);
+		const pieces = Object.keys(V.ALICE_CODES);
 		const color = this.turn;
 		const kp = this.kingPos[color];
 		const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
@@ -308,7 +305,7 @@ class AliceRules extends ChessRules
 
 	getNotation(move)
 	{
-		if (move.appear.length == 2 && move.appear[0].p == VariantRules.KING)
+		if (move.appear.length == 2 && move.appear[0].p == V.KING)
 		{
 			if (move.end.y < move.start.y)
 				return "0-0-0";
@@ -316,8 +313,7 @@ class AliceRules extends ChessRules
 				return "0-0";
 		}
 
-		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+		const finalSquare = String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 		const piece = this.getPiece(move.start.x, move.start.y);
 
 		const captureMark = (move.vanish.length > move.appear.length ? "x" : "");
diff --git a/public/javascripts/variants/Antiking.js b/public/javascripts/variants/Antiking.js
index 014a9c89..e7411684 100644
--- a/public/javascripts/variants/Antiking.js
+++ b/public/javascripts/variants/Antiking.js
@@ -49,7 +49,7 @@ class AntikingRules extends ChessRules
 	{
 		switch (this.getPiece(x,y))
 		{
-			case VariantRules.ANTIKING:
+			case V.ANTIKING:
 				return this.getPotentialAntikingMoves([x,y]);
 			default:
 				return super.getPotentialMovesFrom([x,y]);
@@ -58,7 +58,6 @@ class AntikingRules extends ChessRules
 
 	getPotentialAntikingMoves(sq)
 	{
-		const V = VariantRules;
 		return this.getSlideNJumpMoves(sq,
 			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
 	}
@@ -70,7 +69,6 @@ class AntikingRules extends ChessRules
 
 	isAttackedByKing([x,y], colors)
 	{
-		const V = VariantRules;
 		if (this.getPiece(x,y) == V.ANTIKING)
 			return false; //antiking is not attacked by king
 		return this.isAttackedBySlideNJump([x,y], colors, V.KING,
@@ -79,7 +77,6 @@ class AntikingRules extends ChessRules
 
 	isAttackedByAntiking([x,y], colors)
 	{
-		const V = VariantRules;
 		if ([V.KING,V.ANTIKING].includes(this.getPiece(x,y)))
 			return false; //(anti)king is not attacked by antiking
 		return this.isAttackedBySlideNJump([x,y], colors, V.ANTIKING,
@@ -114,7 +111,7 @@ class AntikingRules extends ChessRules
 		const piece = this.getPiece(move.start.x,move.start.y);
 		const c = this.getColor(move.start.x,move.start.y);
 		// Update antiking position
-		if (piece == VariantRules.ANTIKING)
+		if (piece == V.ANTIKING)
 		{
 			this.antikingPos[c][0] = move.appear[0].x;
 			this.antikingPos[c][1] = move.appear[0].y;
@@ -125,7 +122,7 @@ class AntikingRules extends ChessRules
 	{
 		super.unupdateVariables(move);
 		const c = this.getColor(move.start.x,move.start.y);
-		if (this.getPiece(move.start.x,move.start.y) == VariantRules.ANTIKING)
+		if (this.getPiece(move.start.x,move.start.y) == V.ANTIKING)
 			this.antikingPos[c] = [move.start.x, move.start.y];
 	}
 
diff --git a/public/javascripts/variants/Atomic.js b/public/javascripts/variants/Atomic.js
index 28ee1f26..e2f8ea33 100644
--- a/public/javascripts/variants/Atomic.js
+++ b/public/javascripts/variants/Atomic.js
@@ -14,8 +14,8 @@ class AtomicRules extends ChessRules
 				{
 					let x = m.end.x + step[0];
 					let y = m.end.y + step[1];
-					if (x>=0 && x<8 && y>=0 && y<8 && this.board[x][y] != VariantRules.EMPTY
-						&& this.getPiece(x,y) != VariantRules.PAWN)
+					if (V.OnBoard(x,y) && this.board[x][y] != V.EMPTY
+						&& this.getPiece(x,y) != V.PAWN)
 					{
 						m.vanish.push(
 							new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y}));
@@ -31,16 +31,14 @@ class AtomicRules extends ChessRules
 
 	getPotentialKingMoves([x,y])
 	{
-		const V = VariantRules;
 		// King cannot capture:
 		let moves = [];
-		let [sizeX,sizeY] = V.size;
 		const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
 		for (let step of steps)
 		{
-			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)
+			const i = x + step[0];
+			const j = y + step[1];
+			if (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
 				moves.push(this.getBasicMove([x,y], [i,j]));
 		}
 		return moves.concat(this.getCastleMoves([x,y]));
@@ -48,11 +46,8 @@ class AtomicRules extends ChessRules
 
 	isAttacked(sq, colors)
 	{
-		if (this.getPiece(sq[0],sq[1]) == VariantRules.KING
-			&& this.isAttackedByKing(sq, colors))
-		{
+		if (this.getPiece(sq[0],sq[1]) == V.KING && this.isAttackedByKing(sq, colors))
 			return false; //king cannot take...
-		}
 		return (this.isAttackedByPawn(sq, colors)
 			|| this.isAttackedByRook(sq, colors)
 			|| this.isAttackedByKnight(sq, colors)
diff --git a/public/javascripts/variants/Checkered.js b/public/javascripts/variants/Checkered.js
index 814d91b8..2d62d4b8 100644
--- a/public/javascripts/variants/Checkered.js
+++ b/public/javascripts/variants/Checkered.js
@@ -72,16 +72,16 @@ class CheckeredRules extends ChessRules
 	{
 		let standardMoves = super.getPotentialMovesFrom([x,y]);
 		const lastRank = this.turn == "w" ? 0 : 7;
-		if (this.getPiece(x,y) == VariantRules.KING)
+		if (this.getPiece(x,y) == V.KING)
 			return standardMoves; //king has to be treated differently (for castles)
 		let moves = [];
 		standardMoves.forEach(m => {
-			if (m.vanish[0].p == VariantRules.PAWN)
+			if (m.vanish[0].p == V.PAWN)
 			{
 				if (Math.abs(m.end.x-m.start.x)==2 && !this.pawnFlags[this.turn][m.start.y])
 					return; //skip forbidden 2-squares jumps
-				if (this.board[m.end.x][m.end.y] == VariantRules.EMPTY
-					&& m.vanish.length==2 && this.getColor(m.start.x,m.start.y) == 'c')
+				if (this.board[m.end.x][m.end.y] == V.EMPTY && m.vanish.length==2
+					&& this.getColor(m.start.x,m.start.y) == 'c')
 				{
 					return; //checkered pawns cannot take en-passant
 				}
@@ -94,7 +94,7 @@ class CheckeredRules extends ChessRules
 				m.appear[0].c = "c";
 				moves.push(m);
 				if (m.appear[0].p != m.vanish[1].p //avoid promotions (already treated):
-					&& (m.vanish[0].p != VariantRules.PAWN || m.end.x != lastRank))
+					&& (m.vanish[0].p != V.PAWN || m.end.x != lastRank))
 				{
 					// Add transformation into captured piece
 					let m2 = JSON.parse(JSON.stringify(m));
@@ -147,7 +147,7 @@ class CheckeredRules extends ChessRules
 			{
 				for (let i of [-1,1])
 				{
-					if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN
+					if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==V.PAWN
 						&& this.getColor(x+pawnShift,y+i)==c)
 					{
 						return true;
@@ -184,13 +184,10 @@ class CheckeredRules extends ChessRules
 
 	updateVariables(move)
 	{
-		const c = this.getColor(move.start.x,move.start.y);
-		if (c != 'c') //checkered not concerned by castle flags
-			super.updateVariables(move);
-
-		// Does it turn off a 2-squares pawn flag?
+		super.updateVariables(move);
+		// Does this move turn off a 2-squares pawn flag?
 		const secondRank = [1,6];
-		if (secondRank.includes(move.start.x) && move.vanish[0].p == VariantRules.PAWN)
+		if (secondRank.includes(move.start.x) && move.vanish[0].p == V.PAWN)
 			this.pawnFlags[move.start.x==6 ? "w" : "b"][move.start.y] = false;
 	}
 
@@ -207,18 +204,17 @@ class CheckeredRules extends ChessRules
 
 	evalPosition()
 	{
-		const [sizeX,sizeY] = VariantRules.size;
 		let evaluation = 0;
 		//Just count material for now, considering checkered neutral (...)
-		for (let i=0; i<sizeX; i++)
+		for (let i=0; i<V.size.x; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] != VariantRules.EMPTY)
+				if (this.board[i][j] != V.EMPTY)
 				{
 					const sqColor = this.getColor(i,j);
 					const sign = sqColor == "w" ? 1 : (sqColor=="b" ? -1 : 0);
-					evaluation += sign * VariantRules.VALUES[this.getPiece(i,j)];
+					evaluation += sign * V.VALUES[this.getPiece(i,j)];
 				}
 			}
 		}
@@ -255,10 +251,10 @@ class CheckeredRules extends ChessRules
 
 		// Translate final square
 		let finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+			String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 
 		let piece = this.getPiece(move.start.x, move.start.y);
-		if (piece == VariantRules.PAWN)
+		if (piece == V.PAWN)
 		{
 			// Pawn move
 			let notation = "";
diff --git a/public/javascripts/variants/Crazyhouse.js b/public/javascripts/variants/Crazyhouse.js
index b297f1fb..bf47197d 100644
--- a/public/javascripts/variants/Crazyhouse.js
+++ b/public/javascripts/variants/Crazyhouse.js
@@ -4,7 +4,6 @@ class CrazyhouseRules extends ChessRules
 	{
 		super.initVariables(fen);
 		// Also init reserves (used by the interface to show landing pieces)
-		const V = VariantRules;
 		this.reserve =
 		{
 			"w":
@@ -24,53 +23,48 @@ class CrazyhouseRules extends ChessRules
 				[V.QUEEN]: 0,
 			}
 		};
-		const [sizeX,sizeY] = VariantRules.size;
-		this.promoted = doubleArray(sizeX, sizeY, false);
+		this.promoted = doubleArray(V.size.x, V.size.y, false);
 		// May be a continuation: adjust numbers of pieces in reserve + promoted pieces
 		this.moves.forEach(m => { this.updateVariables(m); });
 	}
 
 	getColor(i,j)
 	{
-		const sizeX = VariantRules.size[0];
-		if (i >= sizeX)
-			return (i==sizeX ? "w" : "b");
+		if (i >= V.size.x)
+			return (i==V.size.x ? "w" : "b");
 		return this.board[i][j].charAt(0);
 	}
 	getPiece(i,j)
 	{
-		const sizeX = VariantRules.size[0];
-		if (i >= sizeX)
-			return VariantRules.RESERVE_PIECES[j];
+		if (i >= V.size.x)
+			return V.RESERVE_PIECES[j];
 		return this.board[i][j].charAt(1);
 	}
 
 	// Used by the interface:
 	getReservePpath(color, index)
 	{
-		return color + VariantRules.RESERVE_PIECES[index];
+		return color + V.RESERVE_PIECES[index];
 	}
 
 	// Ordering on reserve pieces
 	static get RESERVE_PIECES() {
-		const V = VariantRules;
 		return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
 	}
 
 	getReserveMoves([x,y])
 	{
 		const color = this.turn;
-		const p = VariantRules.RESERVE_PIECES[y];
+		const p = V.RESERVE_PIECES[y];
 		if (this.reserve[color][p] == 0)
 			return [];
 		let moves = [];
-		const [sizeX,sizeY] = VariantRules.size;
-		const pawnShift = (p==VariantRules.PAWN ? 1 : 0);
-		for (let i=pawnShift; i<sizeX-pawnShift; i++)
+		const pawnShift = (p==V.PAWN ? 1 : 0);
+		for (let i=pawnShift; i<V.size.x-pawnShift; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] == VariantRules.EMPTY)
+				if (this.board[i][j] == V.EMPTY)
 				{
 					let mv = new Move({
 						appear: [
@@ -94,8 +88,7 @@ class CrazyhouseRules extends ChessRules
 
 	getPotentialMovesFrom([x,y])
 	{
-		const sizeX = VariantRules.size[0];
-		if (x >= sizeX)
+		if (x >= V.size.x)
 		{
 			// Reserves, outside of board: x == sizeX(+1)
 			return this.getReserveMoves([x,y]);
@@ -108,9 +101,8 @@ class CrazyhouseRules extends ChessRules
 	{
 		let moves = super.getAllValidMoves();
 		const color = this.turn;
-		const sizeX = VariantRules.size[0];
-		for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
-			moves = moves.concat(this.getReserveMoves([sizeX+(color=="w"?0:1),i]));
+		for (let i=0; i<V.RESERVE_PIECES.length; i++)
+			moves = moves.concat(this.getReserveMoves([V.size.x+(color=="w"?0:1),i]));
 		return this.filterValid(moves);
 	}
 
@@ -118,11 +110,12 @@ class CrazyhouseRules extends ChessRules
 	{
 		if (!super.atLeastOneMove())
 		{
-			const sizeX = VariantRules.size[0];
-			// Scan for reserve moves
-			for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+			const color = this.turn;
+			// Search one reserve move
+			for (let i=0; i<V.RESERVE_PIECES.length; i++)
 			{
-				let moves = this.filterValid(this.getReserveMoves([sizeX,i]));
+				let moves = this.filterValid(
+					this.getReserveMoves([V.size.x+(this.turn=="w"?0:1), i]) );
 				if (moves.length > 0)
 					return true;
 			}
@@ -137,7 +130,6 @@ class CrazyhouseRules extends ChessRules
 		if (move.vanish.length == 2 && move.appear.length == 2)
 			return; //skip castle
 		const color = this.turn;
-		const V = VariantRules;
 		if (move.vanish.length == 0)
 		{
 			this.reserve[color][move.appear[0].p]--;
@@ -149,7 +141,7 @@ class CrazyhouseRules extends ChessRules
 		this.promoted[move.end.x][move.end.y] = move.movePromoted
 			|| (move.vanish[0].p == V.PAWN && move.appear[0].p != V.PAWN);
 		if (move.capturePromoted)
-			this.reserve[color][VariantRules.PAWN]++;
+			this.reserve[color][V.PAWN]++;
 		else if (move.vanish.length == 2)
 			this.reserve[color][move.vanish[1].p]++;
 	}
@@ -160,7 +152,6 @@ class CrazyhouseRules extends ChessRules
 		if (move.vanish.length == 2 && move.appear.length == 2)
 			return;
 		const color = this.turn;
-		const V = VariantRules;
 		if (move.vanish.length == 0)
 		{
 			this.reserve[color][move.appear[0].p]++;
@@ -170,7 +161,7 @@ class CrazyhouseRules extends ChessRules
 			this.promoted[move.start.x][move.start.y] = true;
 		this.promoted[move.end.x][move.end.y] = move.capturePromoted;
 		if (move.capturePromoted)
-			this.reserve[color][VariantRules.PAWN]--;
+			this.reserve[color][V.PAWN]--;
 		else if (move.vanish.length == 2)
 			this.reserve[color][move.vanish[1].p]--;
 	}
@@ -181,11 +172,11 @@ class CrazyhouseRules extends ChessRules
 	{
 		let evaluation = super.evalPosition();
 		// Add reserves:
-		for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+		for (let i=0; i<V.RESERVE_PIECES.length; i++)
 		{
-			const p = VariantRules.RESERVE_PIECES[i];
-			evaluation += this.reserve["w"][p] * VariantRules.VALUES[p];
-			evaluation -= this.reserve["b"][p] * VariantRules.VALUES[p];
+			const p = V.RESERVE_PIECES[i];
+			evaluation += this.reserve["w"][p] * V.VALUES[p];
+			evaluation -= this.reserve["b"][p] * V.VALUES[p];
 		}
 		return evaluation;
 	}
@@ -196,9 +187,9 @@ class CrazyhouseRules extends ChessRules
 			return super.getNotation(move);
 		// Rebirth:
 		const piece =
-			(move.appear[0].p != VariantRules.PAWN ? move.appear[0].p.toUpperCase() : "");
+			(move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : "");
 		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+			String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 		return piece + "@" + finalSquare;
 	}
 
@@ -207,7 +198,7 @@ class CrazyhouseRules extends ChessRules
 		if (move.vanish.length > 0)
 			return super.getLongNotation(move);
 		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+			String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 		return "@" + finalSquare;
 	}
 }
diff --git a/public/javascripts/variants/Extinction.js b/public/javascripts/variants/Extinction.js
index f0ebeabb..2b0aecaa 100644
--- a/public/javascripts/variants/Extinction.js
+++ b/public/javascripts/variants/Extinction.js
@@ -3,7 +3,6 @@ class ExtinctionRules extends ChessRules
 	initVariables(fen)
 	{
 		super.initVariables(fen);
-		const V = VariantRules;
 		this.material =
 		{
 			"w":
@@ -32,10 +31,8 @@ class ExtinctionRules extends ChessRules
 		let moves = super.getPotentialPawnMoves([x,y]);
 		// Add potential promotions into king
 		const color = this.turn;
-		const V = VariantRules;
-		const [sizeX,sizeY] = V.size;
 		const shift = (color == "w" ? -1 : 1);
-		const lastRank = (color == "w" ? 0 : sizeX-1);
+		const lastRank = (color == "w" ? 0 : V.size.x-1);
 
 		if (x+shift == lastRank)
 		{
@@ -43,13 +40,13 @@ class ExtinctionRules extends ChessRules
 			if (this.board[x+shift][y] == V.EMPTY)
 				moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:V.KING}));
 			// Captures
-			if (y>0 && this.canTake([x,y], [x+shift,y-1])
-				&& this.board[x+shift][y-1] != V.EMPTY)
+			if (y>0 && this.board[x+shift][y-1] != V.EMPTY
+				&& this.canTake([x,y], [x+shift,y-1]))
 			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:V.KING}));
 			}
-			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
-				&& this.board[x+shift][y+1] != V.EMPTY)
+			if (y<V.size.y-1 && this.board[x+shift][y+1] != V.EMPTY
+				&& this.canTake([x,y], [x+shift,y+1]))
 			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:V.KING}));
 			}
@@ -81,7 +78,7 @@ class ExtinctionRules extends ChessRules
 		if (move.appear[0].p != move.vanish[0].p)
 		{
 			this.material[move.appear[0].c][move.appear[0].p]++;
-			this.material[move.appear[0].c][VariantRules.PAWN]--;
+			this.material[move.appear[0].c][V.PAWN]--;
 		}
 		if (move.vanish.length==2 && move.appear.length==1) //capture
 			this.material[move.vanish[1].c][move.vanish[1].p]--;
@@ -93,7 +90,7 @@ class ExtinctionRules extends ChessRules
 		if (move.appear[0].p != move.vanish[0].p)
 		{
 			this.material[move.appear[0].c][move.appear[0].p]--;
-			this.material[move.appear[0].c][VariantRules.PAWN]++;
+			this.material[move.appear[0].c][V.PAWN]++;
 		}
 		if (move.vanish.length==2 && move.appear.length==1)
 			this.material[move.vanish[1].c][move.vanish[1].p]++;
@@ -130,7 +127,7 @@ class ExtinctionRules extends ChessRules
 			p => { return this.material[color][p] == 0; }))
 		{
 			// Very negative (resp. positive) if white (reps. black) pieces set is incomplete
-			return (color=="w"?-1:1) * VariantRules.INFINITY;
+			return (color=="w"?-1:1) * V.INFINITY;
 		}
 		return super.evalPosition();
 	}
diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js
index 9e1504d9..f5ae0653 100644
--- a/public/javascripts/variants/Grand.js
+++ b/public/javascripts/variants/Grand.js
@@ -4,7 +4,6 @@ class GrandRules extends ChessRules
 {
 	static getPpath(b)
 	{
-		const V = VariantRules;
 		return ([V.MARSHALL,V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b;
 	}
 
@@ -14,7 +13,7 @@ class GrandRules extends ChessRules
 		this.captures = { "w": {}, "b": {} }; //for promotions
 	}
 
-	static get size() { return [10,10]; }
+	static get size() { return {x:10,y:10}; }
 
 	static get MARSHALL() { return 'm'; } //rook+knight
 	static get CARDINAL() { return 'c'; } //bishop+knight
@@ -23,7 +22,7 @@ class GrandRules extends ChessRules
 	getEpSquare(move)
 	{
 		const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
-		if (this.getPiece(sx,sy) == VariantRules.PAWN && Math.abs(sx - ex) >= 2)
+		if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) >= 2)
 		{
 			const step = (ex-sx) / Math.abs(ex-sx);
 			let res = [{
@@ -46,9 +45,9 @@ class GrandRules extends ChessRules
 	{
 		switch (this.getPiece(x,y))
 		{
-			case VariantRules.MARSHALL:
+			case V.MARSHALL:
 				return this.getPotentialMarshallMoves([x,y]);
-			case VariantRules.CARDINAL:
+			case V.CARDINAL:
 				return this.getPotentialCardinalMoves([x,y]);
 			default:
 				return super.getPotentialMovesFrom([x,y])
@@ -61,8 +60,7 @@ class GrandRules extends ChessRules
 	{
 		const color = this.turn;
 		let moves = [];
-		const V = VariantRules;
-		const [sizeX,sizeY] = VariantRules.size;
+		const [sizeX,sizeY] = [V.size.x,V.size.y];
 		const shift = (color == "w" ? -1 : 1);
 		const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
 		const lastRanks = (color == "w" ? [0,1,2] : [sizeX-1,sizeX-2,sizeX-3]);
@@ -151,14 +149,12 @@ class GrandRules extends ChessRules
 
 	getPotentialMarshallMoves(sq)
 	{
-		const V = VariantRules;
 		return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
 			this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
 	}
 
 	getPotentialCardinalMoves(sq)
 	{
-		const V = VariantRules;
 		return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
 			this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
 	}
@@ -172,7 +168,6 @@ class GrandRules extends ChessRules
 
 	isAttackedByMarshall(sq, colors)
 	{
-		const V = VariantRules;
 		return this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK])
 			|| this.isAttackedBySlideNJump(
 				sq, colors, V.MARSHALL, V.steps[V.KNIGHT], "oneStep");
@@ -180,7 +175,6 @@ class GrandRules extends ChessRules
 
 	isAttackedByCardinal(sq, colors)
 	{
-		const V = VariantRules;
 		return this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP])
 			|| this.isAttackedBySlideNJump(
 				sq, colors, V.CARDINAL, V.steps[V.KNIGHT], "oneStep");
@@ -189,8 +183,7 @@ class GrandRules extends ChessRules
 	updateVariables(move)
 	{
 		super.updateVariables(move);
-		if (move.vanish.length==2 && move.appear.length==1
-			&& move.vanish[1].p != VariantRules.PAWN)
+		if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != V.PAWN)
 		{
 			// Capture: update this.captures
 			if (!this.captures[move.vanish[1].c][move.vanish[1].p])
@@ -203,8 +196,7 @@ class GrandRules extends ChessRules
 	unupdateVariables(move)
 	{
 		super.unupdateVariables(move);
-		if (move.vanish.length==2 && move.appear.length==1
-			&& move.vanish[1].p != VariantRules.PAWN)
+		if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != V.PAWN)
 		{
 			this.captures[move.vanish[1].c][move.vanish[1].p] =
 				Math.max(0, this.captures[move.vanish[1].c][move.vanish[1].p]-1);
diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Loser.js
index 6a322b9d..3def40a9 100644
--- a/public/javascripts/variants/Loser.js
+++ b/public/javascripts/variants/Loser.js
@@ -18,10 +18,8 @@ class LoserRules extends ChessRules
 
 		// Complete with promotion(s) into king, if possible
 		const color = this.turn;
-		const V = VariantRules;
-		const [sizeX,sizeY] = VariantRules.size;
 		const shift = (color == "w" ? -1 : 1);
-		const lastRank = (color == "w" ? 0 : sizeX-1);
+		const lastRank = (color == "w" ? 0 : V.size.x-1);
 		if (x+shift == lastRank)
 		{
 			// Normal move
@@ -33,7 +31,7 @@ class LoserRules extends ChessRules
 			{
 				moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:V.KING}));
 			}
-			if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
+			if (y<V.size.y-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:V.KING}));
@@ -45,7 +43,6 @@ class LoserRules extends ChessRules
 
 	getPotentialKingMoves(sq)
 	{
-		const V = VariantRules;
 		return this.getSlideNJumpMoves(sq,
 			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
 	}
@@ -55,12 +52,11 @@ class LoserRules extends ChessRules
 	{
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
-		const [sizeX,sizeY] = VariantRules.size;
-		for (let i=0; i<sizeX; i++)
+		for (let i=0; i<V.size.x; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
+				if (this.board[i][j] != V.EMPTY && this.getColor(i,j) != oppCol)
 				{
 					const moves = this.getPotentialMovesFrom([i,j]);
 					if (moves.length > 0)
@@ -88,7 +84,7 @@ class LoserRules extends ChessRules
 		let moves = this.filterValid( this.getPotentialMovesFrom(sq) );
 		// This is called from interface: we need to know if a capture is possible
 		if (this.atLeastOneCapture())
-			moves = VariantRules.KeepCaptures(moves);
+			moves = V.KeepCaptures(moves);
 		return moves;
 	}
 
@@ -96,7 +92,7 @@ class LoserRules extends ChessRules
 	{
 		let moves = super.getAllValidMoves();
 		if (moves.some(m => { return m.vanish.length == 2; }))
-			moves = VariantRules.KeepCaptures(moves);
+			moves = V.KeepCaptures(moves);
 		return moves;
 	}
 
diff --git a/public/javascripts/variants/Magnetic.js b/public/javascripts/variants/Magnetic.js
index ec2c9752..2e90e6c0 100644
--- a/public/javascripts/variants/Magnetic.js
+++ b/public/javascripts/variants/Magnetic.js
@@ -23,7 +23,6 @@ class MagneticRules extends ChessRules
 	// TODO: job is done multiple times for (normal) promotions.
 	applyMagneticLaws(move)
 	{
-		const V = VariantRules;
 		if (move.appear[0].p == V.KING && move.appear.length==1)
 			return [move]; //kings are not charged
 		const aIdx = (move.appear[0].p != V.KING ? 0 : 1); //if castling, rook is charged
@@ -32,11 +31,10 @@ class MagneticRules extends ChessRules
 		const lastRank = (color=="w" ? 0 : 7);
 		const standardMove = JSON.parse(JSON.stringify(move));
 		this.play(standardMove);
-		const [sizeX,sizeY] = V.size;
 		for (let step of [[-1,0],[1,0],[0,-1],[0,1]])
 		{
 			let [i,j] = [x+step[0],y+step[1]];
-			while (i>=0 && i<sizeX && j>=0 && j<sizeY)
+			while (V.OnBoard(i,j))
 			{
 				if (this.board[i][j] != V.EMPTY)
 				{
@@ -71,7 +69,7 @@ class MagneticRules extends ChessRules
 						{
 							// Push it until we meet an obstacle or edge of the board
 							let [ii,jj] = [i+step[0],j+step[1]];
-							while (ii>=0 && ii<sizeX && jj>=0 && jj<sizeY)
+							while (V.OnBoard(ii,jj))
 							{
 								if (this.board[ii][jj] != V.EMPTY)
 									break;
@@ -166,9 +164,9 @@ class MagneticRules extends ChessRules
 	{
 		super.updateVariables(move);
 		const c = this.getColor(move.start.x,move.start.y);
-		if (this.board[move.end.x][move.end.y] != VariantRules.EMPTY
+		if (this.board[move.end.x][move.end.y] != V.EMPTY
 			&& c != this.getColor(move.end.x,move.end.y)
-			&& this.getPiece(move.end.x,move.end.y) == VariantRules.KING)
+			&& this.getPiece(move.end.x,move.end.y) == V.KING)
 		{
 			// We took opponent king !
 			const oppCol = this.getOppCol(c);
diff --git a/public/javascripts/variants/Switching.js b/public/javascripts/variants/Switching.js
index cedba3db..ee84e4ca 100644
--- a/public/javascripts/variants/Switching.js
+++ b/public/javascripts/variants/Switching.js
@@ -6,7 +6,6 @@ class SwitchingRules extends ChessRules
 		const c = this.getColor(x1,y1); //same as color at square 2
 		const p1 = this.getPiece(x1,y1);
 		const p2 = this.getPiece(x2,y2);
-		const V = VariantRules;
 		if (p1 == V.KING && p2 == V.ROOK)
 			return []; //avoid duplicate moves (potential conflict with castle)
 		let move = new Move({
@@ -22,8 +21,7 @@ class SwitchingRules extends ChessRules
 			end: {x:x2,y:y2}
 		});
 		// Move completion: promote switched pawns (as in Magnetic)
-		const sizeX = VariantRules.size[0];
-		const lastRank = (c == "w" ? 0 : sizeX-1);
+		const lastRank = (c == "w" ? 0 : V.size.x-1);
 		let moves = [];
 		if ((p1==V.PAWN && x2==lastRank) || (p2==V.PAWN && x1==lastRank))
 		{
@@ -55,10 +53,8 @@ class SwitchingRules extends ChessRules
 	{
 		let moves = super.getPotentialMovesFrom([x,y]);
 		// Add switches: respecting chessboard ordering if "computer" is on
-		const V = VariantRules;
 		const color = this.turn;
 		const piece = this.getPiece(x,y);
-		const [sizeX,sizeY] = V.size;
 		const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
 		const kp = this.kingPos[color];
 		const oppCol = this.getOppCol(color);
@@ -67,7 +63,7 @@ class SwitchingRules extends ChessRules
 			let [i,j] = [x+step[0],y+step[1]];
 			if (!!computer && (i<x || (i==x && j<y)))
 				continue; //only switch with superior indices
-			if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j]!=V.EMPTY
+			if (V.OnBoard(i,j) && this.board[i][j]!=V.EMPTY
 				&& this.getColor(i,j)==color && this.getPiece(i,j)!=piece
 				// No switching under check (theoretically non-king pieces could, but not)
 				&& !this.isAttacked(kp, [oppCol]))
@@ -87,12 +83,11 @@ class SwitchingRules extends ChessRules
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
 		let potentialMoves = [];
-		const [sizeX,sizeY] = VariantRules.size;
-		for (let i=0; i<sizeX; i++)
+		for (let i=0; i<V.size.x; i++)
 		{
-			for (let j=0; j<sizeY; j++)
+			for (let j=0; j<V.size.y; j++)
 			{
-				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) == color)
+				if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == color)
 				{
 					Array.prototype.push.apply(potentialMoves,
 						this.getPotentialMovesFrom([i,j], computer));
@@ -106,7 +101,7 @@ class SwitchingRules extends ChessRules
 	{
 		super.updateVariables(move);
 		if (move.appear.length == 2 && move.vanish.length == 2
-			&& move.appear[1].p == VariantRules.KING)
+			&& move.appear[1].p == V.KING)
 		{
 			// Switch with the king; not castle, and not handled by main class
 			const color = this.getColor(move.start.x, move.start.y);
@@ -118,7 +113,7 @@ class SwitchingRules extends ChessRules
 	{
 		super.unupdateVariables(move);
 		if (move.appear.length == 2 && move.vanish.length == 2
-			&& move.appear[1].p == VariantRules.KING)
+			&& move.appear[1].p == V.KING)
 		{
 			const color = this.getColor(move.start.x, move.start.y);
 			this.kingPos[color] = [move.appear[0].x, move.appear[0].y];
@@ -132,13 +127,13 @@ class SwitchingRules extends ChessRules
 		if (move.appear.length == 1)
 			return super.getNotation(move); //no switch
 		// Switch or castle
-		if (move.appear[0].p == VariantRules.KING && move.appear[1].p == VariantRules.ROOK)
+		if (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK)
 			return (move.end.y < move.start.y ? "0-0-0" : "0-0");
 		// Switch:
 		const startSquare =
-			String.fromCharCode(97 + move.start.y) + (VariantRules.size[0]-move.start.x);
+			String.fromCharCode(97 + move.start.y) + (V.size.x-move.start.x);
 		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+			String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 		return "S" + startSquare + finalSquare;
 	}
 }
diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js
index ab12c716..04ef29b0 100644
--- a/public/javascripts/variants/Ultima.js
+++ b/public/javascripts/variants/Ultima.js
@@ -56,14 +56,12 @@ class UltimaRules extends ChessRules
 		const piece = this.getPiece(x,y);
 		const color = this.getColor(x,y);
 		const oppCol = this.getOppCol(color);
-		const V = VariantRules;
 		const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-		const [sizeX,sizeY] = V.size;
 		outerLoop:
 		for (let step of adjacentSteps)
 		{
 			const [i,j] = [x+step[0],y+step[1]];
-			if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] != V.EMPTY
+			if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY
 				&& this.getColor(i,j) == oppCol)
 			{
 				const oppPiece = this.getPiece(i,j);
@@ -75,8 +73,8 @@ class UltimaRules extends ChessRules
 						const [i2,j2] = [i+step2[0],j+step2[1]];
 						if (i2 == x && j2 == y)
 							continue; //skip initial piece!
-						if (i2>=0 && i2<sizeX && j2>=0 && j2<sizeY
-							&& this.board[i2][j2] != V.EMPTY && this.getColor(i2,j2) == color)
+						if (V.OnBoard(i2,j2) && this.board[i2][j2] != V.EMPTY
+							&& this.getColor(i2,j2) == color)
 						{
 							if ([V.BISHOP,V.IMMOBILIZER].includes(this.getPiece(i2,j2)))
 								return false;
@@ -99,7 +97,7 @@ class UltimaRules extends ChessRules
 			return [];
 		switch (this.getPiece(x,y))
 		{
-			case VariantRules.IMMOBILIZER:
+			case V.IMMOBILIZER:
 				return this.getPotentialImmobilizerMoves([x,y]);
 			default:
 				return super.getPotentialMovesFrom([x,y]);
@@ -111,14 +109,12 @@ class UltimaRules extends ChessRules
 		const color = this.getColor(x,y);
 		const piece = this.getPiece(x,y);
 		let moves = [];
-		const [sizeX,sizeY] = VariantRules.size;
 		outerLoop:
 		for (let step of steps)
 		{
 			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)
+			while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
 			{
 				moves.push(this.getBasicMove([x,y], [i,j]));
 				if (oneStep !== undefined)
@@ -127,11 +123,8 @@ class UltimaRules extends ChessRules
 				j += step[1];
 			}
 			// Only king can take on occupied square:
-			if (piece==VariantRules.KING && i>=0 && i<sizeX && j>=0
-				&& j<sizeY && this.canTake([x,y], [i,j]))
-			{
+			if (piece==V.KING && V.OnBoard(i,j) && this.canTake([x,y], [i,j]))
 				moves.push(this.getBasicMove([x,y], [i,j]));
-			}
 		}
 		return moves;
 	}
@@ -139,8 +132,7 @@ class UltimaRules extends ChessRules
 	// Modify capturing moves among listed pawn moves
 	addPawnCaptures(moves, byChameleon)
 	{
-		const steps = VariantRules.steps[VariantRules.ROOK];
-		const [sizeX,sizeY] = VariantRules.size;
+		const steps = V.steps[V.ROOK];
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
 		moves.forEach(m => {
@@ -150,17 +142,16 @@ class UltimaRules extends ChessRules
 			for (let step of steps)
 			{
 				const sq2 = [m.end.x+2*step[0],m.end.y+2*step[1]];
-				if (sq2[0]>=0 && sq2[0]<sizeX && sq2[1]>=0 && sq2[1]<sizeY
-					&& this.board[sq2[0]][sq2[1]] != VariantRules.EMPTY
+				if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] != V.EMPTY
 					&& this.getColor(sq2[0],sq2[1]) == color)
 				{
 					// Potential capture
 					const sq1 = [m.end.x+step[0],m.end.y+step[1]];
-					if (this.board[sq1[0]][sq1[1]] != VariantRules.EMPTY
+					if (this.board[sq1[0]][sq1[1]] != V.EMPTY
 						&& this.getColor(sq1[0],sq1[1]) == oppCol)
 					{
 						const piece1 = this.getPiece(sq1[0],sq1[1]);
-						if (!byChameleon || piece1 == VariantRules.PAWN)
+						if (!byChameleon || piece1 == V.PAWN)
 						{
 							m.vanish.push(new PiPo({
 								x:sq1[0],
@@ -196,10 +187,10 @@ class UltimaRules extends ChessRules
 			const corner2 = [kp[0], m.end.y];
 			for (let [i,j] of [corner1,corner2])
 			{
-				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) == oppCol)
+				if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol)
 				{
 					const piece = this.getPiece(i,j);
-					if (!byChameleon || piece == VariantRules.ROOK)
+					if (!byChameleon || piece == V.ROOK)
 					{
 						m.vanish.push( new PiPo({
 							x:i,
@@ -225,9 +216,7 @@ class UltimaRules extends ChessRules
 	getKnightCaptures(startSquare, byChameleon)
 	{
 		// Look in every direction for captures
-		const V = VariantRules;
 		const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-		const [sizeX,sizeY] = V.size;
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
 		let moves = [];
@@ -237,12 +226,12 @@ class UltimaRules extends ChessRules
 		for (let step of steps)
 		{
 			let [i,j] = [x+step[0], y+step[1]];
-			while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j]==V.EMPTY)
+			while (V.OnBoard(i,j) && this.board[i][j]==V.EMPTY)
 			{
 				i += step[0];
 				j += step[1];
 			}
-			if (i<0 || i>=sizeX || j<0 || j>=sizeY || this.getColor(i,j)==color
+			if (!V.OnBoard(i,j) || this.getColor(i,j)==color
 				|| (!!byChameleon && this.getPiece(i,j)!=V.KNIGHT))
 			{
 				continue;
@@ -254,7 +243,7 @@ class UltimaRules extends ChessRules
 			let last = [i,j];
 			let cur = [i+step[0],j+step[1]];
 			let vanished = [ new PiPo({x:x,y:y,c:color,p:piece}) ];
-			while (cur[0]>=0 && cur[0]<sizeX && cur[1]>=0 && cur[1]<sizeY)
+			while (V.OnBoard(cur[0],cur[1]))
 			{
 				if (this.board[last[0]][last[1]] != V.EMPTY)
 				{
@@ -304,9 +293,8 @@ class UltimaRules extends ChessRules
 		this.addQueenCaptures(moves, "asChameleon");
 		// Post-processing: merge similar moves, concatenating vanish arrays
 		let mergedMoves = {};
-		const [sizeX,sizeY] = VariantRules.size;
 		moves.forEach(m => {
-			const key = m.end.x + sizeX * m.end.y;
+			const key = m.end.x + V.size.x * m.end.y;
 			if (!mergedMoves[key])
 				mergedMoves[key] = m;
 			else
@@ -327,16 +315,13 @@ class UltimaRules extends ChessRules
 		if (moves.length == 0)
 			return;
 		const [x,y] = [moves[0].start.x,moves[0].start.y];
-		const V = VariantRules;
 		const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
 		let capturingDirections = [];
 		const color = this.turn;
 		const oppCol = this.getOppCol(color);
-		const [sizeX,sizeY] = V.size;
 		adjacentSteps.forEach(step => {
 			const [i,j] = [x+step[0],y+step[1]];
-			if (i>=0 && i<sizeX && j>=0 && j<sizeY
-				&& this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol
+			if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol
 				&& (!byChameleon || this.getPiece(i,j) == V.QUEEN))
 			{
 				capturingDirections.push(step);
@@ -378,7 +363,6 @@ class UltimaRules extends ChessRules
 
 	getPotentialKingMoves(sq)
 	{
-		const V = VariantRules;
 		return this.getSlideNJumpMoves(sq,
 			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
 	}
@@ -390,38 +374,31 @@ class UltimaRules extends ChessRules
 		// Square (x,y) must be surroundable by two enemy pieces,
 		// and one of them at least should be a pawn (moving).
 		const dirs = [ [1,0],[0,1] ];
-		const steps = VariantRules.steps[VariantRules.ROOK];
-		const [sizeX,sizeY] = VariantRules.size;
+		const steps = V.steps[V.ROOK];
 		for (let dir of dirs)
 		{
 			const [i1,j1] = [x-dir[0],y-dir[1]]; //"before"
 			const [i2,j2] = [x+dir[0],y+dir[1]]; //"after"
-			if (i1>=0 && i1<sizeX && i2>=0 && i2<sizeX
-				&& j1>=0 && j1<sizeY && j2>=0 && j2<sizeY)
+			if (V.OnBoard(i1,j1) && V.OnBoard(i2,j2))
 			{
-				if ((this.board[i1][j1]!=VariantRules.EMPTY
-					&& colors.includes(this.getColor(i1,j1))
-					&& this.board[i2][j2]==VariantRules.EMPTY)
+				if ((this.board[i1][j1]!=V.EMPTY && colors.includes(this.getColor(i1,j1))
+					&& this.board[i2][j2]==V.EMPTY)
 						||
-					(this.board[i2][j2]!=VariantRules.EMPTY
-					&& colors.includes(this.getColor(i2,j2))
-					&& this.board[i1][j1]==VariantRules.EMPTY))
+					(this.board[i2][j2]!=V.EMPTY && colors.includes(this.getColor(i2,j2))
+					&& this.board[i1][j1]==V.EMPTY))
 				{
 					// Search a movable enemy pawn landing on the empty square
 					for (let step of steps)
 					{
-						let [ii,jj] = (this.board[i1][j1]==VariantRules.EMPTY ? [i1,j1] : [i2,j2]);
+						let [ii,jj] = (this.board[i1][j1]==V.EMPTY ? [i1,j1] : [i2,j2]);
 						let [i3,j3] = [ii+step[0],jj+step[1]];
-						while (i3>=0 && i3<sizeX && j3>=0 && j3<sizeY
-							&& this.board[i3][j3]==VariantRules.EMPTY)
+						while (V.OnBoard(i3,j3) && this.board[i3][j3]==V.EMPTY)
 						{
 							i3 += step[0];
 							j3 += step[1];
 						}
-						if (i3>=0 && i3<sizeX && j3>=0 && j3<sizeY
-							&& colors.includes(this.getColor(i3,j3))
-							&& this.getPiece(i3,j3) == VariantRules.PAWN
-							&& !this.isImmobilized([i3,j3]))
+						if (V.OnBoard(i3,j3) && colors.includes(this.getColor(i3,j3))
+							&& this.getPiece(i3,j3) == V.PAWN && !this.isImmobilized([i3,j3]))
 						{
 							return true;
 						}
@@ -436,20 +413,18 @@ class UltimaRules extends ChessRules
 	{
 		// King must be on same column or row,
 		// and a rook should be able to reach a capturing square
-		const [sizeX,sizeY] = VariantRules.size;
 		// colors contains only one element, giving the oppCol and thus king position
 		const sameRow = (x == this.kingPos[colors[0]][0]);
 		const sameColumn = (y == this.kingPos[colors[0]][1]);
 		if (sameRow || sameColumn)
 		{
 			// Look for the enemy rook (maximum 1)
-			for (let i=0; i<sizeX; i++)
+			for (let i=0; i<V.size.x; i++)
 			{
-				for (let j=0; j<sizeY; j++)
+				for (let j=0; j<V.size.y; j++)
 				{
-					if (this.board[i][j] != VariantRules.EMPTY
-						&& colors.includes(this.getColor(i,j))
-						&& this.getPiece(i,j) == VariantRules.ROOK)
+					if (this.board[i][j] != V.EMPTY && colors.includes(this.getColor(i,j))
+						&& this.getPiece(i,j) == V.ROOK)
 					{
 						if (this.isImmobilized([i,j]))
 							return false; //because only one rook
@@ -472,25 +447,23 @@ class UltimaRules extends ChessRules
 	{
 		// Square (x,y) must be on same line as a knight,
 		// and there must be empty square(s) behind.
-		const V = VariantRules;
 		const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-		const [sizeX,sizeY] = V.size;
 		outerLoop:
 		for (let step of steps)
 		{
 			const [i0,j0] = [x+step[0],y+step[1]];
-			if (i0>=0 && i0<sizeX && j0>=0 && j0<sizeY && this.board[i0][j0] == V.EMPTY)
+			if (V.OnBoard(i0,j0) && this.board[i0][j0] == V.EMPTY)
 			{
 				// Try in opposite direction:
 				let [i,j] = [x-step[0],y-step[1]];
-				while (i>=0 && i<sizeX && j>=0 && j<sizeY)
+				while (V.OnBoard(i,j))
 				{
-					while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == V.EMPTY)
+					while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
 					{
 						i -= step[0];
 						j -= step[1];
 					}
-					if (i>=0 && i<sizeX && j>=0 && j<sizeY)
+					if (V.OnBoard(i,j))
 					{
 						if (colors.includes(this.getColor(i,j)))
 						{
@@ -514,13 +487,11 @@ class UltimaRules extends ChessRules
 	{
 		// We cheat a little here: since this function is used exclusively for king,
 		// it's enough to check the immediate surrounding of the square.
-		const V = VariantRules;
 		const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-		const [sizeX,sizeY] = V.size;
 		for (let step of adjacentSteps)
 		{
 			const [i,j] = [x+step[0],y+step[1]];
-			if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j]!=V.EMPTY
+			if (V.OnBoard(i,j) && this.board[i][j]!=V.EMPTY
 				&& colors.includes(this.getColor(i,j)) && this.getPiece(i,j) == V.BISHOP)
 			{
 				return true; //bishops are never immobilized
@@ -533,14 +504,11 @@ class UltimaRules extends ChessRules
 	{
 		// Square (x,y) must be adjacent to a queen, and the queen must have
 		// some free space in the opposite direction from (x,y)
-		const V = VariantRules;
 		const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
-		const [sizeX,sizeY] = V.size;
 		for (let step of adjacentSteps)
 		{
 			const sq2 = [x+2*step[0],y+2*step[1]];
-			if (sq2[0]>=0 && sq2[0]<sizeX && sq2[1]>=0 && sq2[1]<sizeY
-				&& this.board[sq2[0]][sq2[1]] == V.EMPTY)
+			if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] == V.EMPTY)
 			{
 				const sq1 = [x+step[0],y+step[1]];
 				if (this.board[sq1[0]][sq1[1]] != V.EMPTY
@@ -560,7 +528,7 @@ class UltimaRules extends ChessRules
 		// Just update king(s) position(s)
 		const piece = this.getPiece(move.start.x,move.start.y);
 		const c = this.getColor(move.start.x,move.start.y);
-		if (piece == VariantRules.KING && move.appear.length > 0)
+		if (piece == V.KING && move.appear.length > 0)
 		{
 			this.kingPos[c][0] = move.appear[0].x;
 			this.kingPos[c][1] = move.appear[0].y;
@@ -642,20 +610,19 @@ class UltimaRules extends ChessRules
 	getNotation(move)
 	{
 		const initialSquare =
-			String.fromCharCode(97 + move.start.y) + (VariantRules.size[0]-move.start.x);
-		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+			String.fromCharCode(97 + move.start.y) + (V.size.x-move.start.x);
+		const finalSquare = String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 		let notation = undefined;
-		if (move.appear[0].p == VariantRules.PAWN)
+		if (move.appear[0].p == V.PAWN)
 		{
 			// Pawn: generally ambiguous short notation, so we use full description
 			notation = "P" + initialSquare + finalSquare;
 		}
-		else if (move.appear[0].p == VariantRules.KING)
+		else if (move.appear[0].p == V.KING)
 			notation = "K" + (move.vanish.length>1 ? "x" : "") + finalSquare;
 		else
 			notation = move.appear[0].p.toUpperCase() + finalSquare;
-		if (move.vanish.length > 1 && move.appear[0].p != VariantRules.KING)
+		if (move.vanish.length > 1 && move.appear[0].p != V.KING)
 			notation += "X"; //capture mark (not describing what is captured...)
 		return notation;
 	}
diff --git a/public/javascripts/variants/Wildebeest.js b/public/javascripts/variants/Wildebeest.js
index 5bfa87b0..bb478cc2 100644
--- a/public/javascripts/variants/Wildebeest.js
+++ b/public/javascripts/variants/Wildebeest.js
@@ -2,11 +2,10 @@ class WildebeestRules extends ChessRules
 {
 	static getPpath(b)
 	{
-		const V = VariantRules;
 		return ([V.CAMEL,V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
 	}
 
-	static get size() { return [10,11]; }
+	static get size() { return {x:10,y:11}; }
 
 	static get CAMEL() { return 'c'; }
 	static get WILDEBEEST() { return 'w'; }
@@ -22,7 +21,7 @@ class WildebeestRules extends ChessRules
 	getEpSquare(move)
 	{
 		const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
-		if (this.getPiece(sx,sy) == VariantRules.PAWN && Math.abs(sx - ex) >= 2)
+		if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) >= 2)
 		{
 			const step = (ex-sx) / Math.abs(ex-sx);
 			let res = [{
@@ -45,9 +44,9 @@ class WildebeestRules extends ChessRules
 	{
 		switch (this.getPiece(x,y))
 		{
-			case VariantRules.CAMEL:
+			case V.CAMEL:
 				return this.getPotentialCamelMoves([x,y]);
-			case VariantRules.WILDEBEEST:
+			case V.WILDEBEEST:
 				return this.getPotentialWildebeestMoves([x,y]);
 			default:
 				return super.getPotentialMovesFrom([x,y])
@@ -59,8 +58,7 @@ class WildebeestRules extends ChessRules
 	{
 		const color = this.turn;
 		let moves = [];
-		const V = VariantRules;
-		const [sizeX,sizeY] = VariantRules.size;
+		const [sizeX,sizeY] = [V.size.x,V.size.y];
 		const shift = (color == "w" ? -1 : 1);
 		const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
 		const lastRank = (color == "w" ? 0 : sizeX-1);
@@ -147,13 +145,11 @@ class WildebeestRules extends ChessRules
 
 	getPotentialCamelMoves(sq)
 	{
-		return this.getSlideNJumpMoves(
-			sq, VariantRules.steps[VariantRules.CAMEL], "oneStep");
+		return this.getSlideNJumpMoves(sq, V.steps[V.CAMEL], "oneStep");
 	}
 
 	getPotentialWildebeestMoves(sq)
 	{
-		const V = VariantRules;
 		return this.getSlideNJumpMoves(
 			sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
 	}
@@ -168,12 +164,11 @@ class WildebeestRules extends ChessRules
 	isAttackedByCamel(sq, colors)
 	{
 		return this.isAttackedBySlideNJump(sq, colors,
-			VariantRules.CAMEL, VariantRules.steps[VariantRules.CAMEL], "oneStep");
+			V.CAMEL, V.steps[V.CAMEL], "oneStep");
 	}
 
 	isAttackedByWildebeest(sq, colors)
 	{
-		const V = VariantRules;
 		return this.isAttackedBySlideNJump(sq, colors, V.WILDEBEEST,
 			V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
 	}
diff --git a/public/javascripts/variants/Zen.js b/public/javascripts/variants/Zen.js
index f57ab3c5..64587d20 100644
--- a/public/javascripts/variants/Zen.js
+++ b/public/javascripts/variants/Zen.js
@@ -11,15 +11,13 @@ class ZenRules extends ChessRules
 	{
 		const color = this.getColor(x,y);
 		let moves = [];
-		const [sizeX,sizeY] = VariantRules.size;
 		outerLoop:
 		for (let loop=0; loop<steps.length; loop++)
 		{
 			const 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)
+			while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
 			{
 				moves.push(this.getBasicMove([x,y], [i,j]));
 				if (!!oneStep)
@@ -38,13 +36,11 @@ class ZenRules extends ChessRules
 	{
 		const color = this.getColor(x,y);
 		var moves = [];
-		const V = VariantRules;
 		const steps = asA != V.PAWN
 			? (asA==V.QUEEN ? V.steps[V.ROOK].concat(V.steps[V.BISHOP]) : V.steps[asA])
 			: color=='w' ? [[-1,-1],[-1,1]] : [[1,-1],[1,1]];
 		const oneStep = (asA==V.KNIGHT || asA==V.PAWN); //we don't capture king
-		const [sizeX,sizeY] = V.size;
-		const lastRank = (color == 'w' ? 0 : sizeY-1);
+		const lastRank = (color == 'w' ? 0 : V.size.x-1);
 		const promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
 		outerLoop:
 		for (let loop=0; loop<steps.length; loop++)
@@ -52,15 +48,15 @@ class ZenRules extends ChessRules
 			const 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] == V.EMPTY)
+			while (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
 			{
 				if (oneStep)
 					continue outerLoop;
 				i += step[0];
 				j += step[1];
 			}
-			if (i>=0 && i<sizeX && j>=0 && j<sizeY &&
-				this.getColor(i,j) == this.getOppCol(color) && this.getPiece(i,j) == asA)
+			if (V.OnBoard(i,j) && this.getColor(i,j) == this.getOppCol(color)
+				&& this.getPiece(i,j) == asA)
 			{
 				// eat!
 				if (this.getPiece(x,y) == V.PAWN && i == lastRank)
@@ -85,11 +81,11 @@ class ZenRules extends ChessRules
 	{
 		let moves = [];
 
-		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.PAWN));
-		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.ROOK));
-		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.KNIGHT));
-		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.BISHOP));
-		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, VariantRules.QUEEN));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.PAWN));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.ROOK));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.KNIGHT));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.BISHOP));
+		Array.prototype.push.apply(moves, this.findCaptures_aux(sq, V.QUEEN));
 
 		return moves;
 	}
@@ -97,13 +93,12 @@ class ZenRules extends ChessRules
 	getPotentialPawnMoves([x,y])
 	{
 		const color = this.getColor(x,y);
-		var moves = [];
-		var V = VariantRules;
-		let [sizeX,sizeY] = VariantRules.size;
-		let shift = (color == 'w' ? -1 : 1);
-		let startRank = (color == 'w' ? sizeY-2 : 1);
-		let firstRank = (color == 'w' ? sizeY-1 : 0);
-		let lastRank = (color == "w" ? 0 : sizeY-1);
+		let moves = [];
+		const [sizeX,sizeY] = [V.size.x,V.size.y];
+		const shift = (color == 'w' ? -1 : 1);
+		const startRank = (color == 'w' ? sizeY-2 : 1);
+		const firstRank = (color == 'w' ? sizeY-1 : 0);
+		const lastRank = (color == "w" ? 0 : sizeY-1);
 
 		if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
 		{
@@ -140,31 +135,27 @@ class ZenRules extends ChessRules
 
 	getPotentialRookMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(
-			sq, VariantRules.steps[VariantRules.ROOK]);
+		let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.ROOK]);
 		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
 	getPotentialKnightMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(
-			sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+		let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep");
 		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
 	getPotentialBishopMoves(sq)
 	{
-		let noCaptures = this.getSlideNJumpMoves(
-			sq, VariantRules.steps[VariantRules.BISHOP]);
+		let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]);
 		let captures = this.findCaptures(sq);
 		return noCaptures.concat(captures);
 	}
 
 	getPotentialQueenMoves(sq)
 	{
-		const V = VariantRules;
 		let noCaptures = this.getSlideNJumpMoves(
 			sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
 		let captures = this.findCaptures(sq);
@@ -173,7 +164,6 @@ class ZenRules extends ChessRules
 
 	getPotentialKingMoves(sq)
 	{
-		const V = VariantRules;
 		// Initialize with normal moves
 		let noCaptures = this.getSlideNJumpMoves(sq,
 			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
@@ -195,15 +185,15 @@ class ZenRules extends ChessRules
 
 		// Translate initial square (because pieces may fly unusually in this variant!)
 		const initialSquare =
-			String.fromCharCode(97 + move.start.y) + (VariantRules.size[0]-move.start.x);
+			String.fromCharCode(97 + move.start.y) + (V.size.x-move.start.x);
 
 		// Translate final square
 		const finalSquare =
-			String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x);
+			String.fromCharCode(97 + move.end.y) + (V.size.x-move.end.x);
 
 		let notation = "";
 		const piece = this.getPiece(move.start.x, move.start.y);
-		if (piece == VariantRules.PAWN)
+		if (piece == V.PAWN)
 		{
 			// pawn move (TODO: enPassant indication)
 			if (move.vanish.length > 1)
diff --git a/views/variant.pug b/views/variant.pug
index 7de86ca3..a44a3741 100644
--- a/views/variant.pug
+++ b/views/variant.pug
@@ -23,6 +23,7 @@ block javascripts
 	script(src="/javascripts/variants/" + variant + ".js")
 	script.
 		const VariantRules = #{variant}Rules;
+		const V = VariantRules; //because this variable is often used
 		const variant = "#{variant}";
 	script(src="/javascripts/components/rules.js")
 	script(src="/javascripts/components/game.js")