From 388e4c401f05b1f6a4c54e33c9da9114969a53c0 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Mon, 24 Dec 2018 18:20:17 +0100
Subject: [PATCH] Implemented and tested DarkChess. Berolina, Upsidedown should
 be OK. Marseille: TODO

---
 db/create.sql                             |  4 ++
 public/javascripts/base_rules.js          | 14 ++--
 public/javascripts/components/game.js     |  8 +--
 public/javascripts/variants/Alice.js      |  8 +--
 public/javascripts/variants/Antiking.js   |  8 +--
 public/javascripts/variants/Atomic.js     |  4 +-
 public/javascripts/variants/Crazyhouse.js |  2 +-
 public/javascripts/variants/Dark.js       | 87 ++++++++++++++++++-----
 public/javascripts/variants/Loser.js      |  2 +-
 public/javascripts/variants/Magnetic.js   |  8 +--
 public/javascripts/variants/Marseille.js  |  3 +
 public/javascripts/variants/Switching.js  |  4 +-
 public/javascripts/variants/Ultima.js     |  4 +-
 public/javascripts/variants/Upsidedown.js | 57 +++++++++++++++
 public/stylesheets/variant.sass           |  2 +-
 15 files changed, 166 insertions(+), 49 deletions(-)
 create mode 100644 public/javascripts/variants/Marseille.js
 create mode 100644 public/javascripts/variants/Upsidedown.js

diff --git a/db/create.sql b/db/create.sql
index 033df4be..321274d0 100644
--- a/db/create.sql
+++ b/db/create.sql
@@ -18,6 +18,10 @@ insert into Variants values
 	('Crazyhouse', 'Captures reborn'),
 	('Switching', 'Exchange pieces positions'),
 	('Extinction', 'Capture all of a kind'),
