From: Benjamin Auder <benjamin.auder@somewhere>
Date: Wed, 12 Dec 2018 02:02:19 +0000 (+0100)
Subject: Fix HalfChess, more random Loser initial position, continue draft of Ultima
X-Git-Url: https://git.auder.net/variants/current/css/doc/vendor/common.css?a=commitdiff_plain;h=2eef6db6cdce30fe785e601b88858c7fc743eee8;p=vchess.git

Fix HalfChess, more random Loser initial position, continue draft of Ultima
---

diff --git a/public/images/pieces/Ultima/bm.svg b/public/images/pieces/Ultima/bm.svg
new file mode 100644
index 00000000..fdc0ee59
--- /dev/null
+++ b/public/images/pieces/Ultima/bm.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="100%"
+   width="100%"
+   version="1.1"
+   viewBox="0 0 2048 2048"
+   id="svg44"
+   sodipodi:docname="bu.svg"
+   inkscape:version="0.92.2 2405546, 2018-03-11">
+  <metadata
+     id="metadata50">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs48" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="960"
+     inkscape:window-height="1060"
+     id="namedview46"
+     showgrid="false"
+     inkscape:zoom="0.11523438"
+     inkscape:cx="1041.3559"
+     inkscape:cy="1024"
+     inkscape:window-x="0"
+     inkscape:window-y="20"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg44" />
+  <path
+     style="color:#000000;display:block;fill:#000000;fill-rule:nonzero"
+     d="m 1161,1706 h 170 v 137 h 274 V 1468 L 1383,1297 V 819 L 1553,649 V 444 h 153 V 205 H 341 v 239 h 153 v 205 l 171,170 v 478 l -222,171 v 375 h 273 v -137 h 171 v 137 h 274 z M 564,460 V 358 h 920 v 102 z m 460,1092 H 512 v -46 l 73,-55 h 879 l 71,55 v 46 z m 0,-169 H 674 l 60,-47 v -57 h 580 v 57 l 60,47 z m 0,-546 H 734 v -46 l -60,-58 h 700 l -60,58 v 46 z m 0,-172 H 610 l -46,-43 v -58 h 920 v 58 l -46,43 z"
+     display="block"
+     id="path30"
+     inkscape:connector-curvature="0" />
+  <g
+     id="g42"
+     transform="matrix(1,0,0,-1,0,2048)"
+     style="fill:#ffffff;fill-rule:nonzero">
+    <path
+       style="color:#000000;display:block"
+       d="m 564,1588 v 102 h 920 v -102 z"
+       display="block"
+       id="path32"
+       inkscape:connector-curvature="0" />
+    <path
+       style="color:#000000;display:block"
+       d="M 1024,496 H 512 v 46 l 73,55 h 879 l 71,-55 v -46 z"
+       display="block"
+       id="path34"
+       inkscape:connector-curvature="0" />
+    <path
+       style="color:#000000;display:block"
+       d="M 1024,665 H 674 l 60,47 v 57 h 580 v -57 l 60,-47 z"
+       display="block"
+       id="path36"
+       inkscape:connector-curvature="0" />
+    <path
+       style="color:#000000;display:block"
+       d="M 1024,1211 H 734 v 46 l -60,58 h 700 l -60,-58 v -46 z"
+       display="block"
+       id="path38"
+       inkscape:connector-curvature="0" />
+    <path
+       style="color:#000000;display:block"
+       d="M 1024,1383 H 610 l -46,43 v 58 h 920 v -58 l -46,-43 z"
+       display="block"
+       id="path40"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/public/images/pieces/Ultima/wm.svg b/public/images/pieces/Ultima/wm.svg
new file mode 100644
index 00000000..bf9f16ad
--- /dev/null
+++ b/public/images/pieces/Ultima/wm.svg
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="100%"
+   width="100%"
+   version="1.1"
+   viewBox="0 0 2048 2048"
+   id="svg70"
+   sodipodi:docname="wu.svg"
+   inkscape:version="0.92.2 2405546, 2018-03-11">
+  <metadata
+     id="metadata76">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs74" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="960"
+     inkscape:window-height="1060"
+     id="namedview72"
+     showgrid="false"
+     inkscape:zoom="0.11523438"
+     inkscape:cx="1041.3559"
+     inkscape:cy="1024"
+     inkscape:window-x="0"
+     inkscape:window-y="20"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg70" />
+  <path
+     style="color:#000000;display:block;fill:#000000;fill-rule:nonzero"
+     d="m 1161,1706 h 170 v 137 h 274 V 1468 L 1383,1297 V 819 L 1553,649 V 444 h 153 V 205 H 341 v 239 h 153 v 205 l 171,170 v 478 l -222,171 v 375 h 273 v -137 h 171 v 137 h 274 z M 1639,376 H 409 V 273 H 1639 Z M 1484,580 H 564 V 444 h 920 z m -170,717 H 734 V 819 h 580 z m 222,239 v 239 h -137 v -137 h -308 v 137 H 956 V 1638 H 649 v 137 H 512 V 1536 Z M 1459,649 1356,751 H 693 L 588,649 Z m -110,716 127,103 H 572 l 128,-103 z"
+     display="block"
+     id="path54"
+     inkscape:connector-curvature="0" />
+  <g
+     id="g64"
+     transform="matrix(1,0,0,-1,0,2048)"
+     style="fill:#ffffff;fill-rule:nonzero">
+    <path
+       style="color:#000000;display:block"
+       d="M 1639,1672 H 409 v 103 h 1230 z"
+       display="block"
+       id="path56"
+       inkscape:connector-curvature="0" />
+    <path
+       style="color:#000000;display:block"
+       d="M 1484,1468 H 564 v 136 h 920 z"
+       display="block"
+       id="path58"
+       inkscape:connector-curvature="0" />
+    <path
+       style="color:#000000;display:block"
+       d="M 1314,751 H 734 v 478 h 580 z"
+       display="block"
+       id="path60"
+       inkscape:connector-curvature="0" />
+    <path
+       style="color:#000000;display:block"
+       d="M 1536,512 V 273 H 1399 V 410 H 1091 V 273 H 956 V 410 H 649 V 273 H 512 v 239 z"
+       display="block"
+       id="path62"
+       inkscape:connector-curvature="0" />
+  </g>
+  <path
+     style="color:#000000;display:block;fill:#ffffff;fill-rule:nonzero"
+     d="M 1459,649 1356,751 H 693 L 588,649 Z"
+     display="block"
+     id="path66"
+     inkscape:connector-curvature="0" />
+  <path
+     style="color:#000000;display:block;fill:#ffffff;fill-rule:nonzero"
+     d="m 1349,1365 127,103 H 572 l 128,-103 z"
+     display="block"
+     id="path68"
+     inkscape:connector-curvature="0" />
+</svg>
diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 750cd2d4..8de0176c 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -1033,11 +1033,10 @@ class ChessRules
 			pieces[c][knight2Pos] = 'n';
 			pieces[c][rook2Pos] = 'r';
 		}
