From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sun, 6 Jan 2019 23:00:07 +0000 (+0100)
Subject: Draft code reorganisation (+ fix Alice rules + stateless VariantRules object)
X-Git-Url: https://git.auder.net/variants/current/css/img/pieces/doc/%3C?a=commitdiff_plain;h=b6487fb9c41705187cf97215fc9e8f86a59057c7;p=vchess.git

Draft code reorganisation (+ fix Alice rules + stateless VariantRules object)
---

diff --git a/README.md b/README.md
index b541ddc2..0b2d396b 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,8 @@ in english.
 I hope it's intuitive enough :)
 
 But, a few important points:
- - All games start with a random assymetric position!
- - Games are untimed: you decide to play fast or not...
- - Your identity (if filled) is revealed only after the game
+ - Games start with a random assymetric position!
+ - Your identity is revealed only after a game
 
 ## Resources
 
diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 8f875e4f..e405cbaa 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -66,10 +66,13 @@ class ChessRules
 		// 2) Check turn
 		if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn))
 			return false;
-		// 3) Check flags
+		// 3) Check moves count
+		if (!fenParsed.movesCount || !(parseInt(fenParsed.movesCount) >= 0))
+			return false;
+		// 4) Check flags
 		if (V.HasFlags && (!fenParsed.flags || !V.IsGoodFlags(fenParsed.flags)))
 			return false;
-		// 4) Check enpassant
+		// 5) Check enpassant
 		if (V.HasEnpassant &&
 			(!fenParsed.enpassant || !V.IsGoodEnpassant(fenParsed.enpassant)))
 		{
@@ -288,8 +291,9 @@ class ChessRules
 		{
 			position: fenParts[0],
 			turn: fenParts[1],
+			movesCount: fenParts[2],
 		};
-		let nextIdx = 2;
+		let nextIdx = 3;
 		if (V.HasFlags)
 			Object.assign(res, {flags: fenParts[nextIdx++]});
 		if (V.HasEnpassant)
@@ -300,7 +304,8 @@ class ChessRules
 	// Return current fen (game state)
 	getFen()
 	{
-		return this.getBaseFen() + " " + this.getTurnFen() +
+		return this.getBaseFen() + " " +
+			this.getTurnFen() + " " + this.movesCount +
 			(V.HasFlags ? (" " + this.getFlagsFen()) : "") +
 			(V.HasEnpassant ? (" " + this.getEnpassantFen()) : "");
 	}
@@ -401,12 +406,12 @@ class ChessRules
 	// INITIALIZATION
 
 	// Fen string fully describes the game state
-	constructor(fen, moves)
+	constructor(fen)
 	{
-		this.moves = moves;
 		const fenParsed = V.ParseFen(fen);
 		this.board = V.GetBoard(fenParsed.position);
 		this.turn = fenParsed.turn[0]; //[0] to work with MarseilleRules
+		this.movesCount = parseInt(fenParsed.movesCount);
 		this.setOtherVariables(fen);
 	}
 
@@ -498,12 +503,6 @@ class ChessRules
 		return (color=="w" ? "b" : "w");
 	}
 
-	get lastMove()
-	{
-		const L = this.moves.length;
-		return (L>0 ? this.moves[L-1] : null);
-	}
-
 	// Pieces codes (for a clearer code)
 	static get PAWN() { return 'p'; }
 	static get ROOK() { return 'r'; }
@@ -1044,7 +1043,7 @@ class ChessRules
 //		if (!ingame) this.states.push(this.getFen());
 
 		if (!!ingame)
-			move.notation = [this.getNotation(move), this.getLongNotation(move)];
+			move.notation = this.getNotation(move);
 
 		if (V.HasFlags)
 			move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
@@ -1052,7 +1051,7 @@ class ChessRules
 			this.epSquares.push( this.getEpSquare(move) );
 		V.PlayOnBoard(this.board, move);
 		this.turn = this.getOppCol(this.turn);
-		this.moves.push(move);
+		this.movesCount++;
 		this.updateVariables(move);
 
 		if (!!ingame)
@@ -1070,7 +1069,7 @@ class ChessRules
 			this.disaggregateFlags(JSON.parse(move.flags));
 		V.UndoOnBoard(this.board, move);
 		this.turn = this.getOppCol(this.turn);
-		this.moves.pop();
+		this.movesCount--;
 		this.unupdateVariables(move);
 
 		// DEBUG:
@@ -1082,32 +1081,9 @@ class ChessRules
 	///////////////
 	// END OF GAME
 
