From 4f7723a1a0d1554f16c699017ae308079aa43a69 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 25 Dec 2018 11:08:52 +0100
Subject: [PATCH] Some improvements; to be continuated

---
 public/javascripts/base_rules.js         |   4 +-
 public/javascripts/components/game.js    | 208 +++++++++++++----------
 public/javascripts/variants/Marseille.js |  15 +-
 public/stylesheets/variant.sass          |   4 +
 views/rules/Baroque/fr.pug               |   6 +-
 views/translations/en.pug                |   4 +-
 views/translations/es.pug                |   4 +-
 views/translations/fr.pug                |   4 +-
 8 files changed, 148 insertions(+), 101 deletions(-)

diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 8b8165a0..7a42a385 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -956,11 +956,11 @@ class ChessRules
 			board[psq.x][psq.y] = psq.c + psq.p;
 	}
 
-	// Before move is played, update variables + flags
+	// After move is played, update variables + flags
 	updateVariables(move)
 	{
 		const piece = move.vanish[0].p;
-		const c = move.vanish[0].c;
+		const c = this.getOppCol(this.turn); //'move.vanish[0].c' doesn't work for Checkered
 		const firstRank = (c == "w" ? V.size.x-1 : 0);
 
 		// Update king position + flags
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index c14130b5..085dafa3 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -11,7 +11,7 @@ Vue.component('my-game', {
 			selectedPiece: null, //moving piece (or clicked piece)
 			conn: null, //socket connection
 			score: "*", //'*' means 'unfinished'
-			mode: "idle", //human, chat, friend, problem, computer or idle (if not playing)
+			mode: "idle", //human, friend, problem, computer or idle (if not playing)
 			myid: "", //our ID, always set
 			oppid: "", //opponent ID in case of HH game
 			gameId: "", //useful if opponent started other human games after we disconnected
@@ -58,12 +58,13 @@ Vue.component('my-game', {
 					"play": true,
 					"seek": this.seek,
 					"playing": this.mode == "human",
-					"spaceright": true,
 				},
 			},
 			[h('i', { 'class': { "material-icons": true } }, "accessibility")])
 		);
-		if (variant!="Dark" && ["idle","chat","computer"].includes(this.mode))
+		if (variant != "Dark" &&
+			(["idle","computer","friend"].includes(this.mode)
+				|| ["friend","human"].includes(this.mode) && this.score != "*"))
 		{
 			actionArray.push(
 				h('button',
@@ -74,13 +75,15 @@ Vue.component('my-game', {
 						"tooltip":true,
 						"play": true,
 						"playing": this.mode == "computer",
-						"spaceright": true,
+						"spaceleft": true,
 					},
 				},
 				[h('i', { 'class': { "material-icons": true } }, "computer")])
 			);
 		}
-		if (variant!="Dark" && ["idle","chat","friend"].includes(this.mode))
+		if (variant != "Dark" &&
+			(["idle","friend"].includes(this.mode)
+				|| ["computer","human"].includes(this.mode) && this.score != "*"))
 		{
 			actionArray.push(
 				h('button',
@@ -91,7 +94,7 @@ Vue.component('my-game', {
 						"tooltip":true,
 						"play": true,
 						"playing": this.mode == "friend",
-						"spaceright": true,
+						"spaceleft": true,
 					},
 				},
 				[h('i', { 'class': { "material-icons": true } }, "people")])
@@ -114,7 +117,7 @@ Vue.component('my-game', {
 				parseFloat(settingsStyle.height.slice(0,-2)) - 2
 			];
 			let aboveBoardElts = [];
-			if (["chat","human"].includes(this.mode))
+			if (this.mode == "human")
 			{
 				const connectedIndic = h(
 					'div',
@@ -132,7 +135,7 @@ Vue.component('my-game', {
 				);
 				aboveBoardElts.push(connectedIndic);
 			}
-			if (this.mode == "chat")
+			if (this.mode == "human" && this.score != "*")
 			{
 				const chatButton = h(
 					'button',
@@ -153,14 +156,14 @@ Vue.component('my-game', {
 				);
 				aboveBoardElts.push(chatButton);
 			}
-			else if (this.mode == "computer")
+			if (["human","computer","friend"].includes(this.mode))
 			{
 				const clearButton = h(
 					'button',
 					{
-						on: { click: this.clearComputerGame },
+						on: { click: this.clearCurrentGame },
 						attrs: {
-							"aria-label": translations['Clear game versus computer'],
+							"aria-label": translations['Clear current game'],
 							"id": "clearBtn",
 						},
 						'class': {
@@ -277,7 +280,8 @@ Vue.component('my-game', {
 			// Create board element (+ reserves if needed by variant or mode)
 			const lm = this.vr.lastMove;
 			const showLight = this.hints && variant!="Dark" &&
-				(!["idle","chat"].includes(this.mode) || this.cursor==this.vr.moves.length);
+				(this.mode != "idle" ||
+					(this.vr.moves.length > 0 && this.cursor==this.vr.moves.length));
 			const gameDiv = h('div',
 				{
 					'class': {
@@ -358,47 +362,51 @@ Vue.component('my-game', {
 					);
 				}), choices]
 			);
-			if (!["idle","chat"].includes(this.mode))
+			if (["human","computer"].includes(this.mode))
 			{
-				actionArray.push(
-					h('button',
-						{
-							on: { click: this.resign },
-							attrs: { "aria-label": translations['Resign'] },
-							'class': {
-								"tooltip":true,
-								"play": true,
+				if (this.score == "*")
+				{
+					actionArray.push(
+						h('button',
+							{
+								on: { click: this.resign },
+								attrs: { "aria-label": translations['Resign'] },
+								'class': {
+									"tooltip":true,
+									"play": true,
+									"spaceleft": true,
+								},
 							},
-						},
-						[h('i', { 'class': { "material-icons": true } }, "flag")])
-				);
-			}
-			else if (this.vr.moves.length > 0)
-			{
-				// A game finished, and another is not started yet: allow navigation
-				actionArray = actionArray.concat([
-					h('button',
-						{
-							on: { click: e => this.undo() },
-							attrs: { "aria-label": translations['Undo'] },
-							"class": {
-								"play": true,
-								"spaceleft": true,
+							[h('i', { 'class': { "material-icons": true } }, "flag")])
+					);
+				}
+				else
+				{
+					// A game finished, and another is not started yet: allow navigation
+					actionArray = actionArray.concat([
+						h('button',
+							{
+								on: { click: e => this.undo() },
+								attrs: { "aria-label": translations['Undo'] },
+								"class": {
+									"play": true,
+									"big-spaceleft": true,
+								},
 							},
-						},
-						[h('i', { 'class': { "material-icons": true } }, "fast_rewind")]),
-					h('button',
-						{
-							on: { click: e => this.play() },
-							attrs: { "aria-label": translations['Play'] },
-							"class": {
-								"play": true,
-								"spaceleft": true,
+							[h('i', { 'class': { "material-icons": true } }, "fast_rewind")]),
+						h('button',
+							{
+								on: { click: e => this.play() },
+								attrs: { "aria-label": translations['Play'] },
+								"class": {
+									"play": true,
+									"spaceleft": true,
+								},
 							},
-						},
-						[h('i', { 'class': { "material-icons": true } }, "fast_forward")]),
-					]
-				);
+							[h('i', { 'class': { "material-icons": true } }, "fast_forward")]),
+						]
+					);
+				}
 			}
 			if (["friend","problem"].includes(this.mode))
 			{
@@ -410,7 +418,7 @@ Vue.component('my-game', {
 							attrs: { "aria-label": translations['Undo'] },
 							"class": {
 								"play": true,
-								"spaceleft": true,
+								"big-spaceleft": true,
 							},
 						},
 						[h('i', { 'class': { "material-icons": true } }, "undo")]
@@ -989,6 +997,7 @@ Vue.component('my-game', {
 		const url = socketUrl;
 		const humanContinuation = (localStorage.getItem("variant") === variant);
 		const computerContinuation = (localStorage.getItem("comp-variant") === variant);
+		const friendContinuation = (localStorage.getItem("anlz-variant") === variant);
 		this.myid = (humanContinuation ? localStorage.getItem("myid") : getRandString());
 		this.conn = new WebSocket(url + "/?sid=" + this.myid + "&page=" + variant);
 		const socketOpenListener = () => {
@@ -996,6 +1005,8 @@ Vue.component('my-game', {
 				this.continueGame("human");
 			else if (computerContinuation)
 				this.continueGame("computer");
+			else if (friendContinuation)
+				this.continueGame("friend");
 		};
 		const socketMessageListener = msg => {
 			const data = JSON.parse(msg.data);
@@ -1072,9 +1083,9 @@ Vue.component('my-game', {
 				// TODO: also use (dis)connect info to count online players?
 				case "connect":
 				case "disconnect":
-					if (["human","chat"].includes(this.mode) && this.oppid == data.id)
+					if (this.mode=="human" && this.oppid == data.id)
 						this.oppConnected = (data.code == "connect");
-					if (this.oppConnected && this.mode == "chat")
+					if (this.oppConnected && this.score != "*")
 					{
 						// Send our name to the opponent, in case of he hasn't it
 						this.conn.send(JSON.stringify({
@@ -1174,9 +1185,16 @@ Vue.component('my-game', {
 				this.conn.send(JSON.stringify({
 					code:"myname", name:this.myname, oppid:this.oppid}));
 			}
-			this.mode = (this.mode=="human" ? "chat" : "idle");
 			this.cursor = this.vr.moves.length; //to navigate in finished game
 		},
+		getStoragePrefix: function(mode) {
+			let prefix = "";
+			if (mode == "computer")
+				prefix = "comp-";
+			else if (mode == "friend")
+				prefix = "anlz-";
+			return prefix;
+		},
 		setStorage: function() {
 			if (this.mode=="human")
 			{
@@ -1184,8 +1202,7 @@ Vue.component('my-game', {
 				localStorage.setItem("oppid", this.oppid);
 				localStorage.setItem("gameId", this.gameId);
 			}
-			// 'prefix' = "comp-" to resume games vs. computer
-			const prefix = (this.mode=="computer" ? "comp-" : "");
+			const prefix = this.getStoragePrefix(this.mode);
 			localStorage.setItem(prefix+"variant", variant);
 			localStorage.setItem(prefix+"mycolor", this.mycolor);
 			localStorage.setItem(prefix+"fenStart", this.fenStart);
@@ -1194,7 +1211,7 @@ Vue.component('my-game', {
 			localStorage.setItem(prefix+"score", "*");
 		},
 		updateStorage: function() {
-			const prefix = (this.mode=="computer" ? "comp-" : "");
+			const prefix = this.getStoragePrefix(this.mode);
 			localStorage.setItem(prefix+"moves", JSON.stringify(this.vr.moves));
 			localStorage.setItem(prefix+"fen", this.vr.getFen());
 			if (this.score != "*")
@@ -1202,13 +1219,13 @@ Vue.component('my-game', {
 		},
 		// "computer mode" clearing is done through the menu
 		clearStorage: function() {
-			if (["human","chat"].includes(this.mode))
+			if (this.mode == "human")
 			{
 				delete localStorage["myid"];
 				delete localStorage["oppid"];
 				delete localStorage["gameId"];
 			}
-			const prefix = (this.mode=="computer" ? "comp-" : "");
+			const prefix = this.getStoragePrefix(this.mode);
 			delete localStorage[prefix+"variant"];
 			delete localStorage[prefix+"mycolor"];
 			delete localStorage[prefix+"fenStart"];
@@ -1226,9 +1243,9 @@ Vue.component('my-game', {
 			this.getRidOfTooltip(e.currentTarget);
 			document.getElementById("modal-chat").checked = true;
 		},
-		clearComputerGame: function(e) {
+		clearCurrentGame: function(e) {
 			this.getRidOfTooltip(e.currentTarget);
-			this.clearStorage(); //this.mode=="computer" (already checked)
+			this.clearStorage();
 			location.reload(); //to see clearing effects
 		},
 		showSettings: function(e) {
@@ -1285,10 +1302,11 @@ Vue.component('my-game', {
 			if (mode=="human" && !oppId)
 			{
 				const storageVariant = localStorage.getItem("variant");
-				if (!!storageVariant && storageVariant !== variant)
+				if (!!storageVariant && storageVariant !== variant
+					&& localStorage["score"] == "*")
 				{
 					return alert(translations["Finish your "] +
-							storageVariant + translations[" game first!"]);
+						storageVariant + translations[" game first!"]);
 				}
 				// Send game request and wait..
 				try {
@@ -1302,17 +1320,13 @@ Vue.component('my-game', {
 				setTimeout(() => { modalBox.checked = false; }, 2000);
 				return;
 			}
-			if (["human","chat"].includes(this.mode))
-			{
-				// Start a new game vs. another human (or...) => forget about current one
-				this.clearStorage();
-			}
+			const prefix = this.getStoragePrefix(mode);
 			if (mode == "computer")
 			{
-				const storageVariant = localStorage.getItem("comp-variant");
+				const storageVariant = localStorage.getItem(prefix+"variant");
 				if (!!storageVariant)
 				{
-					const score = localStorage.getItem("comp-score");
+					const score = localStorage.getItem(prefix+"score");
 					if (storageVariant !== variant && score == "*")
 					{
 						if (!confirm(storageVariant +
@@ -1325,12 +1339,30 @@ Vue.component('my-game', {
 						return this.continueGame("computer");
 				}
 			}
+			else if (mode == "friend")
+			{
+				const storageVariant = localStorage.getItem(prefix+"variant");
+				if (!!storageVariant)
+				{
+					const score = localStorage.getItem(prefix+"score");
+					if (storageVariant !== variant && score == "*")
+					{
+						if (!confirm(storageVariant +
+							translations[": current analysis will be erased"]))
+						{
+							return;
+						}
+					}
+				}
+			}
 			this.vr = new VariantRules(fen, []);
 			this.score = "*";
 			this.pgnTxt = ""; //redundant with this.score = "*", but cleaner
 			this.mode = mode;
 			this.incheck = [];
 			this.fenStart = V.ParseFen(fen).position; //this is enough
+			if (mode != "problem")
+				this.setStorage(); //store game state in case of interruptions
 			if (mode=="human")
 			{
 				// Opponent found!
@@ -1342,13 +1374,11 @@ Vue.component('my-game', {
 				if (this.sound >= 1)
 					new Audio("/sounds/newgame.mp3").play().catch(err => {});
 				document.getElementById("modal-newgame").checked = false;
-				this.setStorage(); //in case of interruptions
 			}
 			else if (mode == "computer")
 			{
 				this.compWorker.postMessage(["init",this.vr.getFen()]);
 				this.mycolor = (Math.random() < 0.5 ? 'w' : 'b');
-				this.setStorage(); //store game state
 				if (this.mycolor != this.vr.turn)
 					this.playComputerMove();
 			}
@@ -1359,7 +1389,7 @@ Vue.component('my-game', {
 		continueGame: function(mode) {
 			this.mode = mode;
 			this.oppid = (mode=="human" ? localStorage.getItem("oppid") : undefined);
-			const prefix = (mode=="computer" ? "comp-" : "");
+			const prefix = this.getStoragePrefix(mode);
 			this.mycolor = localStorage.getItem(prefix+"mycolor");
 			const moves = JSON.parse(localStorage.getItem(prefix+"moves"));
 			const fen = localStorage.getItem(prefix+"fen");
@@ -1374,12 +1404,13 @@ Vue.component('my-game', {
 				this.conn.send(JSON.stringify({
 					code:"ping",oppid:this.oppid,gameId:this.gameId}));
 			}
-			else
+			else if (mode == "computer")
 			{
 				this.compWorker.postMessage(["init",fen]);
 				if (this.mycolor != this.vr.turn)
 					this.playComputerMove();
 			}
+			//else: nothing special to do in friend mode
 			if (score != "*")
 			{
 				// Small delay required when continuation run faster than drawing page
@@ -1432,7 +1463,7 @@ Vue.component('my-game', {
 				this.selectedPiece.style.zIndex = 3000;
 				const startSquare = this.getSquareFromId(e.target.parentNode.id);
 				this.possibleMoves = [];
-				if (!["idle","chat"].includes(this.mode))
+				if (this.score == "*")
 				{
 					const color = ["friend","problem"].includes(this.mode)
 						? this.vr.turn
@@ -1440,7 +1471,6 @@ Vue.component('my-game', {
 					if (this.vr.canIplay(color,startSquare))
 						this.possibleMoves = this.vr.getPossibleMovesFrom(startSquare);
 				}
-				console.log(this.possibleMoves);
 				// Next line add moving piece just after current image
 				// (required for Crazyhouse reserve)
 				e.target.parentNode.insertBefore(this.selectedPiece, e.target.nextSibling);
@@ -1538,14 +1568,11 @@ Vue.component('my-game', {
 				move = this.vr.moves[this.cursor++];
 			}
 			if (!!programmatic) //computer or human opponent
-			{
-				this.animateMove(move);
-				return;
-			}
+				return this.animateMove(move);
 			// Not programmatic, or animation is over
 			if (this.mode == "human" && this.vr.turn == this.mycolor)
 				this.conn.send(JSON.stringify({code:"newmove", move:move, oppid:this.oppid}));
-			if (!["idle","chat"].includes(this.mode))
+			if (this.score == "*")
 			{
 				// Emergency check, if human game started "at the same time"
 				// TODO: robustify this...
@@ -1567,22 +1594,19 @@ Vue.component('my-game', {
 				VariantRules.PlayOnBoard(this.vr.board, move);
 				this.$forceUpdate(); //TODO: ?!
 			}
-			if (!["idle","chat"].includes(this.mode))
+			const eog = this.vr.checkGameOver();
+			if (eog != "*")
 			{
-				const eog = this.vr.checkGameOver();
-				if (eog != "*")
+				if (["human","computer"].includes(this.mode))
+					this.endGame(eog);
+				else
 				{
-					if (["human","computer"].includes(this.mode))
-						this.endGame(eog);
-					else
-					{
-						// Just show score on screen (allow undo)
-						this.score = eog;
-						this.showScoreMsg();
-					}
+					// Just show score on screen (allow undo)
+					this.score = eog;
+					this.showScoreMsg();
 				}
 			}
-			if (["human","computer"].includes(this.mode))
+			if (["human","computer","friend"].includes(this.mode))
 				this.updateStorage(); //after our moves and opponent moves
 			if (this.mode == "computer" && this.vr.turn != this.mycolor && this.score == "*")
 				this.playComputerMove();
diff --git a/public/javascripts/variants/Marseille.js b/public/javascripts/variants/Marseille.js
index ab7a3afe..f4d79298 100644
--- a/public/javascripts/variants/Marseille.js
+++ b/public/javascripts/variants/Marseille.js
@@ -1,3 +1,16 @@
-//TODO: turn en fonction de la parité des coups...
+//TODO:
 //adapter alphabeta (dans baserules ? --> basé sur turn OK)
 // le reste == standard
+
+class MarseilleRules extends ChessRules
+{
+	// TODO: fen indication pour turn : w1, w2 ou b1, ou b2 (about to play 1st or 2nd sub-turn)
+	// + quelque chose pour indiquer si c'est le tout premier coup ("w" sans + d'indications)
+	//
+	// this.turn == "w" ou "b"
+	// this.subTurn == 0 ou 1 (ou 1 et 2)
+	//
+	// Alpha-beta ?
+}
+
+const VariantRules = MarseilleRules;
diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass
index c3df08c4..c2e28a18 100644
--- a/public/stylesheets/variant.sass
+++ b/public/stylesheets/variant.sass
@@ -77,6 +77,10 @@ button.play.spaceleft
   margin-left: 15px
 button.play.spaceright
   margin-right: 15px
+button.play.big-spaceleft
+  margin-left: 25px
+button.play.big-spaceright
+  margin-right: 25px
 
 .aboveboard-wrapper
   width: 80vh
diff --git a/views/rules/Baroque/fr.pug b/views/rules/Baroque/fr.pug
index b49a66b0..ff698452 100644
--- a/views/rules/Baroque/fr.pug
+++ b/views/rules/Baroque/fr.pug
@@ -27,7 +27,7 @@ ul
 	li Tour : coordinateur
 	li Cavalier : "sauteur long"
 	li Fou : caméléon
-	li Dame : "supprimeur"
+	li Dame : "retireur"
 	li King : roi (même comportement qu'aux échecs orthodoxes)
 p.
 	En outre, une nouvelle pièce est ajoutée : l'immobiliseur, dénoté par la
@@ -117,7 +117,7 @@ figure.diagram-container
 		| fen:2n4k/3r4/5b2/3p4/1m6/3b4/3N4/K7 d4,d6,d8,a5:
 	figcaption Toutes les captures marquées sont jouables depuis d2.
 
-h4 "Supprimeur" (dame)
+h4 "Retireur" (dame)
 
 p.
 	La dame capture en s'éloignant d'une pièce adverse adjacente, dans la direction
@@ -135,7 +135,7 @@ p.
 	Ainsi, il
 ul
 	li pince les pions (s'il bouge comme un pion),
-	li supprime les supprimeurs,
+	li retire les retireurs,
 	li saute par dessus les sauteurs longs,
 	li coordonne les coordinateurs.
 p ...et ces captures peuvent se cumuler.
diff --git a/views/translations/en.pug b/views/translations/en.pug
index 5253c976..a05b1fa8 100644
--- a/views/translations/en.pug
+++ b/views/translations/en.pug
@@ -34,7 +34,7 @@
 		"New game versus computer": "New game versus computer",
 		"Analysis mode": "Analysis mode",
 		"Start chat": "Start chat",
-		"Clear game versus computer": "Clear game versus computer",
+		"Clear current game": "Clear current game",
 		"Settings": "Settings",
 		"Resign": "Resign",
 		"Undo": "Undo",
@@ -79,4 +79,6 @@
 		" game first!": " game first!",
 		": unfinished computer game will be erased":
 			": unfinished computer game will be erased",
+		": current analysis will be erased":
+			": current analysis will be erased",
 	};
diff --git a/views/translations/es.pug b/views/translations/es.pug
index a839ef81..6c2dbcf2 100644
--- a/views/translations/es.pug
+++ b/views/translations/es.pug
@@ -34,7 +34,7 @@
 		"New game versus computer": "Nueva partida contra la computadora",
 		"Analysis mode": "Modo de análisis",
 		"Start chat": "Iniciar chat",
-		"Clear game versus computer": "Borrar la partida contra la computadora",
+		"Clear current game": "Borrar la partida actual",
 		"Settings": "Ajustes",
 		"Resign": "Abandonar",
 		"Undo": "Deshacer",
@@ -79,4 +79,6 @@
 		" game first!": " partida primero !",
 		": unfinished computer game will be erased":
 			" : una partida inconclusa contra la computadora será borrado",
+		": current analysis will be erased":
+			" : el análisis actual será borrado",
 	};
diff --git a/views/translations/fr.pug b/views/translations/fr.pug
index c4229fe8..e4197b41 100644
--- a/views/translations/fr.pug
+++ b/views/translations/fr.pug
@@ -34,7 +34,7 @@
 		"New game versus computer": "Nouvelle partie contre l'ordinateur",
 		"Analysis mode": "Mode analyse",
 		"Start chat": "Démarrer le chat",
-		"Clear game versus computer": "Effacer la partie contre l'ordinateur",
+		"Clear current game": "Effacer la partie courante",
 		"Settings": "Réglages",
 		"Resign": "Abandonner",
 		"Undo": "Annuler",
@@ -79,4 +79,6 @@
 		" game first!": " partie d'abord !",
 		": unfinished computer game will be erased":
 			" : une partie inachevée contre l'ordinateur sera effacée",
+		": current analysis will be erased":
+			" : l'analyse en cours sera effacée",
 	};
-- 
2.44.0