-		let fen = pieces["b"].join("") +
+		return pieces["b"].join("") +
 			"/pppppppp/8/8/8/8/PPPPPPPP/" +
 			pieces["w"].join("").toUpperCase() +
 			" 1111"; //add flags
-		return fen;
 	}
 
 	// Return current fen according to pieces+colors state
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index d76ab011..208e0482 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -25,7 +25,6 @@ Vue.component('my-game', {
 	},
 	render(h) {
 		const [sizeX,sizeY] = VariantRules.size;
-		console.log(sizeX + " " + sizeY);
 		const smallScreen = (screen.width <= 420);
 		// Precompute hints squares to facilitate rendering
 		let hintSquares = doubleArray(sizeX, sizeY, false);
diff --git a/public/javascripts/variants/Half.js b/public/javascripts/variants/Half.js
index 2f1174ed..6c6c7ebb 100644
--- a/public/javascripts/variants/Half.js
+++ b/public/javascripts/variants/Half.js
@@ -1,8 +1,35 @@
+// Standard rules on a 4x8 board with no pawns
 class HalfRules extends ChessRules
 {
-	// Standard rules on a 4x8 board with no pawns
-
-	initVariables(fen) { } //nothing to do
+	initVariables(fen)
+	{
+		this.kingPos = {'w':[-1,-1], 'b':[-1,-1]};
+		const fenParts = fen.split(" ");
+		const position = fenParts[0].split("/");
+		for (let i=0; i<position.length; i++)
+		{
+			let k = 0;
+			for (let j=0; j<position[i].length; j++)
+			{
+				switch (position[i].charAt(j))
+				{
+					case 'k':
+						this.kingPos['b'] = [i,k];
+						break;
+					case 'K':
+						this.kingPos['w'] = [i,k];
+						break;
+					default:
+						let num = parseInt(position[i].charAt(j));
+						if (!isNaN(num))
+							k += (num-1);
+				}
+				k++;
+			}
+		}
+		// No pawns so no ep., but otherwise we must redefine play()
+		this.epSquares = [];
+	}
 
 	setFlags(fen)
 	{
@@ -10,7 +37,7 @@ class HalfRules extends ChessRules
 		this.castleFlags = { "w":[false,false], "b":[false,false] };
 	}
 
-	static get size() { return [8,4]; }
+	static get size() { return [4,8]; }
 
 	getPotentialKingMoves(sq)
 	{
@@ -29,11 +56,17 @@ class HalfRules extends ChessRules
 			|| this.isAttackedByKing(sq, colors));
 	}
 
-	// Unused:
-	updateVariables(move) { }
-	unupdateVariables(move) { }
-
-	static get SEARCH_DEPTH() { return 4; }
+	updateVariables(move)
+	{
+		// Just update king position
+		const piece = this.getPiece(move.start.x,move.start.y);
+		const c = this.getColor(move.start.x,move.start.y);
+		if (piece == VariantRules.KING)
+		{
+			this.kingPos[c][0] = move.appear[0].x;
+			this.kingPos[c][1] = move.appear[0].y;
+		}
+	}
 
 	static GenRandInitFen()
 	{
@@ -71,18 +104,29 @@ class HalfRules extends ChessRules
 			let queenPos = positions[randIndex];
 			positions.splice(randIndex, 1);
 
+			// Random square for king (no castle)
+			randIndex = _.random(2);
+			let kingPos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
 			// Rooks and king positions:
 			let rook1Pos = positions[0];
-			let kingPos = positions[1];
-			let rook2Pos = positions[2];
+			let rook2Pos = positions[1];
 
 			majorPieces[c][rook1Pos] = 'r';
 			majorPieces[c][rook2Pos] = 'r';
 			majorPieces[c][kingPos] = 'k';
 			majorPieces[c][queenPos] = 'q';
 		}
-		return majorPieces["b"].join("") + "/" + minorPieces["b"].join("") + "/4/4/4/4/" +
-			minorPieces["w"].join("").toUpperCase() + "/" +
-			majorPieces["w"].join("").toUpperCase() + " 0000"; //TODO: flags?!
+		let fen = "";
+		for (let i=0; i<4; i++)
+		{
+			fen += majorPieces["b"][i] + minorPieces["b"][i] + "4" +
+				minorPieces["w"][i].toUpperCase() + majorPieces["w"][i].toUpperCase();
+			if (i < 3)
+				fen += "/";
+		}
+		fen += " 0000"; //TODO: flags?!
+		return fen;
 	}
 }
diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Loser.js
index da4999fe..6a322b9d 100644
--- a/public/javascripts/variants/Loser.js
+++ b/public/javascripts/variants/Loser.js
@@ -142,4 +142,60 @@ class LoserRules extends ChessRules
 	{
 		return - super.evalPosition(); //better with less material
 	}
+
+	static GenRandInitFen()
+	{
+		let pieces = { "w": new Array(8), "b": new Array(8) };
+		// Shuffle pieces on first and last rank
+		for (let c of ["w","b"])
+		{
+			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);
+
+			// Random square for king (no castle)
+			randIndex = _.random(2);
+			let kingPos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			// Rooks positions are now fixed
+			let rook1Pos = positions[0];
+			let rook2Pos = positions[1];
+
+			// 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';
+		}
+		return pieces["b"].join("") +
+			"/pppppppp/8/8/8/8/PPPPPPPP/" +
+			pieces["w"].join("").toUpperCase() +
+			" 0000"; //add flags (TODO?!)
+	}
 }
diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js
index 812d440b..9212afba 100644
--- a/public/javascripts/variants/Ultima.js
+++ b/public/javascripts/variants/Ultima.js
@@ -1,8 +1,247 @@
 class UltimaRules extends ChessRules
 {
-	// TODO: think about move UI for "removing an immobilized  piece from the board"
-	// (extend game.js and feedback Rules.js with "there was a click, is it a move?")
+	static getPpath(b)
+	{
+		if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1)
+			return "Ultima/" + b;
+		return b; //usual piece
+	}
 