-	// Check for 3 repetitions (position + flags + turn)
-	checkRepetition()
-	{
-		if (!this.hashStates)
-			this.hashStates = {};
-		const startIndex =
-			Object.values(this.hashStates).reduce((a,b) => { return a+b; }, 0)
-		// Update this.hashStates with last move (or all moves if continuation)
-		// NOTE: redundant storage, but faster and moderate size
-		for (let i=startIndex; i<this.moves.length; i++)
-		{
-			const move = this.moves[i];
-			if (!this.hashStates[move.hash])
-				this.hashStates[move.hash] = 1;
-			else
-				this.hashStates[move.hash]++;
-		}
-		return Object.values(this.hashStates).some(elt => { return (elt >= 3); });
-	}
-
 	// Is game over ? And if yes, what is the score ?
 	checkGameOver()
 	{
-		if (this.checkRepetition())
-			return "1/2";
-
 		if (this.atLeastOneMove()) // game not over
 			return "*";
 
@@ -1328,6 +1304,7 @@ class ChessRules
 	/////////////////////////
 
 	// Context: just before move is played, turn hasn't changed
+	// TODO: un-ambiguous notation (switch on piece type, check directions...)
 	getNotation(move)
 	{
 		if (move.appear.length == 2 && move.appear[0].p == V.KING) //castle
@@ -1370,7 +1347,7 @@ class ChessRules
 	}
 
 	// The score is already computed when calling this function
-	getPGN(mycolor, score, fenStart, mode)
+	getPGN(moves, mycolor, score, fenStart, mode)
 	{
 		let pgn = "";
 		pgn += '[Site "vchess.club"]\n';
@@ -1386,25 +1363,15 @@ class ChessRules
 			: "analyze";
 		pgn += '[White "' + whiteName + '"]\n';
 		pgn += '[Black "' + blackName + '"]\n';
-		pgn += '[FenStart "' + fenStart + '"]\n';
-		pgn += '[Fen "' + this.getFen() + '"]\n';
+		pgn += '[Fen "' + fenStart + '"]\n';
 		pgn += '[Result "' + score + '"]\n\n';
 
-		// Standard PGN
-		for (let i=0; i<this.moves.length; i++)
-		{
-			if (i % 2 == 0)
-				pgn += ((i/2)+1) + ".";
-			pgn += this.moves[i].notation[0] + " ";
-		}
-		pgn += "\n\n";
-
-		// "Complete moves" PGN (helping in ambiguous cases)
-		for (let i=0; i<this.moves.length; i++)
+		// Print moves
+		for (let i=0; i<moves.length; i++)
 		{
 			if (i % 2 == 0)
 				pgn += ((i/2)+1) + ".";
-			pgn += this.moves[i].notation[1] + " ";
+			pgn += moves[i].notation + " ";
 		}
 
 		return pgn + "\n";
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 54ee1282..c02893b5 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -190,24 +190,6 @@ Vue.component('my-game', {
 				}
 			);
 			aboveBoardElts.push(turnIndic);
-			const settingsBtn = h(
-				'button',
-				{
-					on: { click: this.showSettings },
-					attrs: {
-						"aria-label": translations['Settings'],
-						"id": "settingsBtn",
-					},
-					'class': {
-						"tooltip": true,
-						"play": true,
-						"above-board": true,
-						"indic-right": true,
-					},
-				},
-				[h('i', { 'class': { "material-icons": true } }, "settings")]
-			);
-			aboveBoardElts.push(settingsBtn);
 			elementArray.push(
 				h('div',
 					{ "class": { "aboveboard-wrapper": true } },
@@ -618,175 +600,6 @@ Vue.component('my-game', {
 			)
 		];
 		elementArray = elementArray.concat(modalFenEdit);
-		const modalSettings = [
-			h('input',
-				{
-					attrs: { "id": "modal-settings", type: "checkbox" },
-					"class": { "modal": true },
-				}),
-			h('div',
-				{
-					attrs: { "role": "dialog", "aria-labelledby": "settingsTitle" },
-				},
-				[
-					h('div',
-						{
-							"class": { "card": true, "smallpad": true },
-						},
-						[
-							h('label',
-								{
-									attrs: { "id": "close-settings", "for": "modal-settings" },
-									"class": { "modal-close": true },
-								}
-							),
-							h('h3',
-								{
-									attrs: { "id": "settingsTitle" },
-									"class": { "section": true },
-									domProps: { innerHTML: translations["Preferences"] },
-								}
-							),
-							h('fieldset',
-							  { },
-								[
-									h('label',
-										{
-											attrs: { for: "nameSetter" },
-											domProps: { innerHTML: translations["My name is..."] },
-										},
-									),
-									h('input',
-										{
-											attrs: {
-												"id": "nameSetter",
-												type: "text",
-												value: this.myname,
-											},
-											on: { "change": this.setMyname },
-										}
-									),
-								]
-							),
-							h('fieldset',
-							  { },
-								[
-									h('label',
-										{
-											attrs: { for: "setHints" },
-											domProps: { innerHTML: translations["Show hints?"] },
-										},
-									),
-									h('input',
-										{
-											attrs: {
-												"id": "setHints",
-												type: "checkbox",
-												checked: this.hints,
-											},
-											on: { "change": this.toggleHints },
-										}
-									),
-								]
-							),
-							h('fieldset',
-								{ },
-								[
-									h('label',
-										{
-											attrs: { for: "selectColor" },
-											domProps: { innerHTML: translations["Board colors"] },
-										},
-									),
-									h("select",
-										{
-											attrs: { "id": "selectColor" },
-											on: { "change": this.setBoardColor },
-										},
-										[
-											h("option",
-												{
-													domProps: {
-														"value": "lichess",
-														innerHTML: translations["brown"]
-													},
-													attrs: { "selected": this.color=="lichess" },
-												}
-											),
-											h("option",
-												{
-													domProps: {
-														"value": "chesscom",
-														innerHTML: translations["green"]
-													},
-													attrs: { "selected": this.color=="chesscom" },
-												}
-											),
-											h("option",
-												{
-													domProps: {
-														"value": "chesstempo",
-														innerHTML: translations["blue"]
-													},
-													attrs: { "selected": this.color=="chesstempo" },
-												}
-											),
-										],
-									),
-								]
-							),
-							h('fieldset',
-								{ },
-								[
-									h('label',
-										{
-											attrs: { for: "selectSound" },
-											domProps: { innerHTML: translations["Play sounds?"] },
-										},
-									),
-									h("select",
-										{
-											attrs: { "id": "selectSound" },
-											on: { "change": this.setSound },
-										},
-										[
-											h("option",
-												{
-													domProps: {
-														"value": "0",
-														innerHTML: translations["None"]
-													},
-													attrs: { "selected": this.sound==0 },
-												}
-											),
-											h("option",
-												{
-													domProps: {
-														"value": "1",
-														innerHTML: translations["New game"]
-													},
-													attrs: { "selected": this.sound==1 },
-												}
-											),
-											h("option",
-												{
-													domProps: {
-														"value": "2",
-														innerHTML: translations["All"]
-													},
-													attrs: { "selected": this.sound==2 },
-												}
-											),
-										],
-									),
-								]
-							),
-						]
-					)
-				]
-			)
-		];
-		elementArray = elementArray.concat(modalSettings);
 		let chatEltsArray =
 		[
 			h('label',
@@ -1637,3 +1450,10 @@ Vue.component('my-game', {
 		},
 	},
 })
+
+// TODO: keep moves list here
+get lastMove()
+	{
+		const L = this.moves.length;
+		return (L>0 ? this.moves[L-1] : null);
+	}
diff --git a/public/javascripts/components/gameList.js b/public/javascripts/components/gameList.js
new file mode 100644
index 00000000..3fcb40e5
--- /dev/null
+++ b/public/javascripts/components/gameList.js
@@ -0,0 +1,4 @@
+// TODO
+//My games : (tabs)
+//mes parties en cours --> démarrer là-dessus si y en a et c'est à moi de jouer ?
+//mes parties terminées (possibilité de supprimer)
diff --git a/public/javascripts/components/problems.js b/public/javascripts/components/problems.js
index 63f06e55..4caabb55 100644
--- a/public/javascripts/components/problems.js
+++ b/public/javascripts/components/problems.js
@@ -127,3 +127,8 @@ Vue.component('my-problems', {
 		},
 	},
 })
+
+// TODO:
+// possibilité de supprimer / éditer si peer ID reconnu comme celui du probleme (champ "uploader")
+// --> côté serveur on vérifie un certain "secret"
+// --> filtre possible "mes problèmes"
diff --git a/public/javascripts/components/room.js b/public/javascripts/components/room.js
new file mode 100644
index 00000000..941434b4
--- /dev/null
+++ b/public/javascripts/components/room.js
@@ -0,0 +1,20 @@
+// TODO: main playing hall, chat + online players + current challenges + button "new game"
+/*
+input#modal-newgame.modal(type="checkbox")
+div(role="dialog" aria-labelledby="newGameTxt")
+	.card.smallpad.small-modal
+		label#close-newgame.modal-close(for="modal-newgame")
+		h3#newGameTxt= translations["New game"]
+		p= translations["Waiting for opponent..."]
+*/
+
+/*
+Players + challenges : == "room" home of variant (surligner si nouveau défi perso et pas affichage courant)
+joueurs en ligne (dte),
+Nouvelle partie + défis en temps réel + parties en cours (milieu, tabs),
+chat général (gauche, activé ou non (bool global storage)).
+(cadences base + incrément, corr == incr >= 1jour ou base >= 7j)
+--> correspondance: stocker sur serveur lastMove + peerId + color + movesCount + gameId + variant + timeleft
+quand je poste un lastMove corr, supprimer mon ancien lastMove le cas échéant (tlm l'a eu)
+fin de partie corr: garder maxi nbPlayers lastMove sur serveur, pendant 7 jours (arbitraire)
+*/
diff --git a/public/javascripts/index.js b/public/javascripts/index.js
index e460f868..64489262 100644
--- a/public/javascripts/index.js
+++ b/public/javascripts/index.js
@@ -8,6 +8,7 @@ new Vue({
 	},
 	computed: {
 		sortedCounts: function () {
+			// TODO: priorité aux parties corr où c'est à nous de jouer !
 			const variantsCounts = variantArray
 			.filter( v => {
 				return v.name.startsWith(this.curPrefix);
@@ -74,19 +75,10 @@ new Vue({
 			}
 			// ...ignore everything else
 		};
-		// Show welcome dialog box if "first visit"
-		const visited = getCookie("visited");
-		if (!visited || visited !== "1")
-			document.getElementById("modalB4welcome").checked = true;
-	},
-	methods: {
-		showWelcomeMsg: function() {
-			document.getElementById("modalB4welcome").checked = false;
-			document.getElementById("modalWelcome").checked = true;
-		},
-		markAsVisited: function() {
-			setCookie('visited', '1');
-			document.getElementById('modalWelcome').checked = false;
-		},
 	},
 });
+
+// TODO:
+// si dernier lastMove sur serveur n'est pas le mien et nextColor == moi, alors background orange
+// ==> background orange si à moi de jouer par corr (sur main index)
+// (fonction "getNextCol()" dans base_rules.js ?)
diff --git a/public/javascripts/layout.js b/public/javascripts/layout.js
new file mode 100644
index 00000000..89f1f476
--- /dev/null
+++ b/public/javascripts/layout.js
@@ -0,0 +1,2 @@
+// TODO:
+//à l'arrivée sur le site : set peerID (un identifiant unique en tout cas...) si pas trouvé
diff --git a/public/javascripts/settings.js b/public/javascripts/settings.js
new file mode 100644
index 00000000..c9adbf16
--- /dev/null
+++ b/public/javascripts/settings.js
@@ -0,0 +1,3 @@
+// TODO:
+//à chaque onChange, envoyer matching event settings update
+//(par exemple si mise à jour du nom, juste envoyer cet update aux autres connectés ...etc)
diff --git a/public/javascripts/utils/printDiagram.js b/public/javascripts/utils/printDiagram.js
index 47274303..9f7a0203 100644
--- a/public/javascripts/utils/printDiagram.js
+++ b/public/javascripts/utils/printDiagram.js
@@ -1,88 +1,95 @@
-// Assuming V(ariantRules) class is loaded.
-// args: object with position (mandatory), orientation, marks (optional)
-function getDiagram(args)
+// Turn (human) marks into coordinates
+function getMarkArray(marks)
 {
-	const [sizeX,sizeY] = [V.size.x,V.size.y];
-	// Obtain array of pieces images names
-	const board = VariantRules.GetBoard(args.position);
-	const orientation = args.orientation || "w";
-	let markArray = [];
-	if (!!args.marks && args.marks != "-")
+	if (!marks || marks == "-")
+		return [];
+	let markArray = doubleArray(V.size.x, V.size.y, false);
+	const squares = marks.split(",");
+	for (let i=0; i<squares.length; i++)
 	{
-		// Turn (human) marks into coordinates
-		markArray = doubleArray(sizeX, sizeY, false);
-		let squares = args.marks.split(",");
-		for (let i=0; i<squares.length; i++)
-		{
-			const coords = V.SquareToCoords(squares[i]);
-			markArray[coords.x][coords.y] = true;
-		}
+		const coords = V.SquareToCoords(squares[i]);
+		markArray[coords.x][coords.y] = true;
 	}
-	let shadowArray = [];
-	if (!!args.shadow && args.shadow != "-")
+	return markArray;
+}
+
+// Turn (human) shadow indications into coordinates
+function getShadowArray(shadow)
+{
+	if (!shadow || shadow == "-")
+		return [];
+	let shadowArray = doubleArray(V.size.x, V.size.y, false);
+	const squares = shadow.split(",");
+	for (let i=0; i<squares.length; i++)
 	{
-		// Turn (human) shadow indications into coordinates
-		shadowArray = doubleArray(sizeX, sizeY, false);
-		let squares = args.shadow.split(",");
-		for (let i=0; i<squares.length; i++)
+		const rownum = V.size.x - parseInt(squares[i]);
+		if (!isNaN(rownum))
 		{
-			const rownum = V.size.x - parseInt(squares[i]);
-			if (!isNaN(rownum))
-			{
-				// Shadow a full row
-				for (let i=0; i<V.size.y; i++)
-					shadowArray[rownum][i] = true;
-				continue;
-			}
-			if (squares[i].length == 1)
-			{
-				// Shadow a full column
-				const colnum = V.ColumnToCoord(squares[i]);
-				for (let i=0; i<V.size.x; i++)
-					shadowArray[i][colnum] = true;
-				continue;
-			}
-			if (squares[i].indexOf("-") >= 0)
+			// Shadow a full row
+			for (let i=0; i<V.size.y; i++)
+				shadowArray[rownum][i] = true;
+			continue;
+		}
+		if (squares[i].length == 1)
+		{
+			// Shadow a full column
+			const colnum = V.ColumnToCoord(squares[i]);
+			for (let i=0; i<V.size.x; i++)
+				shadowArray[i][colnum] = true;
+			continue;
+		}
+		if (squares[i].indexOf("-") >= 0)
+		{
+			// Shadow a range of squares, horizontally or vertically
+			const firstLastSq = squares[i].split("-");
+			const range =
+			[
+				V.SquareToCoords(firstLastSq[0]),
+				V.SquareToCoords(firstLastSq[1])
+			];
+			const step =
+			[
+				range[1].x == range[0].x
+					? 0
+					: (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x),
+				range[1].y == range[0].y
+					? 0
+					: (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y)
+			];
+			// Convention: range always from smaller to larger number
+			for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y;
+				x += step[0], y += step[1])
 			{
-				// Shadow a range of squares, horizontally or vertically
-				const firstLastSq = squares[i].split("-");
-				const range =
-				[
-					V.SquareToCoords(firstLastSq[0]),
-					V.SquareToCoords(firstLastSq[1])
-				];
-				const step =
-				[
-					range[1].x == range[0].x
-						? 0
-						: (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x),
-					range[1].y == range[0].y
-						? 0
-						: (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y)
-				];
-				// Convention: range always from smaller to larger number
-				for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y;
-					x += step[0], y += step[1])
-				{
-					shadowArray[x][y] = true;
-				}
-				continue;
+				shadowArray[x][y] = true;
 			}
-			// Shadow just one square:
-			const coords = V.SquareToCoords(squares[i]);
-			shadowArray[coords.x][coords.y] = true;
+			continue;
 		}
+		// Shadow just one square:
+		const coords = V.SquareToCoords(squares[i]);
+		shadowArray[coords.x][coords.y] = true;
 	}
+	return shadowArray;
+}
+
+// args: object with position (mandatory), and
+// orientation, marks, shadow (optional)
+function getDiagram(args)
+{
+	// Obtain the array of pieces images names:
+	const board = VariantRules.GetBoard(args.position);
+	const orientation = args.orientation || "w";
+	const markArray = getMarkArray(args.marks);
+	const shadowArray = getShadowArray(args.shadow);
 	let boardDiv = "";
 	const [startX,startY,inc] = orientation == 'w'
 		? [0, 0, 1]
-		: [sizeX-1, sizeY-1, -1];
-	for (let i=startX; i>=0 && i<sizeX; i+=inc)
+		: [V.size.x-1, V.size.y-1, -1];
+	for (let i=startX; i>=0 && i<V.size.x; i+=inc)
 	{
 		boardDiv += "<div class='row'>";
-		for (let j=startY; j>=0 && j<sizeY; j+=inc)
+		for (let j=startY; j>=0 && j<V.size.y; j+=inc)
 		{
-			boardDiv += "<div class='board board" + sizeY + " " +
+			boardDiv += "<div class='board board" + V.size.y + " " +
 				((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") +
 				(shadowArray.length > 0 && shadowArray[i][j] ? " in-shadow" : "") +
 				"'>";
diff --git a/public/javascripts/variant.js b/public/javascripts/variant.js
index 819bf90f..ae576d89 100644
--- a/public/javascripts/variant.js
+++ b/public/javascripts/variant.js
@@ -1,28 +1,25 @@
 new Vue({
 	el: "#variantPage",
 	data: {
-		display: "play", //default: play!
-		problem: undefined, //current problem in view
+		display: "room", //default: main hall
 	},
 	created: function() {
+		// TODO: navigation becomes a little more complex
 		const url = window.location.href;
 		const hashPos = url.indexOf("#");
 		if (hashPos >= 0)
 			this.setDisplay(url.substr(hashPos+1));
 	},
 	methods: {
-		showProblem: function(problemTxt) {
-			this.problem = JSON.parse(problemTxt);
-			this.display = "play";
-		},
 		setDisplay: function(elt) {
 			this.display = elt;
+			// Close menu on small screens:
 			let menuToggle = document.getElementById("drawer-control");
 			if (!!menuToggle)
 				menuToggle.checked = false;
 		},
-		notDark: function() {
-			return variant != "Dark";
-		},
 	},
 });
+
+// TODO:
+// si quand on arrive il y a une continuation "humaine" : display="game" et retour à la partie !
diff --git a/public/javascripts/variants/Alice.js b/public/javascripts/variants/Alice.js
index 2788e210..57c7b25c 100644
--- a/public/javascripts/variants/Alice.js
+++ b/public/javascripts/variants/Alice.js
@@ -95,11 +95,25 @@ class AliceRules extends ChessRules
 		const pieces = Object.keys(V.ALICE_CODES);
 		const codes = Object.keys(V.ALICE_PIECES);
 		const mirrorSide = (pieces.includes(this.getPiece(x,y)) ? 1 : 2);
+		const color = this.getColor(x,y);
 
 		// Search valid moves on sideBoard
 		let saveBoard = this.board;
 		this.board = sideBoard || this.getSideBoard(mirrorSide);
-		let moves = super.getPotentialMovesFrom([x,y]);
+		let moves = super.getPotentialMovesFrom([x,y])
+			.filter(m => {
+				// Filter out king moves which result in under-check position on
+				// current board (before mirror traversing)
+				let aprioriValid = true;
+				if (m.appear[0].p == V.KING)
+				{
+					this.play(m);
+					if (this.underCheck(color))
+						aprioriValid = false;
+					this.undo(m);
+				}
+				return aprioriValid;
+			});
 		this.board = saveBoard;
 
 		// Finally filter impossible moves
diff --git a/public/javascripts/variants/Crazyhouse.js b/public/javascripts/variants/Crazyhouse.js
index c4780b78..3d9c743f 100644
--- a/public/javascripts/variants/Crazyhouse.js
+++ b/public/javascripts/variants/Crazyhouse.js
@@ -29,8 +29,8 @@ class CrazyhouseRules extends ChessRules
 		return Object.assign(
 			ChessRules.ParseFen(fen),
 			{
-				reserve: fenParts[4],
-				promoted: fenParts[5],
+				reserve: fenParts[5],
+				promoted: fenParts[6],
 			}
 		);
 	}
diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js
index 16963f82..8d06f710 100644
--- a/public/javascripts/variants/Grand.js
+++ b/public/javascripts/variants/Grand.js
@@ -40,7 +40,7 @@ class GrandRules extends ChessRules
 		const fenParts = fen.split(" ");
 		return Object.assign(
 			ChessRules.ParseFen(fen),
-			{ captured: fenParts[4] }
+			{ captured: fenParts[5] }
 		);
 	}
 
diff --git a/views/index.pug b/views/index.pug
index 34c059b4..5d27eb8a 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -5,51 +5,32 @@ block css
 
 block content
 	.container#indexPage
-		include langNames.pug
 		case lang
 			when "en"
-				include translations/en.pug
-				include welcome/en.pug
-				include modal-lang/en.pug
-				include modal-help/en.pug
+				include welcome/en
 			when "es"
-				include translations/es.pug
-				include welcome/es.pug
-				include modal-lang/es.pug
-				include modal-help/es.pug
+				include welcome/es
 			when "fr"
-				include translations/fr.pug
-				include welcome/fr.pug
-				include modal-lang/fr.pug
-				include modal-help/fr.pug
+				include welcome/fr
 		.row
 			#header.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
-				#mainTitle
+				#mainTitle.clickable(
+						onClick="document.getElementById('modalWelcome').checked=true")
 					img(src="/images/index/unicorn.svg")
 					.info-container
 						p vchess.club
 					img(src="/images/index/wildebeest.svg")
-				#flagMenu.clickable(
-						onClick="document.getElementById('modalLang').checked=true")
-					img(src="/images/flags/" + lang + ".svg")
-				#helpMenu.clickable(
-						onClick="document.getElementById('modalHelp').checked=true")
-					.info-container
-						p= translations["Help"]
+				#settings.clickable(
+						onClick="document.getElementById('modalSettings').checked=true")
+					i.material-icons settings
 		.row
 			my-variant-summary(v-for="(v,idx) in sortedCounts"
 				v-bind:vobj="v" v-bind:index="idx" v-bind:key="v.name")
-		// Other modals:
-		input#modalB4welcome.modal(type="checkbox")
-		div(role="dialog")
-			#b4welcome.card.text-center.small-modal
-				h3.blue= translations["First visit?"]
-				p#readThis.clickable(@click="showWelcomeMsg")
-					=translations[">>> Please read this <<<"]
+
+					redesign index page :: lien github, lien contact mail, settings
 
 block javascripts
 	script.
-		const translations = !{JSON.stringify(translations)};
 		const variantArray = !{JSON.stringify(variantArray)};
 	script(src="/javascripts/utils/misc.js")
 	script(src="/javascripts/socket_url.js")
diff --git a/views/layout.pug b/views/layout.pug
index 5df5212a..d894ff0c 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -20,16 +20,29 @@ html
 		link(rel="mask-icon" href="/images/favicon/safari-pinned-tab.svg" color="#5bbad5")
 		link(rel="shortcut icon" href="/images/favicon/favicon.ico")
 		link(rel="stylesheet" href="/stylesheets/layout.css")
+		link(rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons")
 		block css
 
 	body
 
 		main
+			include langNames
+			case lang
+				when "en"
+					include translations/en
+				when "es"
+					include translations/es
+				when "fr"
+					include translations/fr
+			include settings
 			block content
 
 		script(src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js")
+		script(src="/javascripts/layout.js")
 		if development
 			script(src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js")
 		else
 			script(src="https://cdn.jsdelivr.net/npm/vue")
+		script.
+			const translations = !{JSON.stringify(translations)};
 		block javascripts
diff --git a/views/modal-help/en.pug b/views/modal-help/en.pug
deleted file mode 100644
index f40adbed..00000000
--- a/views/modal-help/en.pug
+++ /dev/null
@@ -1,23 +0,0 @@
-input#modalHelp.modal(type="checkbox")
-div(role="dialog")
-	#help.card
-		label.modal-close(for="modalHelp")
-		.section
-			ol
-				li Click on a variant,
-				li Read the rules (in the upper left corner),
-				li Back to playing mode (click on "Play")
-				li Click on "New live game", and then, while waiting
-				li Click on "New computer game" (just next).
-			p Reminder:
-			ul
-				li All games start with a random assymetric position.
-				li Games are untimed, and played anonymously.
-				li No chat while playing, to focus on the moves.
-		.section
-			h3.red Bug report
-			p
-				| Please send an email (in English or French) to 
-				a(href="mailto:contact@vchess.club?subject=[vchess.club] bug report")
-					| contact@vchess.club
-				| .
diff --git a/views/modal-help/es.pug b/views/modal-help/es.pug
deleted file mode 100644
index 2ac6ad4e..00000000
--- a/views/modal-help/es.pug
+++ /dev/null
@@ -1,23 +0,0 @@
-input#modalHelp.modal(type="checkbox")
-div(role="dialog")
-	#help.card
-		label.modal-close(for="modalHelp")
-		.section
-			ol
-				li Haga clic en una variante,
-				li Lee las reglas (arriba a la izquierda),
-				li Volver al modo de juego (haga clic en "Jugar"),
-				li Haga clic en "Nuevo juego en vivo", luego, mientras tanto,
-				li Haga clic en "Nuevo juego contra la computadora" (siguiente).
-			p Recordatorio :
-			ul
-				li Todas las partes comienzan con una posición aleatoria asimétrica.
-				li Los juegos no están cronometrados, y se juegan anónimamente.
-				li No charla durante el juego, para concentrarse en los movimientos.
-		.section
-			h3.red Informe de bug
-			p
-				| Por favor envíe un correo electrónico (en inglés o francés) a 
-				a(href="mailto:contact@vchess.club?subject=[vchess.club] bug report")
-					| contact@vchess.club
-				| .
diff --git a/views/modal-help/fr.pug b/views/modal-help/fr.pug
deleted file mode 100644
index 4775f986..00000000
--- a/views/modal-help/fr.pug
+++ /dev/null
@@ -1,23 +0,0 @@
-input#modalHelp.modal(type="checkbox")
-div(role="dialog")
-	#help.card
-		label.modal-close(for="modalHelp")
-		.section
-			ol
-				li Cliquez sur une variante,
-				li Lisez les règles (en haut à gauche),
-				li Revenez en mode jeu (cliquer sur "Jouer")
-				li Cliquez sur "Nouvelle partie en direct", puis, en attendant
-				li Cliquez sur "Nouvelle partie contre l'ordinateur" (à côté).
-			p Rappel :
-			ul
-				li Toutes les parties démarrent avec une position aléatoire assymétrique.
-				li Les parties ne sont pas chronométrées, et jouée anonymemement.
-				li Pas de chat pendant la partie, pour se concentrer sur les coups.
-		.section
-			h3.red Rapport de bug
-			p
-				| SVP envoyez un email (en français ou anglais) à 
-				a(href="mailto:contact@vchess.club?subject=[vchess.club] bug report")
-					| contact@vchess.club
-				| .
diff --git a/views/modal-lang/en.pug b/views/modal-lang/en.pug
deleted file mode 100644
index 53795729..00000000
--- a/views/modal-lang/en.pug
+++ /dev/null
@@ -1,24 +0,0 @@
-input#modalLang.modal(type="checkbox")
-div(role="dialog")
-	#language.card
-		label.modal-close(for="modalLang")
-		.section
-			fieldset
-				label(for="langSelect") Preferred language?
-				select#langSelect(onChange="setLanguage(event)")
-					each langCode in languages
-						option(value=langCode selected=(lang==langCode))
-							=langName[langCode]
-		.section
-			h3.blue Contribute
-			p
-				| Browse the 
-				a(href="https://github.com/yagu0/vchess/tree/master/views")
-					| github repository
-				| : all files in the folders langNames/, modal-help/, modal-lang/,
-				| translations/ and welcome/ should be translated. Moreover, and
-				| it's the longest part, you would have to translate all the rules under 
-				| rules/. When it's done, send me the files: 
-				a(href="mailto:contact@vchess.club?subject=[vchess.club] translation")
-					| contact@vchess.club
-				| . Thanks!
diff --git a/views/modal-lang/es.pug b/views/modal-lang/es.pug
deleted file mode 100644
index 114bbd0a..00000000
--- a/views/modal-lang/es.pug
+++ /dev/null
@@ -1,25 +0,0 @@
-input#modalLang.modal(type="checkbox")
-div(role="dialog")
-	#language.card
-		label.modal-close(for="modalLang")
-		.section
-			fieldset
-				label(for="langSelect") ¿ Idioma preferido ?
-				select#langSelect(onChange="setLanguage(event)")
-					each langCode in languages
-						option(value=langCode selected=(lang==langCode))
-							=langName[langCode]
-		.section
-			h3.blue Contribuir
-			p
-				| ¿ Tu idioma favorito no está disponible ? Entonces...
-				| Navegar por el 
-				a(href="https://github.com/yagu0/vchess/tree/master/views")
-					| repositorio github
-				| &nbsp;: tiene que traducir todos los ficheros en los archivos langNames/,
-				| modal-help/, modal-lang/, translations/ y welcome/.
-				| Además, y esa es la parte principal del trabajo, tiene que traducir todas
-				| las reglas en rules/. Una vez hecho esto, envíame los archivos : 
-				a(href="mailto:contact@vchess.club?subject=[vchess.club] translation")
-					| contact@vchess.club
-				| . ¡ Gracias !
diff --git a/views/modal-lang/fr.pug b/views/modal-lang/fr.pug
deleted file mode 100644
index ffb25c40..00000000
--- a/views/modal-lang/fr.pug
+++ /dev/null
@@ -1,25 +0,0 @@
-input#modalLang.modal(type="checkbox")
-div(role="dialog")
-	#language.card
-		label.modal-close(for="modalLang")
-		.section
-			fieldset
-				label(for="langSelect") Langue préférée ?
-				select#langSelect(onChange="setLanguage(event)")
-					each langCode in languages
-						option(value=langCode selected=(lang==langCode))
-							=langName[langCode]
-		.section
-			h3.blue Contribuer
-			p
-				| Votre langue favorite n'est pas disponible ? Alors...
-				| Parcourez le 
-				a(href="https://github.com/yagu0/vchess/tree/master/views")
-					| dépôt github
-				| &nbsp;: il faut traduire tous les fichiers dans les dossiers langNames/,
-				| modal-help/, modal-lang/, translations/ et welcome/. De plus, et c'est l'essentiel
-				| du travail il faut traduire toutes les règles dans rules/.
-				| Une fois que c'est fait, envoyez-moi les fichiers : 
-				a(href="mailto:contact@vchess.club?subject=[vchess.club] translation")
-					| contact@vchess.club
-				| . Merci !
diff --git a/views/settings.pug b/views/settings.pug
new file mode 100644
index 00000000..c46c5f8f
--- /dev/null
+++ b/views/settings.pug
@@ -0,0 +1,36 @@
+input#modal-settings.modal(type="checkbox")
+div(role="dialog" aria-labelledby="settingsTitle")
+	.card.smallpad(onChange="blabla(event)")
+		label#close-settings.modal-close(for="modal-settings")
+		h3#settingsTitle.section= translations["Preferences"]
+		fieldset
+			label(for="langSelect")= translations["Language"]
+			// image avec drapeau + select language ici
+			select#langSelect
+				each langCode in languages
+					option(value=langCode selected=(lang==langCode))
+						=langName[langCode]
+		fieldset
+			label(for="nameSetter")
+				=translations["My name is..."]
+			input#nameSetter(type="text" value=this.myname)
+		// theme sombre / clair
+		// taille echiquier : TODO
+		fieldset
+			label(for="setHints")= translations["Show hints?"]
+			input#setHints(type: "checkbox" checked=this.hints)
+		fieldset
+			label(for="selectColor")= translations["Board colors"]
+			select#selectColor
+				option(value="lichess" selected="this.color=='lichess'")
+					= translations["brown"]
+				option(value="chesscom" selected="this.color=='chesscom'")
+					= translations["green"]
+				option(value="chesstempo" selected="this.color=='chesstempo'")
+					= translations["blue"]
+		fieldset
+			label(for="selectSound")= translations["Play sounds?"]
+			select#selectSound
+				option(value="0" selected="this.sound==0")= translations["None"]
+				option(value="1" selected="this.sound==1")= translations["New game"]
+				option(value="2" selected="this.sound==2")= translations["All"]
diff --git a/views/translations/en.pug b/views/translations/en.pug
index 10a82cd7..fe67aba9 100644
--- a/views/translations/en.pug
+++ b/views/translations/en.pug
@@ -1,6 +1,8 @@
 -
 	var translations =
 	{
+		"Language": "Language",
+
 		// Index page:
 		"Help": "Help",
 		"First visit?": "First visit?",
diff --git a/views/translations/es.pug b/views/translations/es.pug
index 98d8b06d..642059ed 100644
--- a/views/translations/es.pug
+++ b/views/translations/es.pug
@@ -1,6 +1,8 @@
 -
 	var translations =
 	{
+		"Language": "Idioma",
+
 		// Index page:
 		"Help": "Ayuda",
 		"First visit?": "¿ Primera visita ?",
diff --git a/views/translations/fr.pug b/views/translations/fr.pug
index b76e4d25..78b10744 100644
--- a/views/translations/fr.pug
+++ b/views/translations/fr.pug
@@ -1,6 +1,8 @@
 -
 	var translations =
 	{
+		"Language": "Langue",
+
 		// Index page:
 		"Help": "Aide",
 		"First visit?": "Première visite ?",
diff --git a/views/variant.pug b/views/variant.pug
index 7f01b910..ca9091bf 100644
--- a/views/variant.pug
+++ b/views/variant.pug
@@ -1,58 +1,36 @@
 extends layout
 
 block css
-	link(rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons")
 	link(rel="stylesheet" href="/stylesheets/variant.css")
 
 block content
 	.container#variantPage
-		include langNames.pug
-		case lang
-			when "en"
-				include translations/en.pug
-				include modal-lang/en.pug
-				include modal-help/en.pug
-			when "es"
-				include translations/es.pug
-				include modal-lang/es.pug
-				include modal-help/es.pug
-			when "fr"
-				include translations/fr.pug
-				include modal-lang/fr.pug
-				include modal-help/fr.pug
-		input#modal-newgame.modal(type="checkbox")
-		div(role="dialog" aria-labelledby="newGameTxt")
-			.card.smallpad.small-modal
-				label#close-newgame.modal-close(for="modal-newgame")
-				h3#newGameTxt= translations["New game"]
-				p= translations["Waiting for opponent..."]
 		.row
 			.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
 				label.drawer-toggle(for="drawer-control")
 				input#drawer-control.drawer(type="checkbox")
 				#menuBar
 					label.drawer-close(for="drawer-control")
-					a#homeLink(href="/")
+					a.icon-link(href="/")
 						i.material-icons home
-					.info-container
-						a(href="#rules" @click="setDisplay('rules')")
-							=translations["Rules"]
-						a(href="#play" @click="setDisplay('play')")
-							=translations["Play"]
-						a(href="#problems" v-if="notDark()" @click="setDisplay('problems')") 
-							=translations["Problems"]
-					#flagMenu.clickable(
-							onClick="document.getElementById('modalLang').checked=true")
-						img(src="/images/flags/" + lang + ".svg")
-					#helpMenu.clickable(
-							onClick="document.getElementById('modalHelp').checked=true")
-						.info-container
-							p= translations["Help"]
+					a(href="#room" @click="setDisplay('room')")
+						=translations["Hall"]
+					a(href="#gameList" @click="setDisplay('gameList')")
+						=translations["Play"]
+					a(href="#rules" @click="setDisplay('rules')")
+						=translations["Rules"]
+					a(href="#problems" @click="setDisplay('problems')")
+						=translations["Problems"]
+					#settings.clickable(
+							onClick="document.getElementById('modalSettings').checked=true")
+						i.material-icons settings
 		.row
+			my-room(v-show="display=='room'")
+			my-games-list(v-show="display=='gameList'")
 			my-rules(v-show="display=='rules'")
-			my-game(v-show="display=='play'" v-bind:problem="problem")
-			my-problems(v-if="notDark()" v-show="display=='problems'"
-				v-on:show-problem="showProblem($event)")
+			my-problems(v-show="display=='problems'")
+			// my-game: for room and games-list components
+			my-game(v-show="display=='game'" :gameId="")
 
 block javascripts
 	script(src="/javascripts/utils/misc.js")
@@ -68,7 +46,6 @@ block javascripts
 		const V = VariantRules; //because this variable is often used
 		const variant = "#{variant}";
 		const problemArray = !{JSON.stringify(problemArray)};
-		const translations = !{JSON.stringify(translations)};
 	script(src="/javascripts/components/rules.js")
 	script(src="/javascripts/components/game.js")
 	script(src="/javascripts/components/problemSummary.js")
diff --git a/views/welcome/en.pug b/views/welcome/en.pug
index dbaf234c..06a9ca84 100644
--- a/views/welcome/en.pug
+++ b/views/welcome/en.pug
@@ -46,6 +46,4 @@ div(role="dialog")
 				For informations about hundreds (if not thousands) of variants, you
 				can visit the excellent
 				#[a(href="https://www.chessvariants.com/") chessvariants] website.
-		p#disableMsg.clickable(@click="markAsVisited")
-			| Click here to not show this message next time
 		p.smallfont Image credit: #[a(href=wikipediaUrl) Wikipedia]
diff --git a/views/welcome/es.pug b/views/welcome/es.pug
index 19d903e1..0c561bc2 100644
--- a/views/welcome/es.pug
+++ b/views/welcome/es.pug
@@ -49,6 +49,4 @@ div(role="dialog")
 				Pour s'informer sur des centaines de variantes (au moins), je vous invite à
 				visiter l'excellent site
 				#[a(href="https://www.chessvariants.com/") chessvariants].
-		p#disableMsg.clickable(@click="markAsVisited")
-			| Haga clic aquí para no mostrar este mensaje la próxima vez.
 		p.smallfont Credito de imagen : #[a(href=wikipediaUrl) Wikipedia]
diff --git a/views/welcome/fr.pug b/views/welcome/fr.pug
index 3821f896..d650a739 100644
--- a/views/welcome/fr.pug
+++ b/views/welcome/fr.pug
@@ -46,6 +46,4 @@ div(role="dialog")
 				Pour s'informer sur des centaines de variantes (au moins), je vous invite à
 				visiter l'excellent site
 				#[a(href="https://www.chessvariants.com/") chessvariants].
-		p#disableMsg.clickable(@click="markAsVisited")
-			| Cliquer ici pour ne pas montrer ce message la prochaine fois
 		p.smallfont Crédit image : #[a(href=wikipediaUrl) Wikipedia]