From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sat, 17 Nov 2018 17:11:18 +0000 (+0100)
Subject: Highlight king in red if undercheck + some improvements
X-Git-Url: https://git.auder.net/variants/img/pieces/%7B%7B%20asset%28%27mixstore/images/R.css?a=commitdiff_plain;h=e64a4effa6b357addc3253504870cf1a6fc29977;p=vchess.git

Highlight king in red if undercheck + some improvements
---

diff --git a/TODO b/TODO
index 92929ac7..dafd7c0b 100644
--- a/TODO
+++ b/TODO
@@ -1,2 +1,4 @@
-End of game a bit too verbose: should just say "Black win" ... + show PGN
 Styles must be improved (full width for smartphones, selectable text for PGN...)
+Tooltip text should fade (even when mouse stay on it, especially for small screens)
+Checkered stage 2: switch button at reserve position.
+If a played disconnect right after opponent sent a move, it might be never received: secure this
diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js
index de1a0d0f..9a4ef912 100644
--- a/public/javascripts/base_rules.js
+++ b/public/javascripts/base_rules.js
@@ -517,6 +517,32 @@ class ChessRules
 		// No: if happen on last 1/2 move, could lead to forbidden moves, wrong evals
 		return this.filterValid(potentialMoves);
 	}
+	
+	// Stop at the first move found
+	atLeastOneMove(color)
+	{
+		const oppCol = this.getOppCol(color);
+		let [sizeX,sizeY] = VariantRules.size;
+		for (var i=0; i<sizeX; i++)
+		{
+			for (var j=0; j<sizeY; j++)
+			{
+				if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
+				{
+					const moves = this.getPotentialMovesFrom([i,j]);
+					if (moves.length > 0)
+					{
+						for (let i=0; i<moves.length; i++)
+						{
+							if (this.filterValid([moves[i]]).length > 0)
+								return true;
+						}
+					}
+				}
+			}
+		}
+		return false;
+	}
 
 	// Check if pieces of color 'color' are attacking square x,y
 	isAttacked(sq, color)
@@ -676,12 +702,15 @@ class ChessRules
 		this.movesCount++;
 	}
 
-	undo(move)
+	undo(move, ingame)
 	{
 		VariantRules.UndoOnBoard(this.board, move);
 		this.epSquares.pop();
 		this.movesCount--;
 
+		if (!!ingame)
+			this.moves.pop();
+
 		// Update king position, and reset stored/computed flags
 		const c = this.getColor(move.start.x,move.start.y);
 		if (this.getPiece(move.start.x,move.start.y) == VariantRules.KING)
@@ -709,8 +738,7 @@ class ChessRules
 			}
 		}
 
