From 32cfcea44bf00b0c6c4d172cca715823076ff490 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 11 Dec 2018 19:09:26 +0100
Subject: [PATCH] Draft of Ultima chess rules; almost OK HalfChess

---
 TODO                                  |  3 +
 public/javascripts/base_rules.js      |  9 +--
 public/javascripts/components/game.js |  1 +
 public/javascripts/variants/Half.js   | 88 +++++++++++++++++++++++++++
 public/javascripts/variants/Loser.js  |  1 -
 public/javascripts/variants/Ultima.js |  8 +++
 public/stylesheets/variant.sass       |  4 ++
 variants.js                           |  2 +
 views/rules/Half.pug                  | 24 ++++++++
 views/rules/Ultima.pug                | 34 +++++++++++
 views/rules/Zen.pug                   |  1 -
 11 files changed, 169 insertions(+), 6 deletions(-)
 create mode 100644 TODO
 create mode 100644 public/javascripts/variants/Half.js
 create mode 100644 public/javascripts/variants/Ultima.js
 create mode 100644 views/rules/Half.pug
 create mode 100644 views/rules/Ultima.pug

diff --git a/TODO b/TODO
new file mode 100644
index 00000000..f8465c6c
--- /dev/null
+++ b/TODO
@@ -0,0 +1,3 @@
+Full detection of repeated positions (including turn)
+Debug HalfChess
+Implement UltimaChess
diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index a8f24893..750cd2d4 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -752,6 +752,7 @@ class ChessRules
 	// END OF GAME
 
 	// Basic check for 3 repetitions (in the last moves only)