+	('Marseille', 'Move twice'),
+	('Upsidedown', 'Walking on the hands'),
+	('Dark', 'In the shadow'),
+	('Berolina', 'Pawns move diagonally'),
 	('Ultima', 'Exotic captures');
 
 create table Problems (
diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index d85ff869..6cdae328 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -994,12 +994,12 @@ class ChessRules
 
 		if (V.HasFlags)
 			move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
-		this.updateVariables(move);
-		this.moves.push(move);
 		if (V.HasEnpassant)
 			this.epSquares.push( this.getEpSquare(move) );
-		this.turn = this.getOppCol(this.turn);
 		V.PlayOnBoard(this.board, move);
+		this.turn = this.getOppCol(this.turn);
+		this.moves.push(move);
+		this.updateVariables(move);
 
 		if (!!ingame)
 		{
@@ -1010,14 +1010,14 @@ class ChessRules
 
 	undo(move)
 	{
-		V.UndoOnBoard(this.board, move);
-		this.turn = this.getOppCol(this.turn);
 		if (V.HasEnpassant)
 			this.epSquares.pop();
-		this.moves.pop();
-		this.unupdateVariables(move);
 		if (V.HasFlags)
 			this.disaggregateFlags(JSON.parse(move.flags));
+		V.UndoOnBoard(this.board, move);
+		this.turn = this.getOppCol(this.turn);
+		this.moves.pop();
+		this.unupdateVariables(move);
 
 		// DEBUG:
 //		if (this.getFen() != this.states[this.states.length-1])
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index b308aa89..3474ea84 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -276,7 +276,7 @@ Vue.component('my-game', {
 			);
 			// Create board element (+ reserves if needed by variant or mode)
 			const lm = this.vr.lastMove;
-			const showLight = this.hints &&
+			const showLight = this.hints && variant!="Dark" &&
 				(!["idle","chat"].includes(this.mode) || this.cursor==this.vr.moves.length);
 			const gameDiv = h('div',
 				{
@@ -299,7 +299,7 @@ Vue.component('my-game', {
 							let cj = (this.mycolor=='w' ? j : sizeY-j-1);
 							let elems = [];
 							if (this.vr.board[ci][cj] != VariantRules.EMPTY && (variant!="Dark"
-								|| this.score!="*" || this.vr.isEnlightened(ci,cj,this.mycolor)))
+								|| this.score!="*" || this.vr.enlightened[this.mycolor][ci][cj]))
 							{
 								elems.push(
 									h(
@@ -344,7 +344,7 @@ Vue.component('my-game', {
 										'dark-square': (i+j)%2==1,
 										[this.color]: true,
 										'in-shadow': variant=="Dark" && this.score=="*"
-											&& !this.vr.isEnlightened(ci,cj,this.mycolor),
+											&& !this.vr.enlightened[this.mycolor][ci][cj],
 										'highlight': showLight && !!lm && _.isMatch(lm.end, {x:ci,y:cj}),
 										'incheck': showLight && incheckSq[ci][cj],
 									},
@@ -1022,7 +1022,7 @@ Vue.component('my-game', {
 					this.newGame("human", data.fen, data.color, data.oppid);
 					break;
 				case "newmove": //..he played!
-					this.play(data.move, "animate");
+					this.play(data.move, (variant!="Dark" ? "animate" : null));
 					break;
 				case "pong": //received if we sent a ping (game still alive on our side)
 					if (this.gameId != data.gameId)
diff --git a/public/javascripts/variants/Alice.js b/public/javascripts/variants/Alice.js
index f7d2fa13..1cbfccf3 100644
--- a/public/javascripts/variants/Alice.js
+++ b/public/javascripts/variants/Alice.js
@@ -256,8 +256,8 @@ class AliceRules extends ChessRules
 	updateVariables(move)
 	{
 		super.updateVariables(move); //standard king
-		const piece = this.getPiece(move.start.x,move.start.y);
-		const c = this.getColor(move.start.x,move.start.y);
+		const piece = move.vanish[0].p;
+		const c = move.vanish[0].c;
 		// "l" = Alice king
 		if (piece == "l")
 		{
@@ -270,8 +270,8 @@ class AliceRules extends ChessRules
 	unupdateVariables(move)
 	{
 		super.unupdateVariables(move);
-		const c = this.getColor(move.start.x,move.start.y);
-		if (this.getPiece(move.start.x,move.start.y) == "l")
+		const c = move.vanish[0].c;
+		if (move.vanish[0].p == "l")
 			this.kingPos[c] = [move.start.x, move.start.y];
 	}
 
diff --git a/public/javascripts/variants/Antiking.js b/public/javascripts/variants/Antiking.js
index e4d402ce..25a9dfcd 100644
--- a/public/javascripts/variants/Antiking.js
+++ b/public/javascripts/variants/Antiking.js
@@ -113,8 +113,8 @@ class AntikingRules extends ChessRules
 	updateVariables(move)
 	{
 		super.updateVariables(move);
-		const piece = this.getPiece(move.start.x,move.start.y);
-		const c = this.getColor(move.start.x,move.start.y);
+		const piece = move.vanish[0].p;
+		const c = move.vanish[0].c;
 		// Update antiking position
 		if (piece == V.ANTIKING)
 		{
@@ -126,8 +126,8 @@ class AntikingRules extends ChessRules
 	unupdateVariables(move)
 	{
 		super.unupdateVariables(move);
-		const c = this.getColor(move.start.x,move.start.y);
-		if (this.getPiece(move.start.x,move.start.y) == V.ANTIKING)
+		const c = move.vanish[0].c;
+		if (move.vanish[0].p == 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 0275805f..666c50b2 100644
--- a/public/javascripts/variants/Atomic.js
+++ b/public/javascripts/variants/Atomic.js
@@ -58,7 +58,7 @@ class AtomicRules extends ChessRules
 	updateVariables(move)
 	{
 		super.updateVariables(move);
-		const color = this.getColor(move.start.x,move.start.y);
+		const color = move.vanish[0].c;
 		if (move.appear.length == 0) //capture
 		{
 			const firstRank = {"w": 7, "b": 0};
@@ -89,7 +89,7 @@ class AtomicRules extends ChessRules
 	unupdateVariables(move)
 	{
 		super.unupdateVariables(move);
-		const c = this.getColor(move.start.x,move.start.y);
+		const c = move.vanish[0].c;
 		const oppCol = this.getOppCol(c);
 		if ([this.kingPos[c][0],this.kingPos[oppCol][0]].some(e => { return e < 0; }))
 		{
diff --git a/public/javascripts/variants/Crazyhouse.js b/public/javascripts/variants/Crazyhouse.js
index f36bfcc4..c4780b78 100644
--- a/public/javascripts/variants/Crazyhouse.js
+++ b/public/javascripts/variants/Crazyhouse.js
@@ -212,7 +212,7 @@ class CrazyhouseRules extends ChessRules
 		super.updateVariables(move);
 		if (move.vanish.length == 2 && move.appear.length == 2)
 			return; //skip castle
-		const color = this.turn;
+		const color = move.appear[0].c;
 		if (move.vanish.length == 0)
 		{
 			this.reserve[color][move.appear[0].p]--;
diff --git a/public/javascripts/variants/Dark.js b/public/javascripts/variants/Dark.js
index 9cf50513..4031462c 100644
--- a/public/javascripts/variants/Dark.js
+++ b/public/javascripts/variants/Dark.js
@@ -1,25 +1,42 @@
-class Chess960Rules extends ChessRules
+class DarkRules extends ChessRules
 {
 	// Standard rules, in the shadow
 	setOtherVariables(fen)
 	{
 		super.setOtherVariables(fen);
-		const [sizeX,sizeY] = {V.size.x,V.size.y};
+		const [sizeX,sizeY] = [V.size.x,V.size.y];
 		this.enlightened = {
-			"w": doubleArray(sizeX,sizeY,false),
-			"b": doubleArray(sizeX,sizeY,false)
+			"w": doubleArray(sizeX,sizeY),
+			"b": doubleArray(sizeX,sizeY)
 		};
-		setup enlightened: squares reachable by each side (TODO: one side would be enough)
+		// Setup enlightened: squares reachable by each side
+		// (TODO: one side would be enough ?)
+		this.updateEnlightened();
 	}
 
-	isEnlightened(x, y, color)
+	updateEnlightened()
 	{
-		//TODO: artificlaly change turn
-	}
-
-	getAllPotentialMoves()
-	{
-		let moves = []; //TODO
+		// Initialize with pieces positions (which are seen)
+		for (let i=0; i<V.size.x; i++)
+		{
+			for (let j=0; j<V.size.y; j++)
+			{
+				this.enlightened["w"][i][j] = false;
+				this.enlightened["b"][i][j] = false;
+				if (this.board[i][j] != V.EMPTY)
+					this.enlightened[this.getColor(i,j)][i][j] = true;
+			}
+		}
+		const currentTurn = this.turn;
+		this.turn = "w";
+		const movesWhite = this.getAllValidMoves();
+		this.turn = "b";
+		const movesBlack = this.getAllValidMoves();
+		this.turn = currentTurn;
+		for (let move of movesWhite)
+			this.enlightened["w"][move.end.x][move.end.y] = true;
+		for (let move of movesBlack)
+			this.enlightened["b"][move.end.x][move.end.y] = true;
 	}
 
 	atLeastOneMove()
@@ -47,10 +64,48 @@ class Chess960Rules extends ChessRules
 		return res;
 	}
 
-	// NOTE: no (un)updateVariables() because no computer mode
-	// --> but isEnlightened() should have its variable updated
-	// --> in fact an array is enough (no need for a function)
-	// recomputed after every play/undo (although there are no undo here for now)
+	updateVariables(move)
+	{
+		// Update kings positions
+		const piece = move.vanish[0].p;
+		const c = move.vanish[0].c;
+		if (piece == V.KING && move.appear.length > 0)
+		{
+			this.kingPos[c][0] = move.appear[0].x;
+			this.kingPos[c][1] = move.appear[0].y;
+		}
+		if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
+		{
+			// We took opponent king !
+			const oppCol = this.getOppCol(c);
+			this.kingPos[oppCol] = [-1,-1];
+		}
+
+		// Update moves for both colors:
+		this.updateEnlightened();
+	}
+
+	unupdateVariables(move)
+	{
+		super.unupdateVariables(move);
+		const c = move.vanish[0].c;
+		const oppCol = this.getOppCol(c);
+		if (this.kingPos[oppCol][0] < 0)
+		{
+			// Last move took opponent's king
+			for (let psq of move.vanish)
+			{
+				if (psq.p == 'k')
+				{
+					this.kingPos[oppCol] = [psq.x, psq.y];
+					break;
+				}
+			}
+		}
+
+		// Update moves for both colors:
+		this.updateEnlightened();
+	}
 
 	checkGameEnd()
 	{
diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Loser.js
index 407c6aad..e6eb3207 100644
--- a/public/javascripts/variants/Loser.js
+++ b/public/javascripts/variants/Loser.js
@@ -97,7 +97,7 @@ class LoserRules extends ChessRules
 		return [];
 	}
 
-	// No variables update because no castling
+	// No variables update because no royal king + no castling
 	updateVariables(move) { }
 	unupdateVariables(move) { }
 
diff --git a/public/javascripts/variants/Magnetic.js b/public/javascripts/variants/Magnetic.js
index 8480d799..1ccc192e 100644
--- a/public/javascripts/variants/Magnetic.js
+++ b/public/javascripts/variants/Magnetic.js
@@ -160,10 +160,8 @@ class MagneticRules extends ChessRules
 	updateVariables(move)
 	{
 		super.updateVariables(move);
-		const c = this.getColor(move.start.x,move.start.y);
-		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) == V.KING)
+		const c = move.vanish[0].c;
+		if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
 		{
 			// We took opponent king !
 			const oppCol = this.getOppCol(c);
@@ -185,7 +183,7 @@ class MagneticRules extends ChessRules
 	unupdateVariables(move)
 	{
 		super.unupdateVariables(move);
-		const c = this.getColor(move.start.x,move.start.y);
+		const c = move.vanish[0].c;
 		const oppCol = this.getOppCol(c);
 		if (this.kingPos[oppCol][0] < 0)
 		{
diff --git a/public/javascripts/variants/Marseille.js b/public/javascripts/variants/Marseille.js
new file mode 100644
index 00000000..ab7a3afe
--- /dev/null
+++ b/public/javascripts/variants/Marseille.js
@@ -0,0 +1,3 @@
+//TODO: turn en fonction de la parité des coups...
+//adapter alphabeta (dans baserules ? --> basé sur turn OK)
+// le reste == standard
diff --git a/public/javascripts/variants/Switching.js b/public/javascripts/variants/Switching.js
index e53ab692..53b14d58 100644
--- a/public/javascripts/variants/Switching.js
+++ b/public/javascripts/variants/Switching.js
@@ -104,7 +104,7 @@ class SwitchingRules extends ChessRules
 			&& 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);
+			const color = move.vanish[0].c;
 			this.kingPos[color] = [move.appear[1].x, move.appear[1].y];
 		}
 	}
@@ -115,7 +115,7 @@ class SwitchingRules extends ChessRules
 		if (move.appear.length == 2 && move.vanish.length == 2
 			&& move.appear[1].p == V.KING)
 		{
-			const color = this.getColor(move.start.x, move.start.y);
+			const color = move.vanish[0].c;
 			this.kingPos[color] = [move.appear[0].x, move.appear[0].y];
 		}
 	}
diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js
index 04d62b7d..0b00c262 100644
--- a/public/javascripts/variants/Ultima.js
+++ b/public/javascripts/variants/Ultima.js
@@ -529,8 +529,8 @@ class UltimaRules extends ChessRules
 	updateVariables(move)
 	{
 		// 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);
+		const piece = move.vanish[0].p;
+		const c = move.vanish[0].c;
 		if (piece == V.KING && move.appear.length > 0)
 		{
 			this.kingPos[c][0] = move.appear[0].x;
diff --git a/public/javascripts/variants/Upsidedown.js b/public/javascripts/variants/Upsidedown.js
new file mode 100644
index 00000000..aa9a696d
--- /dev/null
+++ b/public/javascripts/variants/Upsidedown.js
@@ -0,0 +1,57 @@
+class UpsidedownRules extends ChessRUles
+{
+	static HasFlags() { return false; }
+
+	getPotentialKingMoves(sq)
+	{
+		// No castle
+		return this.getSlideNJumpMoves(sq,
+			V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
+	}
+
+	static GenRandInitFen()
+	{
+		let pieces = { "w": new Array(8), "b": new Array(8) };
+		for (let c of ["w","b"])
+		{
+			let positions = _.range(8);
+
+			let randIndex = 2 * _.random(3);
+			let bishop1Pos = positions[randIndex];
+			let randIndex_tmp = 2 * _.random(3) + 1;
+			let bishop2Pos = positions[randIndex_tmp];
+			positions.splice(Math.max(randIndex,randIndex_tmp), 1);
+			positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+
+			randIndex = _.random(5);
+			let knight1Pos = positions[randIndex];
+			positions.splice(randIndex, 1);
+			randIndex = _.random(4);
+			let knight2Pos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			randIndex = _.random(3);
+			let queenPos = positions[randIndex];
+			positions.splice(randIndex, 1);
+
+			let rook1Pos = positions[0];
+			let kingPos = positions[1];
+			let rook2Pos = positions[2];
+
+			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["w"].join("") +
+			"/PPPPPPPP/8/8/8/8/pppppppp/" +
+			pieces["b"].join("").toUpperCase() +
+			" w 1111 -"; //add turn + flags + enpassant
+	}
+}
+
+const VariantRules = UpsidedownRules;
diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass
index 0ebad8c3..c3df08c4 100644
--- a/public/stylesheets/variant.sass
+++ b/public/stylesheets/variant.sass
@@ -222,7 +222,7 @@ img.ghost
   background-color: #00cc66 !important
 
 .in-shadow
-  opacity: 0.5
+  filter: brightness(50%)
 
 .incheck
   background-color: #cc3300 !important
-- 
2.44.0