From 5915f72002ae63b04620cebe47adf778174b1bee Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Wed, 26 Dec 2018 12:17:48 +0100 Subject: [PATCH] Implemented a very basic DarkBot + a few fixes + advance on rules --- public/javascripts/base_rules.js | 4 +- public/javascripts/components/game.js | 15 ++- public/javascripts/components/rules.js | 1 + public/javascripts/utils/printDiagram.js | 40 ++++++- public/javascripts/variants/Dark.js | 144 +++++++++++++++++++++++ views/rules/Dark/en.pug | 28 ++++- views/rules/Marseille/en.pug | 17 ++- views/rules/Upsidedown/en.pug | 19 ++- 8 files changed, 232 insertions(+), 36 deletions(-) diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 389ba342..891948b9 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -137,9 +137,9 @@ class ChessRules } // d --> 3 (column letter to number) - static ColumnToCoord(colnum) + static ColumnToCoord(column) { - return String.fromCharCode(97 + colnum); + return column.charCodeAt(0) - 97; } // a4 --> {x:3,y:0} diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js index 94340e66..6627252f 100644 --- a/public/javascripts/components/game.js +++ b/public/javascripts/components/game.js @@ -62,9 +62,8 @@ Vue.component('my-game', { }, [h('i', { 'class': { "material-icons": true } }, "accessibility")]) ); - if (variant != "Dark" && - (["idle","computer","friend"].includes(this.mode) - || ["friend","human"].includes(this.mode) && this.score != "*")) + if (["idle","computer","friend"].includes(this.mode) + || (this.mode == "human" && this.score != "*")) { actionArray.push( h('button', @@ -81,9 +80,8 @@ Vue.component('my-game', { [h('i', { 'class': { "material-icons": true } }, "computer")]) ); } - if (variant != "Dark" && - (["idle","friend"].includes(this.mode) - || ["computer","human"].includes(this.mode) && this.score != "*")) + if (variant != "Dark" && (["idle","friend"].includes(this.mode) + || (["computer","human"].includes(this.mode) && this.score != "*"))) { actionArray.push( h('button', @@ -1130,12 +1128,13 @@ Vue.component('my-game', { // before they appear on page: const delay = Math.max(500-(Date.now()-self.timeStart), 0); setTimeout(() => { + const animate = (variant!="Dark" ? "animate" : null); if (self.mode == "computer") //warning: mode could have changed! - self.play(compMove[0], "animate"); + self.play(compMove[0], animate); if (compMove.length == 2) setTimeout( () => { if (self.mode == "computer") - self.play(compMove[1], "animate"); + self.play(compMove[1], animate); }, 750); }, delay); } diff --git a/public/javascripts/components/rules.js b/public/javascripts/components/rules.js index d8aaa0fc..1a597878 100644 --- a/public/javascripts/components/rules.js +++ b/public/javascripts/components/rules.js @@ -25,6 +25,7 @@ Vue.component('my-rules', { position: fenParts[0], marks: fenParts[1], orientation: fenParts[2], + shadow: fenParts[3], }; }, }, diff --git a/public/javascripts/utils/printDiagram.js b/public/javascripts/utils/printDiagram.js index 61c726eb..b7282fee 100644 --- a/public/javascripts/utils/printDiagram.js +++ b/public/javascripts/utils/printDiagram.js @@ -7,18 +7,46 @@ function getDiagram(args) const board = VariantRules.GetBoard(args.position); const orientation = args.orientation || "w"; let markArray = []; - if (!!args.marks) + if (!!args.marks && args.marks != "-") { // Turn (human) marks into coordinates markArray = doubleArray(sizeX, sizeY, false); let squares = args.marks.split(","); for (let i=0; i=0 && j"; + ((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") + + (shadowArray.length > 0 && shadowArray[i][j] ? " in-shadow" : "") + + "'>"; if (board[i][j] != V.EMPTY) { boardDiv += ""; } - if (!!args.marks && markArray[i][j]) + if (markArray.length > 0 && markArray[i][j]) boardDiv += ""; boardDiv += ""; } diff --git a/public/javascripts/variants/Dark.js b/public/javascripts/variants/Dark.js index e3e093b1..96f50de7 100644 --- a/public/javascripts/variants/Dark.js +++ b/public/javascripts/variants/Dark.js @@ -126,6 +126,150 @@ class DarkRules extends ChessRules { return 500; //checkmates evals may be slightly below 1000 } + + // In this special situation, we just look 1 half move ahead + getComputerMove() + { + const maxeval = V.INFINITY; + const color = this.turn; + const oppCol = this.getOppCol(color); + const pawnShift = (color == "w" ? -1 : 1); + const kp = this.kingPos[color]; + + // Do not cheat: the current enlightment is all we can see + const myLight = JSON.parse(JSON.stringify(this.enlightened[color])); + + // Can a slider on (i,j) apparently take my king? + // NOTE: inaccurate because assume yes if some squares are shadowed + const sliderTake = ([i,j], piece) => { + let step = undefined; + if (piece == V.BISHOP) + { + if (Math.abs(kp[0] - i) == Math.abs(kp[1] - j)) + { + step = + [ + (i-kp[0]) / Math.abs(i-kp[0]), + (j-kp[1]) / Math.abs(j-kp[1]) + ]; + } + } + else if (piece == V.ROOK) + { + if (kp[0] == i) + step = [0, (j-kp[1]) / Math.abs(j-kp[1])]; + else if (kp[1] == j) + step = [(i-kp[0]) / Math.abs(i-kp[0]), 0]; + } + if (!step) + return false; + // Check for obstacles + let obstacle = false; + for ( + let x=kp[0]+step[0], y=kp[1]+step[1]; + x != i && y != j; + x += step[0], y+= step[1]) + { + if (myLight[x][y] && this.board[x][y] != V.EMPTY) + { + obstacle = true; + break; + } + } + if (!obstacle) + return true; + return false; + }; + + // Do I see something which can take my king ? + const kingThreats = () => { + for (let i=0; i= 0 && kingThreats()) + { + // We didn't take opponent king, and our king will be captured: bad + move.eval = -maxeval; + } + this.undo(move); + if (!!move.eval) + continue; + + move.eval = 0; //a priori... + + // Can I take something ? If yes, do it if it seems good... + if (move.vanish.length == 2 && move.vanish[1].c != color) //avoid castle + { + const myPieceVal = V.VALUES[move.appear[0].p]; + const hisPieceVal = V.VALUES[move.vanish[1].p]; + if (myPieceVal <= hisPieceVal) + move.eval = hisPieceVal - myPieceVal + 2; //favor captures + else + { + // Taking a pawn with minor piece, + // or minor piece or pawn with a rook, + // or anything but a queen with a queen, + // or anything with a king. + // ==> Do it at random, although + // this is clearly inferior to what a human can deduce... + move.eval = (Math.random() < 0.5 ? 1 : -1); + } + } + } + + // TODO: also need to implement the case when an opponent piece (in light) + // is threatening something - maybe not the king, but e.g. pawn takes rook. + + moves.sort((a,b) => b.eval - a.eval); + let candidates = [0]; + for (let j=1; j