From c794dbb87592782913af0a09784ed25e019e4d10 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sun, 16 Dec 2018 04:07:08 +0100
Subject: [PATCH] Finished problems implementation. TODO: specify state for
 Crazyhouse,Grand. Improve style

---
 public/javascripts/base_rules.js                |  9 +++------
 public/javascripts/components/game.js           | 17 ++++++++++++++---
 public/javascripts/components/problemSummary.js | 16 +++++-----------
 public/javascripts/components/problems.js       |  7 +++++--
 public/javascripts/utils/datetime.js            | 15 +++++++++++++++
 public/javascripts/variant.js                   | 12 ++++++++----
 public/javascripts/variants/Antiking.js         |  5 ++---
 public/javascripts/variants/Checkered.js        | 16 ++++++++++------
 public/javascripts/variants/Grand.js            |  5 ++---
 public/javascripts/variants/Loser.js            |  4 ++--
 public/javascripts/variants/Ultima.js           |  4 ++--
 public/javascripts/variants/Wildebeest.js       |  5 ++---
 views/variant.pug                               |  6 ++++--
 13 files changed, 74 insertions(+), 47 deletions(-)
 create mode 100644 public/javascripts/utils/datetime.js

diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 5763af7e..b44d0856 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -50,7 +50,7 @@ class ChessRules
 	/////////////////
 	// INITIALIZATION
 
-	// fen == "position flags"
+	// fen == "position [flags [turn]]"
 	constructor(fen, moves)
 	{
 		this.moves = moves;
@@ -1107,7 +1107,7 @@ class ChessRules
 		return pieces["b"].join("") +
 			"/pppppppp/8/8/8/8/PPPPPPPP/" +
 			pieces["w"].join("").toUpperCase() +
-			" 1111"; //add flags
+			" 1111 w"; //add flags + turn
 	}
 
 	// Return current fen according to pieces+colors state