-	// TODO: Keep usual pieces names here (but comment with Ultima pieces names)
-	// Just change moving + capturing modes.
+	initVariables(fen)
+	{
+		this.kingPos = {'w':[-1,-1], 'b':[-1,-1]};
+		const fenParts = fen.split(" ");
+		const position = fenParts[0].split("/");
+		for (let i=0; i<position.length; i++)
+		{
+			let k = 0;
+			for (let j=0; j<position[i].length; j++)
+			{
+				switch (position[i].charAt(j))
+				{
+					case 'k':
+						this.kingPos['b'] = [i,k];
+						break;
+					case 'K':
+						this.kingPos['w'] = [i,k];
+						break;
+					default:
+						let num = parseInt(position[i].charAt(j));
+						if (!isNaN(num))
+							k += (num-1);
+				}
+				k++;
+			}
+		}
+		this.epSquares = []; //no en-passant here
+	}
+
+	setFlags(fen)
+	{
+		// TODO: for compatibility?
+		this.castleFlags = {"w":[false,false], "b":[false,false]};
+	}
+
+	static get IMMOBILIZER() { return 'm'; }
+	// Although other pieces keep their names here for coding simplicity,
+	// keep in mind that:
+	//  - a "rook" is a coordinator, capturing by coordinating with the king
+	//  - a "knight" is a long-leaper, capturing as in draughts
+	//  - a "bishop" is a chameleon, capturing as its prey
+	//  - a "queen" is a withdrawer, capturing by moving away from pieces
+
+	getPotentialMovesFrom([x,y])
+	{
+		switch (this.getPiece(x,y))
+		{
+			case VariantRules.IMMOBILIZER:
+				return this.getPotentialImmobilizerMoves([x,y]);
+			default:
+				return super.getPotentialMovesFrom([x,y]);
+		}
+		// TODO: add potential suicides as a move "taking the immobilizer"
+		// TODO: add long-leaper captures
+		// TODO: mark matching coordinator/withdrawer/chameleon moves as captures
+		// (will be a bit tedious for chameleons)
+		// --> filter directly in functions below
+	}
+
+	getSlideNJumpMoves([x,y], steps, oneStep)
+	{
+		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)
+			{
+				moves.push(this.getBasicMove([x,y], [i,j]));
+				if (oneStep !== undefined)
+					continue outerLoop;
+				i += step[0];
+				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]))
+			{
+				moves.push(this.getBasicMove([x,y], [i,j]));
+			}
+		}
+		return moves;
+	}
+
+	getPotentialPawnMoves([x,y])
+	{
+		return super.getPotentialRookMoves([x,y]);
+	}
+
+	getPotentialRookMoves(sq)
+	{
+		return super.getPotentialQueenMoves(sq);
+	}
+
+	getPotentialKnightMoves(sq)
+	{
+		return super.getPotentialQueenMoves(sq);
+	}
+
+	getPotentialBishopMoves(sq)
+	{
+		return super.getPotentialQueenMoves(sq);
+	}
+
+	getPotentialQueenMoves(sq)
+	{
+		return super.getPotentialQueenMoves(sq);
+	}
+
+	getPotentialKingMoves(sq)
+	{
+		const V = VariantRules;
+		return this.getSlideNJumpMoves(sq,
+			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+	}
+
+	// isAttacked() is OK because the immobilizer doesn't take
+
+	isAttackedByPawn([x,y], colors)
+	{
+		// Square (x,y) must be surrounded by two enemy pieces,
+		// and one of them at least should be a pawn
+		return false;
+	}
+
+	isAttackedByRook(sq, colors)
+	{
+		// Enemy king must be on same file and a rook on same row (or reverse)
+	}
+
+	isAttackedByKnight(sq, colors)
+	{
+		// Square (x,y) must be on same line as a knight,
+		// and there must be empty square(s) behind.
+	}
+
+	isAttackedByBishop(sq, colors)
+	{
+		// switch on piece nature on square sq: a chameleon attack as this piece
+		// ==> call the appropriate isAttackedBy... (exception of immobilizers)
+		// Other exception: a chameleon cannot attack a chameleon (seemingly...)
+	}
+
+	isAttackedByQueen(sq, colors)
+	{
+		// Square (x,y) must be adjacent to a queen, and the queen must have
+		// some free space in the opposite direction from (x,y)
+	}
+
+	updateVariables(move)
+	{
+		// Just update king position
+		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)
+		{
+			this.kingPos[c][0] = move.appear[0].x;
+			this.kingPos[c][1] = move.appear[0].y;
+		}
+	}
+
+	static get VALUES() { //TODO: totally experimental!
+		return {
+			'p': 1,
+			'r': 2,
+			'n': 5,
+			'b': 3,
+			'q': 3,
+			'm': 5,
+			'k': 1000
+		};
+	}
+
+	static get SEARCH_DEPTH() { return 2; } //TODO?
+
+	static GenRandInitFen()
+	{
+		let pieces = { "w": new Array(8), "b": new Array(8) };
+		// Shuffle pieces on first and last rank
+		for (let c of ["w","b"])
+		{
+			let positions = _.range(8);
+			// Get random squares for every piece, totally freely
+
+			let randIndex = _.random(7);
+			const bishop1Pos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			randIndex = _.random(6);
+			const bishop2Pos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			randIndex = _.random(5);
+			const knight1Pos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			randIndex = _.random(4);
+			const knight2Pos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			randIndex = _.random(3);
+			const queenPos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			randIndex = _.random(2);
+			const kingPos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			randIndex = _.random(1);
+			const rookPos = positions[randIndex];
+			positions.splice(randIndex, 1);
+			const immobilizerPos = positions[2];
+
+			pieces[c][bishop1Pos] = 'b';
+			pieces[c][bishop2Pos] = 'b';
+			pieces[c][knight1Pos] = 'n';
+			pieces[c][knight2Pos] = 'n';
+			pieces[c][queenPos] = 'q';
+			pieces[c][kingPos] = 'k';
+			pieces[c][rookPos] = 'r';
+			pieces[c][immobilizerPos] = 'm';
+		}
+		return pieces["b"].join("") +
+			"/pppppppp/8/8/8/8/PPPPPPPP/" +
+			pieces["w"].join("").toUpperCase() +
+			" 0000"; //TODO: flags?!
+	}
+
+	getFlagsFen()
+	{
+		return "0000"; //TODO: or "-" ?
+	}
 }
diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass
index 8839c342..3eec2900 100644
--- a/public/stylesheets/variant.sass
+++ b/public/stylesheets/variant.sass
@@ -81,10 +81,6 @@ div.board
   display: inline-block
   position: relative
 
-div.board4
-  width: 25%
-  padding-bottom: 25%
-
 div.board8
   width: 12.5%
   padding-bottom: 12.5%
diff --git a/views/rules/Half.pug b/views/rules/Half.pug
index 605eda4b..91b4a4e2 100644
--- a/views/rules/Half.pug
+++ b/views/rules/Half.pug
@@ -1,15 +1,15 @@
 p.boxed
-	| 8x4 board with no pawns. Orthodox rules.
+	| 4x8 board with no pawns. Orthodox rules.
 
 figure.diagram-container
 	.diagram
-		| fen:rkqr/nbbn/4/4/4/4/NBBN/RKQR:
+		| fen:rn4NR/kb4BK/qb4BQ/rn4NR:
 	figcaption Initial position (non-random)
 
 h3 Specifications
 
 ul
-	li Chessboard: 8x4 (see diagram).
+	li Chessboard: 4x8 (see diagram).
 	li Material: no pawns.
 	li Non-capturing moves: standard.
 	li Special moves: none.
diff --git a/views/rules/Ultima.pug b/views/rules/Ultima.pug
index 77bfd093..0a3cc7f0 100644
--- a/views/rules/Ultima.pug
+++ b/views/rules/Ultima.pug
@@ -1,7 +1,7 @@
 p.boxed
 	| Pieces look the same but behave very differently.
 	| They generally move like an orthodox queen,
-	| but capturing rules are complex: you need to read on :)
+	| but capturing rules are complex.
 
 h3 Specifications