From dfb4afc10d56715b80f753b1273738bd11a309dd Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Fri, 16 Nov 2018 17:33:27 +0100
Subject: [PATCH] Store moves, show PGN at end of game

---
 public/javascripts/base_rules.js      | 35 ++++++++--
 public/javascripts/components/game.js | 94 ++++++++++++++++++---------
 2 files changed, 93 insertions(+), 36 deletions(-)

diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index 576bdd9b..0a5c0e72 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -47,9 +47,9 @@ class ChessRules
 	// INITIALIZATION
 
 	// fen = "position flags epSquare movesCount"
-	constructor(fen)
+	constructor(fen, moves)
 	{
-		this.moves = [];
+		this.moves = moves;
 		// Use fen string to initialize variables, flags and board
 		this.initVariables(fen);
 		this.flags = VariantRules.GetFlags(fen);
@@ -665,12 +665,15 @@ class ChessRules
 		move.flags = JSON.stringify(this.flags); //TODO: less costly
 		this.updateVariables(move);
 
+		if (!!ingame)
+		{
+			move.notation = this.getNotation(move);
+			this.moves.push(move);
+		}
+
 		this.epSquares.push( this.getEpSquare(move) );
 		VariantRules.PlayOnBoard(this.board, move);
 		this.movesCount++;
-
-		if (!!ingame)
-			this.moves.push(move);
 	}
 
 	undo(move)
@@ -1007,4 +1010,26 @@ class ChessRules
 			return piece.toUpperCase() + (move.vanish.length > 1 ? "x" : "") + finalSquare;
 		}
 	}