+	// TODO: extend to usual 3-repetition recognition (storing FEN with move?)
 	checkRepetition()
 	{
 		if (this.moves.length >= 8)
@@ -988,9 +989,9 @@ class ChessRules
 	// Setup the initial random (assymetric) position
 	static GenRandInitFen()
 	{
-		let pieces = [new Array(8), new Array(8)];
+		let pieces = { "w": new Array(8), "b": new Array(8) };
 		// Shuffle pieces on first and last rank
-		for (let c = 0; c <= 1; c++)
+		for (let c of ["w","b"])
 		{
 			let positions = _.range(8);
 
@@ -1032,9 +1033,9 @@ class ChessRules
 			pieces[c][knight2Pos] = 'n';
 			pieces[c][rook2Pos] = 'r';
 		}
-		let fen = pieces[0].join("") +
+		let fen = pieces["b"].join("") +
 			"/pppppppp/8/8/8/8/PPPPPPPP/" +
-			pieces[1].join("").toUpperCase() +
+			pieces["w"].join("").toUpperCase() +
 			" 1111"; //add flags
 		return fen;
 	}
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 208e0482..d76ab011 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -25,6 +25,7 @@ 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
new file mode 100644
index 00000000..2f1174ed
--- /dev/null
+++ b/public/javascripts/variants/Half.js
@@ -0,0 +1,88 @@
+class HalfRules extends ChessRules
+{
+	// Standard rules on a 4x8 board with no pawns
+
+	initVariables(fen) { } //nothing to do
+
+	setFlags(fen)
+	{
+		// No castling, hence no flags; but flags defined for compatibility
+		this.castleFlags = { "w":[false,false], "b":[false,false] };
+	}
+
+	static get size() { return [8,4]; }
+
+	getPotentialKingMoves(sq)
+	{
+		const V = VariantRules;
+		// No castling
+		return this.getSlideNJumpMoves(sq,
+			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+	}
+
+	isAttacked(sq, colors)
+	{
+		return (this.isAttackedByRook(sq, colors)
+			|| this.isAttackedByKnight(sq, colors)
+			|| this.isAttackedByBishop(sq, colors)
+			|| this.isAttackedByQueen(sq, colors)
+			|| this.isAttackedByKing(sq, colors));
+	}
+
+	// Unused:
+	updateVariables(move) { }
+	unupdateVariables(move) { }
+
+	static get SEARCH_DEPTH() { return 4; }
+
+	static GenRandInitFen()
+	{
+		let minorPieces = { "w": new Array(4), "b": new Array(4) };
+		let majorPieces = { "w": new Array(4), "b": new Array(4) };
+		for (let c of ["w","b"])
+		{
+			// Minor pieces first (on 2nd rank)
+			let positions = _.range(4);
+
+			// Get random squares for bishops
+			let randIndex = 2 * _.random(1);
+			let bishop1Pos = positions[randIndex];
+			let randIndex_tmp = 2 * _.random(1) + 1;
+			let bishop2Pos = positions[randIndex_tmp];
+			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
+			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+
+			// Get random squares for knights
+			randIndex = _.random(1);
+			let knight1Pos = positions[randIndex];
+			positions.splice(randIndex, 1);
+			let knight2Pos = positions[0];
+
+			minorPieces[c][bishop1Pos] = 'b';
+			minorPieces[c][bishop2Pos] = 'b';
+			minorPieces[c][knight1Pos] = 'n';
+			minorPieces[c][knight2Pos] = 'n';
+
+			// Major pieces then (on 1st rank)
+			positions = _.range(4);
+
+			// Get random square for queen
+			randIndex = _.random(3);
+			let queenPos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			// Rooks and king positions:
+			let rook1Pos = positions[0];
+			let kingPos = positions[1];
+			let rook2Pos = positions[2];
+
+			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?!
+	}
+}
diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Loser.js
index 9ae2e7fc..da4999fe 100644
--- a/public/javascripts/variants/Loser.js
+++ b/public/javascripts/variants/Loser.js
@@ -113,7 +113,6 @@ class LoserRules extends ChessRules
 	// Unused:
 	updateVariables(move) { }
 	unupdateVariables(move) { }
-	parseFlags(flags) { }
 
 	getFlagsFen()
 	{
diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js
new file mode 100644
index 00000000..812d440b
--- /dev/null
+++ b/public/javascripts/variants/Ultima.js
@@ -0,0 +1,8 @@
+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?")
+
+	// TODO: Keep usual pieces names here (but comment with Ultima pieces names)
+	// Just change moving + capturing modes.
+}
diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass
index 3eec2900..8839c342 100644
--- a/public/stylesheets/variant.sass
+++ b/public/stylesheets/variant.sass
@@ -81,6 +81,10 @@ 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/variants.js b/variants.js
index 8a00fe00..5623334e 100644
--- a/variants.js
+++ b/variants.js
@@ -12,4 +12,6 @@ module.exports = [
 	{ "name": "Crazyhouse", "description": "Captures reborn" },
 	{ "name": "Switching", "description": "Exchange pieces positions" },
 	{ "name": "Extinction", "description": "Capture all of a kind" },
+	{ "name": "Ultima", "description": "Non-standard captures" },
+	{ "name": "Half", "description": "Small board" },
 ];
diff --git a/views/rules/Half.pug b/views/rules/Half.pug
new file mode 100644
index 00000000..605eda4b
--- /dev/null
+++ b/views/rules/Half.pug
@@ -0,0 +1,24 @@
+p.boxed
+	| 8x4 board with no pawns. Orthodox rules.
+
+figure.diagram-container
+	.diagram
+		| fen:rkqr/nbbn/4/4/4/4/NBBN/RKQR:
+	figcaption Initial position (non-random)
+
+h3 Specifications
+
+ul
+	li Chessboard: 8x4 (see diagram).
+	li Material: no pawns.
+	li Non-capturing moves: standard.
+	li Special moves: none.
+	li Captures: standard.
+	li End of game: standard.
+
+h3 Credits
+
+p
+	| This variant is shortly described on 
+	a(href="https://www.chessvariants.com/small.dir/halfchess.html") chessvariants.com
+	| .
diff --git a/views/rules/Ultima.pug b/views/rules/Ultima.pug
new file mode 100644
index 00000000..77bfd093
--- /dev/null
+++ b/views/rules/Ultima.pug
@@ -0,0 +1,34 @@
+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 :)
+
+h3 Specifications
+
+ul
+	li Chessboard: standard.
+	li Material: "standard".
+	li Non-capturing moves: often like queen.
+	li Special moves: none.
+	li Captures: very special.
+	li End of game: standard; see below.
+
+h3 Non-capturing moves
+
+// TODO: short paragraph, only the king moves like an orthodox king
+
+h3 Capturing moves
+
+// TODO...
+
+h3 End of the game
+
+// TODO: show the situation from Wikipedia page
+
+h3 Credits
+
+p.
+	A good starting point is the 
+	#[a(href="https://en.wikipedia.org/wiki/Baroque_chess") Wikipedia page], 
+	which also gives pointers to other interesting pages (including chessvariants.com,
+	as usual).
diff --git a/views/rules/Zen.pug b/views/rules/Zen.pug
index bc5f9f58..afa6502f 100644
--- a/views/rules/Zen.pug
+++ b/views/rules/Zen.pug
@@ -43,4 +43,3 @@ p.
 	Very few resources about this variation:
 	#[a(href="http://play.chessvariants.org/erf/ZenChess.html") this webpage] 
 	and #[a(href="http://www.pathguy.com/chess/ZenChess.htm") this one].
-	Ed Friedlander developed the Zen Chess applet from the link above.
-- 
2.44.0