@@ -1209,14 +1209,11 @@ class ChessRules
 	// The score is already computed when calling this function
 	getPGN(mycolor, score, fenStart, mode)
 	{
-		const zeroPad = x => { return (x<10 ? "0" : "") + x; };
 		let pgn = "";
 		pgn += '[Site "vchess.club"]<br>';
-		const d = new Date();
 		const opponent = mode=="human" ? "Anonymous" : "Computer";
 		pgn += '[Variant "' + variant + '"]<br>';
-		pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) +
-			'-' + zeroPad(d.getDate()) + '"]<br>';
+		pgn += '[Date "' + getDate(new Date()) + '"]<br>';
 		pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
 		pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';
 		pgn += '[FenStart "' + fenStart + '"]<br>';
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 49db6188..ded30918 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -1,5 +1,6 @@
 // Game logic on a variant page
 Vue.component('my-game', {
+	props: ["problem"],
 	data: function() {
 		return {
 			vr: null, //object to check moves, store them, FEN..
@@ -23,6 +24,15 @@ Vue.component('my-game', {
 			sound: parseInt(getCookie("sound", "2")),
 		};
 	},
+	watch: {
+		problem: function(p, pp) {
+			// 'problem' prop changed: update board state
+			// TODO: FEN + turn + flags + rappel instructions / solution on click sous l'échiquier
+			// TODO: trouver moyen de passer la situation des reserves pour Crazyhouse,
+			// et l'état des captures pour Grand... bref compléter le descriptif de l'état.
+			this.newGame("problem", p.fen, p.fen.split(" ")[2]);
+		},
+	},
 	render(h) {
 		const [sizeX,sizeY] = [V.size.x,V.size.y];
 		const smallScreen = (window.innerWidth <= 420);
@@ -1077,7 +1087,7 @@ Vue.component('my-game', {
 				if (this.mycolor == 'b')
 					setTimeout(this.playComputerMove, 500);
 			}
-			//else: against a (IRL) friend: nothing more to do
+			//else: against a (IRL) friend or problem solving: nothing more to do
 		},
 		playComputerMove: function() {
 			const timeStart = Date.now();
@@ -1130,8 +1140,9 @@ Vue.component('my-game', {
 				this.selectedPiece.style.display = "inline-block";
 				this.selectedPiece.style.zIndex = 3000;
 				let startSquare = this.getSquareFromId(e.target.parentNode.id);
-				const iCanPlay = this.mode!="idle"
-					&& (this.mode=="friend" || this.vr.canIplay(this.mycolor,startSquare));
+				const iCanPlay = this.mode!="idle" &&
+					(["friend","problem"].includes(this.mode) ||
+					this.vr.canIplay(this.mycolor,startSquare));
 				this.possibleMoves = iCanPlay ? this.vr.getPossibleMovesFrom(startSquare) : [];
 				// Next line add moving piece just after current image
 				// (required for Crazyhouse reserve)
diff --git a/public/javascripts/components/problemSummary.js b/public/javascripts/components/problemSummary.js
index 48a42533..c0b409de 100644
--- a/public/javascripts/components/problemSummary.js
+++ b/public/javascripts/components/problemSummary.js
@@ -5,7 +5,7 @@ Vue.component('my-problem-summary', {
 		<div class="problem col-sm-12" @click="showProblem()">
 			<div class="diagram" v-html="getDiagram(prob.fen)"></div>
 			<div class="problem-instructions" v-html="prob.instructions.substr(0,32)"></div>
-			<div class="problem-time">{{ timestamp2datetime(prob.added) }}</div>
+			<div class="problem-time">{{ timestamp2date(prob.added) }}</div>
 		</div>
 	`,
 	methods: {
@@ -17,18 +17,12 @@ Vue.component('my-problem-summary', {
 				turn: fenParts[2],
 			});
 		},
-		timestamp2datetime(ts) {
-			// TODO
-			return ts;
+		timestamp2date(ts) {
+			return getDate(new Date(ts));
 		},
+		// Propagate "show problem" event to parent component (my-problems)
 		showProblem: function() {
-			alert("show problem");
-			//..........
-			//TODO: send event with object prob.fen, prob.instructions, prob.solution
-			//Event should propagate to game, which set mode=="problem" + other variables
-			//click on a problem ==> land on variant page with mode==friend, FEN prefilled... ok
-			// click on problem ==> masque problems, affiche game tab, launch new game Friend with
-			//   FEN + turn + flags + rappel instructions / solution on click sous l'échiquier
+			this.$emit('show-problem');
 		},
 	},
 })
diff --git a/public/javascripts/components/problems.js b/public/javascripts/components/problems.js
index dbd8340c..68830a4f 100644
--- a/public/javascripts/components/problems.js
+++ b/public/javascripts/components/problems.js
@@ -9,7 +9,7 @@ Vue.component('my-problems', {
 			<button @click="fetchProblems('backward')">Previous</button>
 			<button @click="fetchProblems('forward')">Next</button>
 			<button @click="showNewproblemModal">New</button>
-			<my-problem-summary
+			<my-problem-summary v-on:show-problem="bubbleUp(p)"
 				v-for="(p,idx) in sortedProblems" v-bind:prob="p" v-bind:key="idx">
 			</my-problem-summary>
 			<input type="checkbox" id="modal-newproblem" class="modal">
@@ -45,7 +45,6 @@ Vue.component('my-problems', {
 	`,
 	computed: {
 		sortedProblems: function() {
-			console.log("call");
 			// Newest problem first
 			return this.problems.sort((p1,p2) => { return p2.added - p1.added; });
 		},
@@ -54,6 +53,10 @@ Vue.component('my-problems', {
 		},
 	},
 	methods: {
+		// Propagate "show problem" event to parent component (my-variant)
+		bubbleUp: function(problem) {
+			this.$emit('show-problem', JSON.stringify(problem));
+		},
 		fetchProblems: function(direction) {
 			return; //TODO: re-activate after server side is implemented (see routes/all.js)
 			if (this.problems.length == 0)
diff --git a/public/javascripts/utils/datetime.js b/public/javascripts/utils/datetime.js
new file mode 100644
index 00000000..b6be6df9
--- /dev/null
+++ b/public/javascripts/utils/datetime.js
@@ -0,0 +1,15 @@
+function zeroPad(x)
+{
+	return (x<10 ? "0" : "") + x;
+}
+
+function getDate(d)
+{
+	return d.getFullYear() + '-' + (d.getMonth()+1) + '-' + zeroPad(d.getDate());
+}
+
+function getTime(d)
+{
+	return zeroPad(d.getHours()) + ":" + zeroPad(d.getMinutes()) + ":" +
+		zeroPad(d.getSeconds());
+}
diff --git a/public/javascripts/variant.js b/public/javascripts/variant.js
index 2174c509..3aa2f686 100644
--- a/public/javascripts/variant.js
+++ b/public/javascripts/variant.js
@@ -1,9 +1,9 @@
 new Vue({
 	el: "#variantPage",
-	data: { display: "" }, //do not show anything...
-	// TODO: listen event "show problem", avec le probleme stringifié en arg
-	// Alors: display=game, mode=friend, newGame(fen, turn, ...),
-	//   et set Instructions+Soluce
+	data: {
+		display: "", //do not show anything...
+		problem: undefined, //current problem in view
+	},
 	methods: {
 		toggleDisplay: function(elt) {
 			if (this.display == elt)
@@ -11,5 +11,9 @@ new Vue({
 			else
 				this.display = elt; //show
 		},
+		showProblem: function(problemTxt) {
+			this.problem = JSON.parse(problemTxt);
+			this.display = "game";
+		},
 	},
 });
diff --git a/public/javascripts/variants/Antiking.js b/public/javascripts/variants/Antiking.js
index 2821f3f5..2b07cfbe 100644
--- a/public/javascripts/variants/Antiking.js
+++ b/public/javascripts/variants/Antiking.js
@@ -197,10 +197,9 @@ class AntikingRules extends ChessRules
 			+ "A" + (antikingPos["w"]<7?7-antikingPos["w"]:"");
 		const ranks23_white = (antikingPos["b"]>0?antikingPos["b"]:"") + "a"
 			+ (antikingPos["b"]<7?7-antikingPos["b"]:"") + "/PPPPPPPP";
-		let fen = pieces["b"].join("") + "/" + ranks23_black +
+		return pieces["b"].join("") + "/" + ranks23_black +
 			"/8/8/" +
 			ranks23_white + "/" + pieces["w"].join("").toUpperCase() +
-			" 1111";
-		return fen;
+			" 1111 w";
 	}
 }
diff --git a/public/javascripts/variants/Checkered.js b/public/javascripts/variants/Checkered.js
index 585d20ff..370c6896 100644
--- a/public/javascripts/variants/Checkered.js
+++ b/public/javascripts/variants/Checkered.js
@@ -47,15 +47,17 @@ class CheckeredRules extends ChessRules
 		return !!flags.match(/^[01]{20,20}$/);
 	}
 
-	setFlags(fen)
+	setFlags(fenflags)
 	{
-		super.setFlags(fen); //castleFlags
+		super.setFlags(fenflags); //castleFlags
 		this.pawnFlags =
 		{
-			"w": new Array(8), //pawns can move 2 squares?
-			"b": new Array(8)
+			"w": _.map(_.range(8), i => true), //pawns can move 2 squares?
+			"b": _.map(_.range(8), i => true)
 		};
-		const flags = fen.split(" ")[1].substr(4); //skip first 4 digits, for castle
+		if (!fenflags)
+			return;
+		const flags = fenflags.substr(4); //skip first 4 digits, for castle
 		for (let c of ['w','b'])
 		{
 			for (let i=0; i<8; i++)
@@ -238,7 +240,9 @@ class CheckeredRules extends ChessRules
 
 	static GenRandInitFen()
 	{
-		return ChessRules.GenRandInitFen() + "1111111111111111"; //add 16 pawns flags
+		const randFen = ChessRules.GenRandInitFen();
+		// Add 16 pawns flags:
+		return randFen.replace(" 1111 w", " 11111111111111111111 w");
 	}
 
 	getFlagsFen()
diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js
index cafdadab..276acbc3 100644
--- a/public/javascripts/variants/Grand.js
+++ b/public/javascripts/variants/Grand.js
@@ -275,10 +275,9 @@ class GrandRules extends ChessRules
 			pieces[c][knight2Pos] = 'n';
 			pieces[c][rook2Pos] = 'r';
 		}
-		let fen = pieces["b"].join("") +
+		return pieces["b"].join("") +
 			"/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" +
 			pieces["w"].join("").toUpperCase() +
-			" 1111";
-		return fen;
+			" 1111 w";
 	}
 }
diff --git a/public/javascripts/variants/Loser.js b/public/javascripts/variants/Loser.js
index 98bd9443..2d60d6e8 100644
--- a/public/javascripts/variants/Loser.js
+++ b/public/javascripts/variants/Loser.js
@@ -11,7 +11,7 @@ class LoserRules extends ChessRules
 		return true; //anything is good: no flags
 	}
 
-	setFlags(fen)
+	setFlags(fenflags)
 	{
 		// No castling, hence no flags; but flags defined for compatibility
 		this.castleFlags = { "w":[false,false], "b":[false,false] };
@@ -197,6 +197,6 @@ class LoserRules extends ChessRules
 		return pieces["b"].join("") +
 			"/pppppppp/8/8/8/8/PPPPPPPP/" +
 			pieces["w"].join("").toUpperCase() +
-			" 0000"; //add flags (TODO?!)
+			" 0000 w"; //add flags (TODO?!)
 	}
 }
diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js
index d6fecb70..05fba2f5 100644
--- a/public/javascripts/variants/Ultima.js
+++ b/public/javascripts/variants/Ultima.js
@@ -45,7 +45,7 @@ class UltimaRules extends ChessRules
 		this.epSquares = []; //no en-passant here
 	}
 
-	setFlags(fen)
+	setFlags(fenflags)
 	{
 		// TODO: for compatibility?
 		this.castleFlags = {"w":[false,false], "b":[false,false]};
@@ -608,7 +608,7 @@ class UltimaRules extends ChessRules
 		return pieces["b"].join("") +
 			"/pppppppp/8/8/8/8/PPPPPPPP/" +
 			pieces["w"].join("").toUpperCase() +
-			" 0000"; //TODO: flags?!
+			" 0000 w"; //TODO: flags?!
 	}
 
 	getFlagsFen()
diff --git a/public/javascripts/variants/Wildebeest.js b/public/javascripts/variants/Wildebeest.js
index dcf2d582..9b074108 100644
--- a/public/javascripts/variants/Wildebeest.js
+++ b/public/javascripts/variants/Wildebeest.js
@@ -245,10 +245,9 @@ class WildebeestRules extends ChessRules
 			pieces[c][knight2Pos] = 'n';
 			pieces[c][rook2Pos] = 'r';
 		}
-		let fen = pieces["b"].join("") +
+		return pieces["b"].join("") +
 			"/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
 			pieces["w"].join("").toUpperCase() +
-			" 1111";
-		return fen;
+			" 1111 w";
 	}
 }
diff --git a/views/variant.pug b/views/variant.pug
index 37bd5e7d..b1caabdb 100644
--- a/views/variant.pug
+++ b/views/variant.pug
@@ -14,11 +14,12 @@ block content
 			.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
 				h4.variantpage-title.text-center(v-on:click="toggleDisplay('game')")
 					| #{variant} Game
-				my-game(v-show="display=='game'")
+				my-game(v-show="display=='game'" v-bind:problem="problem")
 			.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
 				h4.variantpage-title.text-center(v-on:click="toggleDisplay('problems')")
 					| #{variant} Problems
-				my-problems(v-show="display=='problems'")
+				my-problems(v-show="display=='problems'"
+					v-on:show-problem="showProblem($event)")
 
 block javascripts
 	script(src="/javascripts/utils/misc.js")
@@ -27,6 +28,7 @@ block javascripts
 	script(src="/javascripts/utils/md5.js")
 	script(src="/javascripts/utils/printDiagram.js")
 	script(src="/javascripts/utils/ajax.js")
+	script(src="/javascripts/utils/datetime.js")
 	script(src="/javascripts/base_rules.js")
 	script(src="/javascripts/variants/" + variant + ".js")
 	script.
-- 
2.44.0