+
+	// The score is already computed when calling this function
+	getPGN(mycolor, score)
+	{
+		let pgn = "";
+		pgn += '[Site "vchess.club"]<br>';
+		const d = new Date();
+		pgn += '[Date "' + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + '"]<br>';
+		pgn += '[White "' + (mycolor=='w'?'Myself':'Anonymous') + '"]<br>';
+		pgn += '[Black "' + (mycolor=='b'?'Myself':'Anonymous') + '"]<br>';
+		pgn += '[Result "' + score + '"]<br>';
+
+		for (let i=0; i<this.moves.length; i++)
+		{
+			if (i % 2 == 0)
+				pgn += ((i/2)+1) + ".";
+			pgn += this.moves[i].notation + " ";
+		}
+
+		pgn += score;
+		return pgn;
+	}
 }
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index f5db3378..592a7001 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -1,4 +1,4 @@
-// TODO: use indexedDB instead of localStorage: more flexible.
+// TODO: use indexedDB instead of localStorage? (more flexible...)
 
 Vue.component('my-game', {
 	data: function() {
@@ -10,7 +10,7 @@ Vue.component('my-game', {
 			start: {}, //pixels coordinates + id of starting square (click or drag)
 			selectedPiece: null, //moving piece (or clicked piece)
 			conn: null, //socket messages
-			endofgame: "", //end of game message
+			score: "*", //'*' means 'unfinished'
 			mode: "idle", //human, computer or idle (when not playing)
 			oppid: "", //opponent ID in case of HH game
 			oppConnected: false,
@@ -208,6 +208,51 @@ Vue.component('my-game', {
 	//				);
 	//				elementArray.push(reserve);
 	//			}
+			let eogMessage = "Unfinished";
+			switch (this.score)
+			{
+				case "1-0":
+					eogMessage = "White win";
+					break;
+				case "0-1":
+					eogMessage = "Black win";
+					break;
+				case "1/2":
+					eogMessage = "Draw";
+					break;
+			}
+			let elemsOfEog =
+			[
+				h('label',
+					{
+						attrs: { "for": "modal-control" },
+						"class": { "modal-close": true },
+					}
+				),
+				h('h3',
+					{
+						"class": { "section": true },
+						domProps: { innerHTML: "End of game" },
+					}
+				),
+				h('p',
+					{
+						"class": { "section": true },
+						domProps: { innerHTML: eogMessage },
+					}
+				)
+			];
+			if (this.score != "*")
+			{
+				elemsOfEog.push(
+					h('p', //'textarea', //TODO: selectable!
+						{
+							domProps: { innerHTML: this.vr.getPGN(this.mycolor, this.score) },
+							//attrs: { "readonly": true },
+						}
+					)
+				);
+			}
 			const modalEog = [
 				h('input',
 					{
@@ -223,26 +268,7 @@ Vue.component('my-game', {
 							{
 								"class": { "card": true, "smallpad": true },
 							},
-							[
-								h('label',
-									{
-										attrs: { "for": "modal-control" },
-										"class": { "modal-close": true },
-									}
-								),
-								h('h3',
-									{
-										"class": { "section": true },
-										domProps: { innerHTML: "End of game" },
-									}
-								),
-								h('p',
-									{
-										"class": { "section": true },
-										domProps: { innerHTML: this.endofgame },
-									}
-								)
-							]
+							elemsOfEog
 						)
 					]
 				)
@@ -332,8 +358,11 @@ Vue.component('my-game', {
 			if (continuation)
 			{
 				// TODO: check FEN integrity with opponent
-				this.newGame("human", localStorage.getItem("fen"),
-					localStorage.getItem("mycolor"), localStorage.getItem("oppid"), true);
+				const fen = localStorage.getItem("fen");
+				const mycolor = localStorage.getItem("mycolor");
+				const oppid = localStorage.getItem("oppid");
+				const moves = JSON.parse(localStorage.getItem("moves"));
+				this.newGame("human", fen, mycolor, oppid, moves, true);
 				// Send ping to server, which answers pong if opponent is connected
 				this.conn.send(JSON.stringify({code:"ping", oppid:this.oppId}));
 			}
@@ -358,7 +387,7 @@ Vue.component('my-game', {
 					this.oppConnected = true;
 					break;
 				case "resign": //..you won!
-					this.endGame("Victory!");
+					this.endGame(this.mycolor=="w"?"1-0":"0-1");
 					break;
 				// TODO: also use (dis)connect info to count online players
 				case "connect":
@@ -380,11 +409,11 @@ Vue.component('my-game', {
 		this.conn.onclose = socketCloseListener;
 	},
 	methods: {
-		endGame: function(message) {
-			this.endofgame = message;
+		endGame: function(score) {
+			this.score = score;
 			let modalBox = document.getElementById("modal-control");
 			modalBox.checked = true;
-			setTimeout(() => { modalBox.checked = false; }, 2000);
+			//setTimeout(() => { modalBox.checked = false; }, 2000); //disabled, to show PGN
 			if (this.mode == "human")
 				this.clearStorage();
 			this.mode = "idle";
@@ -399,7 +428,7 @@ Vue.component('my-game', {
 					return; //resign failed for some reason...
 				}
 			}
-			this.endGame("Try again!");
+			this.endGame(this.mycolor=="w"?"0-1":"1-0");
 		},
 		updateStorage: function() {
 			if (!localStorage.getItem("myid"))
@@ -410,6 +439,7 @@ Vue.component('my-game', {
 				localStorage.setItem("oppid", this.oppid);
 			}
 			localStorage.setItem("fen", this.vr.getFen());
+			localStorage.setItem("moves", JSON.stringify(this.vr.moves));
 		},
 		clearStorage: function() {
 			delete localStorage["variant"];
@@ -417,10 +447,12 @@ Vue.component('my-game', {
 			delete localStorage["mycolor"];
 			delete localStorage["oppid"];
 			delete localStorage["fen"];
+			delete localStorage["moves"];
 		},
-		newGame: function(mode, fenInit, color, oppId, continuation) {
+		newGame: function(mode, fenInit, color, oppId, moves, continuation) {
 			const fen = fenInit || VariantRules.GenRandInitFen();
 			console.log(fen); //DEBUG
+			this.score = "*";
 			if (mode=="human" && !oppId)
 			{
 				// Send game request and wait..
@@ -435,7 +467,7 @@ Vue.component('my-game', {
 				setTimeout(() => { modalBox.checked = false; }, 2000);
 				return;
 			}
-			this.vr = new VariantRules(fen);
+			this.vr = new VariantRules(fen, moves || []);
 			this.mode = mode;
 			if (mode=="human")
 			{
-- 
2.44.0