From c11afcdfa8678a27ea6c0822f6d3fef83967701b Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Mon, 18 Jan 2021 01:49:44 +0100 Subject: [PATCH] Fix Konane and Yote computer play + a few bugs --- client/public/images/pieces/Konane/bp.svg | 2 +- client/public/images/pieces/Konane/wp.svg | 2 +- client/src/components/ComputerGame.vue | 2 +- client/src/variants/Konane.js | 24 +++++- client/src/variants/Yote.js | 100 ++++++++++++++++++---- client/src/views/Game.vue | 2 +- 6 files changed, 107 insertions(+), 25 deletions(-) diff --git a/client/public/images/pieces/Konane/bp.svg b/client/public/images/pieces/Konane/bp.svg index dc5717d3..0d6535fd 120000 --- a/client/public/images/pieces/Konane/bp.svg +++ b/client/public/images/pieces/Konane/bp.svg @@ -1 +1 @@ -../Yote/wp.svg \ No newline at end of file +../Yote/bp.svg \ No newline at end of file diff --git a/client/public/images/pieces/Konane/wp.svg b/client/public/images/pieces/Konane/wp.svg index 0d6535fd..dc5717d3 120000 --- a/client/public/images/pieces/Konane/wp.svg +++ b/client/public/images/pieces/Konane/wp.svg @@ -1 +1 @@ -../Yote/bp.svg \ No newline at end of file +../Yote/wp.svg \ No newline at end of file diff --git a/client/src/components/ComputerGame.vue b/client/src/components/ComputerGame.vue index 9cd5f9b1..fbf2d8e7 100644 --- a/client/src/components/ComputerGame.vue +++ b/client/src/components/ComputerGame.vue @@ -119,7 +119,7 @@ export default { }, gameOver: function(score) { this.game.score = score; - this.game.scoreMsg = getScoreMessage(score); + this.game.scoreMsg = getScoreMessage(score, V.ReverseColors); // If comp is thinking, let him finish: if (!this.compThink) this.$emit("game-stopped"); } diff --git a/client/src/variants/Konane.js b/client/src/variants/Konane.js index 1ff77ee9..6acc54ba 100644 --- a/client/src/variants/Konane.js +++ b/client/src/variants/Konane.js @@ -1,4 +1,5 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; +import { randInt } from "@/utils/alea"; export class KonaneRules extends ChessRules { @@ -199,8 +200,27 @@ export class KonaneRules extends ChessRules { else this.captures.pop(); } - static get SEARCH_DEPTH() { - return 4; + getComputerMove() { + const color = this.turn; + let mvArray = []; + let mv = null; + const undoAll = () => { + for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); + }; + // Just play random moves (for now at least. TODO?) + while (this.turn == color) { + let moves = super.getAllValidMoves(); + if (moves.length == 0) { + // Shouldn't happen, but... + undoAll(); + return null; + } + mv = moves[randInt(moves.length)]; + mvArray.push(mv); + this.play(mv); + } + undoAll(); + return (mvArray.length > 1 ? mvArray : mvArray[0]); } getNotation(move) { diff --git a/client/src/variants/Yote.js b/client/src/variants/Yote.js index dc1ef684..0b967861 100644 --- a/client/src/variants/Yote.js +++ b/client/src/variants/Yote.js @@ -1,4 +1,5 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; +import { randInt } from "@/utils/alea"; export class YoteRules extends ChessRules { @@ -28,7 +29,7 @@ export class YoteRules extends ChessRules { // 3) Check reserves if ( !fenParsed.reserve || - !fenParsed.reserve.match(/^([0-9]{1,2},){2,2}$/) + !fenParsed.reserve.match(/^([0-9]{1,2},?){2,2}$/) ) { return false; } @@ -151,6 +152,7 @@ export class YoteRules extends ChessRules { return (x < V.size.x && this.getColor(x, y) != side); } + // TODO: hoverHighlight() would well take an arg "side"... hoverHighlight(x, y) { const L = this.captures.length; if (!this.captures[L-1]) return false; @@ -186,7 +188,14 @@ export class YoteRules extends ChessRules { getReserveMoves(x) { const color = this.turn; - if (this.reserve[color][V.PAWN] == 0) return []; + const L = this.captures.length; + if ( + this.captures[L-1] || + !this.reserve[color] || + this.reserve[color][V.PAWN] == 0 + ) { + return []; + } let moves = []; for (let i = 0; i < V.size.x; i++) { for (let j = 0; j < V.size.y; j++) { @@ -218,8 +227,7 @@ export class YoteRules extends ChessRules { const mv = this.doClick([x, y]); return (!!mv ? [mv] : []); } - if (x >= V.size.x) - return this.getReserveMoves([x, y]); + if (x >= V.size.x) return this.getReserveMoves([x, y]); return this.getPotentialPawnMoves([x, y]); } @@ -229,7 +237,7 @@ export class YoteRules extends ChessRules { const L = this.lastMove.length; const lm = this.lastMove[L-1]; let forbiddenStep = null; - if (!!lm[color]) { + if (!!lm[color] && x == lm[color].end.x && y == lm[color].end.y) { forbiddenStep = [ lm[color].start.x - lm[color].end.x, lm[color].start.y - lm[color].end.y @@ -237,16 +245,17 @@ export class YoteRules extends ChessRules { } const oppCol = V.GetOppCol(color); for (let s of V.steps[V.ROOK]) { - if ( - !!forbiddenStep && - s[0] == forbiddenStep[0] && s[1] == forbiddenStep[1] - ) { - continue; - } const [i1, j1] = [x + s[0], y + s[1]]; if (V.OnBoard(i1, j1)) { - if (this.board[i1][j1] == V.EMPTY) - moves.push(super.getBasicMove([x, y], [i1, j1])); + if (this.board[i1][j1] == V.EMPTY) { + if ( + !forbiddenStep || + s[0] != forbiddenStep[0] || + s[1] != forbiddenStep[1] + ) { + moves.push(super.getBasicMove([x, y], [i1, j1])); + } + } else if (this.getColor(i1, j1) == oppCol) { const [i2, j2] = [i1 + s[0], j1 + s[1]]; if (V.OnBoard(i2, j2) && this.board[i2][j2] == V.EMPTY) { @@ -268,11 +277,22 @@ export class YoteRules extends ChessRules { } getAllPotentialMoves() { - let moves = super.getAllPotentialMoves(); - const color = this.turn; - moves = moves.concat( + const L = this.captures.length; + const color = (this.captures[L-1] ? V.GetOppCol(this.turn) : this.turn); + let potentialMoves = []; + for (let i = 0; i < V.size.x; i++) { + for (let j = 0; j < V.size.y; j++) { + if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color) { + Array.prototype.push.apply( + potentialMoves, + this.getPotentialMovesFrom([i, j]) + ); + } + } + } + potentialMoves = potentialMoves.concat( this.getReserveMoves(V.size.x + (color == "w" ? 0 : 1))); - return moves; + return potentialMoves; } filterValid(moves) { @@ -369,8 +389,50 @@ export class YoteRules extends ChessRules { return "*"; } - static get SEARCH_DEPTH() { - return 4; + getComputerMove() { + const moves = super.getAllValidMoves(); + if (moves.length == 0) return null; + const color = this.turn; + const oppCol = V.GetOppCol(color); + // Capture available? If yes, play it + const captures = moves.filter(m => m.vanish.length == 2); + if (captures.length >= 1) { + const m1 = captures[randInt(captures.length)]; + this.play(m1); + const moves2 = super.getAllValidMoves(); + // Remove a stone which was about to capture one of ours, if possible + let candidates = []; + for (let m2 of moves2) { + const [x, y] = [m2.start.x, m2.start.y]; + for (let s of V.steps[V.ROOK]) { + const [i, j] = [x + 2*s[0], y + 2*s[1]]; + if ( + V.OnBoard(i, j) && + this.board[i][j] == V.EMPTY && + this.board[i - s[0], j - s[1]] != V.EMPTY && + this.getColor(i - s[0], j - s[1]) == color + ) { + candidates.push(m2); + break; + } + } + } + this.undo(m1); + if (candidates.length >= 1) + return [m1, candidates[randInt(candidates.length)]]; + return [m1, moves2[randInt(moves2.length)]]; + } + // Just play a random move, which if possible do not let a capture + let candidates = []; + for (let m of moves) { + this.play(m); + const moves2 = super.getAllValidMoves(); + if (moves2.every(m2 => m2.vanish.length <= 1)) + candidates.push(m); + this.undo(m); + } + if (candidates.length >= 1) return candidates[randInt(candidates.length)]; + return moves[randInt(moves.length)]; } evalPosition() { diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index ef3819b1..1eef3346 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -1602,7 +1602,7 @@ export default { // In corr games, callback to change page only after score is set: gameOver: function(score, scoreMsg, callback) { this.game.score = score; - if (!scoreMsg) scoreMsg = getScoreMessage(score); + if (!scoreMsg) scoreMsg = getScoreMessage(score, V.ReverseColors); this.game.scoreMsg = scoreMsg; document.getElementById("modalRules").checked = false; // Display result in a un-missable way: -- 2.44.0