-		// TODO: not required to generate ALL: just need one (callback ? hook ? ...)
-		if (this.getAllValidMoves(color).length > 0)
+		if (this.atLeastOneMove(color))
 		{
 			// game not over
 			return "*";
@@ -779,14 +807,14 @@ class ChessRules
 		moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
 
 		// TODO: show current analyzed move for depth 3, allow stopping eval (return moves1[0])
-//		for (let i=0; i<moves1.length; i++)
-//		{
-//			this.play(moves1[i]);
-//			// 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
-//			moves1[i].eval = 0.1*moves1[i].eval + this.alphabeta(oppCol, color, 2, -1000, 1000);
-//			this.undo(moves1[i]);
-//		}
-//		moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
+		for (let i=0; i<moves1.length; i++)
+		{
+			this.play(moves1[i]);
+			// 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
+			moves1[i].eval = 0.1*moves1[i].eval + this.alphabeta(oppCol, color, 2, -1000, 1000);
+			this.undo(moves1[i]);
+		}
+		moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
 
 		let candidates = [0]; //indices of candidates moves
 		for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
@@ -798,7 +826,7 @@ class ChessRules
 
 	alphabeta(color, oppCol, depth, alpha, beta)
   {
-		let moves = this.getAllValidMoves(color);
+		const moves = this.getAllValidMoves(color);
 		if (moves.length == 0)
 		{
 			switch (this.checkGameEnd(color))
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 81ced5b4..125aeea1 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -16,6 +16,7 @@ Vue.component('my-game', {
 			oppConnected: false,
 			seek: false,
 			fenStart: "",
+			incheck: false,
 		};
 	},
 	render(h) {
@@ -35,6 +36,8 @@ Vue.component('my-game', {
 				{
 					on: {
 						click: () => {
+							if (this.mode == "human")
+								return; //no newgame while playing
 							if (this.seek)
 								delete localStorage["newgame"]; //cancel game seek
 							else
@@ -55,7 +58,13 @@ Vue.component('my-game', {
 				[h('i', { 'class': { "material-icons": true } }, "accessibility")]),
 			h('button',
 				{
-					on: { click: () => this.newGame("computer") },
+					on: {
+						click: () => {
+							if (this.mode == "human")
+								return; //no newgame while playing
+							this.newGame("computer");
+						}
+					},
 					attrs: { "aria-label": 'New game VS computer' },
 					'class': {
 						"tooltip":true,
@@ -162,7 +171,9 @@ Vue.component('my-game', {
 								);
 							}
 							const lm = this.vr.lastMove;
-							const highlight = !!lm && _.isMatch(lm.end, {x:ci,y:cj}); //&& _.isMatch(lm.start, {x:ci,y:cj})
+							const highlight = !!lm && _.isMatch(lm.end, {x:ci,y:cj});
+							const incheck = this.incheck
+								&& _.isEqual(this.vr.kingPos[this.vr.turn], [ci,cj]);
 							return h(
 								'div',
 								{
@@ -171,6 +182,7 @@ Vue.component('my-game', {
 										'light-square': !highlight && (i+j)%2==0,
 										'dark-square': !highlight && (i+j)%2==1,
 										'highlight': highlight,
+										'incheck': incheck,
 									},
 									attrs: {
 										id: this.getSquareId({x:ci,y:cj}),
@@ -483,6 +495,14 @@ Vue.component('my-game', {
 				this.oppConnected = true;
 				this.mycolor = color;
 				this.seek = false;
+				if (!!moves && moves.length > 0) //imply continuation
+				{
+					const oppCol = this.vr.turn;
+					const lastMove = moves[moves.length-1];
+					this.vr.undo(lastMove);
+					this.incheck = this.vr.underCheck(lastMove, oppCol);
+					this.vr.play(lastMove, "ingame");
+				}
 				delete localStorage["newgame"];
 				this.setStorage(); //in case of interruptions
 			}
@@ -610,6 +630,8 @@ Vue.component('my-game', {
 				this.animateMove(move);
 				return;
 			}
+			const oppCol = this.vr.getOppCol(this.vr.turn);
+			this.incheck = this.vr.underCheck(move, oppCol); //is opponent in check?
 			// Not programmatic, or animation is over
 			if (this.mode == "human" && this.vr.turn == this.mycolor)
 			{
diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass
index 786d54e3..6a8da838 100644
--- a/public/stylesheets/variant.sass
+++ b/public/stylesheets/variant.sass
@@ -7,10 +7,10 @@
   background-color: lightgrey
   font-weight: bold
 
-.playing
+.playing, button.playing:hover
   background-color: #ffcc99
 
-.seek
+.seek, button.seek:hover
   background-color: #cc99ff
 
 // https://stackoverflow.com/questions/5445491/height-equal-to-dynamic-width-css-fluid-layout
@@ -103,7 +103,10 @@ figure.diagram-container > figcaption
   background-color: #b58863
 
 .highlight
-  background-color: #00cc00
+  background-color: #00cc66
+
+.incheck
+  background-color: #cc3300
 
 .light-square-diag
   background-color: #e5e5ca