X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FHidden.js;h=ef5b0bb77380a56fdcc1500db5ebfde4b921ee2a;hb=6f2f94374f1e73c375edf732d9425e575e81fff7;hp=1836352aa292a8bfb19a1a35ecbdf7416960ac90;hpb=241bf8f2a9a2c48d793aeb0b1d20207f6371de70;p=vchess.git diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js index 1836352a..ef5b0bb7 100644 --- a/client/src/variants/Hidden.js +++ b/client/src/variants/Hidden.js @@ -1,12 +1,16 @@ -import { ChessRules } from "@/base_rules"; +import { ChessRules, PiPo, Move } from "@/base_rules"; import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; -export const VariantRules = class HiddenRules extends ChessRules { +export class HiddenRules extends ChessRules { static get HasFlags() { return false; } + static get HasCastle() { + return false; + } + static get HasEnpassant() { return false; } @@ -16,9 +20,9 @@ export const VariantRules = class HiddenRules extends ChessRules { return false; } - // Moves are revealed only when game ends + // Moves are revealed only when game ends, but are highlighted on board static get ShowMoves() { - return "none"; + return "highlight"; } static get HIDDEN_DECODE() { @@ -42,12 +46,39 @@ export const VariantRules = class HiddenRules extends ChessRules { }; } + // Turn a hidden piece or revealed piece into revealed piece: + static Decode(p) { + if (Object.keys(V.HIDDEN_DECODE).includes(p)) + return V.HIDDEN_DECODE[p]; + return p; + } + static get PIECES() { - return ChessRules.PIECES.concat(Object.values(V.HIDDEN_CODE)); + return ChessRules.PIECES.concat(Object.keys(V.HIDDEN_DECODE)); + } + + // Pieces can be hidden :) + getPiece(i, j) { + const piece = this.board[i][j].charAt(1); + if (Object.keys(V.HIDDEN_DECODE).includes(piece)) + return V.HIDDEN_DECODE[piece]; + return piece; + } + + getPpath(b, color, score) { + if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) { + // Supposed to be hidden. + if (score == "*" && (!color || color != b[0])) + return "Hidden/" + b[0] + "p"; + // Else: condition OK to show the piece + return b[0] + V.HIDDEN_DECODE[b[1]]; + } + // The piece is already not supposed to be hidden: + return b; } // Scan board for kings positions (no castling) - scanKingsRooks(fen) { + scanKings(fen) { this.kingPos = { w: [-1, -1], b: [-1, -1] }; const fenRows = V.ParseFen(fen).position.split("/"); for (let i = 0; i < fenRows.length; i++) { @@ -72,21 +103,55 @@ export const VariantRules = class HiddenRules extends ChessRules { } } - getPpath(b, color, score) { - if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) { - // Supposed to be hidden. - if (score == "*" && (!color || color != b[0])) - return "Hidden/" + b[0] + "p"; - // Else: condition OK to show the piece - return b[0] + V.HIDDEN_DECODE[b[1]]; + getBasicMove([sx, sy], [ex, ey], tr) { + if ( + tr && + Object.keys(V.HIDDEN_DECODE).includes(this.board[sx][sy].charAt(1)) + ) { + // The transformed piece is a priori hidden + tr.p = V.HIDDEN_CODE[tr.p]; } - // The piece is already not supposed to be hidden: - return b; + let mv = new Move({ + appear: [ + new PiPo({ + x: ex, + y: ey, + c: tr ? tr.c : this.getColor(sx, sy), + p: tr ? tr.p : this.board[sx][sy].charAt(1) + }) + ], + vanish: [ + new PiPo({ + x: sx, + y: sy, + c: this.getColor(sx, sy), + p: this.board[sx][sy].charAt(1) + }) + ] + }); + + // The opponent piece disappears if we take it + if (this.board[ex][ey] != V.EMPTY) { + mv.vanish.push( + new PiPo({ + x: ex, + y: ey, + c: this.getColor(ex, ey), + p: this.board[ex][ey].charAt(1) + }) + ); + // Pieces are revealed when they capture + mv.appear[0].p = V.Decode(mv.appear[0].p); + } + + return mv; } - //getPotentialMovesFrom: TODO: write + filterValid(moves) { + return moves; + } - // TODO: bishops on different colors, a1 --> h1, h2 --> a2 ? + // Ignore randomness here: placement is always random asymmetric static GenRandInitFen() { let pieces = { w: new Array(8), b: new Array(8) }; // Shuffle pieces + pawns on two first ranks @@ -111,44 +176,51 @@ export const VariantRules = class HiddenRules extends ChessRules { const knight2Pos = positions[randIndex]; positions.splice(randIndex, 1); - // Get random square for queen + // Get random squares for rooks randIndex = randInt(12); + const rook1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = randInt(11); + const rook2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Get random square for queen + randIndex = randInt(10); const queenPos = positions[randIndex]; positions.splice(randIndex, 1); - // Get random squares for pawns - // TODO... + // Get random square for king + randIndex = randInt(9); + const kingPos = positions[randIndex]; + positions.splice(randIndex, 1); - // Rooks and king positions are now fixed, - // because of the ordering rook-king-rook - const rook1Pos = positions[0]; - const kingPos = positions[1]; - const rook2Pos = positions[2]; + // Pawns position are all remaining slots: + for (let p of positions) + pieces[c][p] = "s"; // Finally put the shuffled pieces in the board array - pieces[c][rook1Pos] = "r"; - pieces[c][knight1Pos] = "n"; - pieces[c][bishop1Pos] = "b"; - pieces[c][queenPos] = "q"; - pieces[c][kingPos] = "k"; - pieces[c][bishop2Pos] = "b"; - pieces[c][knight2Pos] = "n"; - pieces[c][rook2Pos] = "r"; + pieces[c][rook1Pos] = "u"; + pieces[c][knight1Pos] = "o"; + pieces[c][bishop1Pos] = "c"; + pieces[c][queenPos] = "t"; + pieces[c][kingPos] = "l"; + pieces[c][bishop2Pos] = "c"; + pieces[c][knight2Pos] = "o"; + pieces[c][rook2Pos] = "u"; } - return ( - pieces["b"].join("") + - "/pppppppp/8/8/8/8/PPPPPPPP/" + - pieces["w"].join("").toUpperCase() + - " w 0" - ); + let upFen = pieces["b"].join(""); + upFen = upFen.substr(0,8) + "/" + upFen.substr(8).split("").reverse().join(""); + let downFen = pieces["b"].join("").toUpperCase(); + downFen = downFen.substr(0,8) + "/" + downFen.substr(8).split("").reverse().join(""); + return upFen + "/8/8/8/8/" + downFen + " w 0"; } getCheckSquares() { return []; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if ( move.vanish.length >= 2 && [V.KING,V.HIDDEN_CODE[V.KING]].includes(move.vanish[1].p) @@ -158,8 +230,8 @@ export const VariantRules = class HiddenRules extends ChessRules { } } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); const c = move.vanish[0].c; const oppCol = V.GetOppCol(c); if (this.kingPos[oppCol][0] < 0) @@ -178,8 +250,77 @@ export const VariantRules = class HiddenRules extends ChessRules { } getComputerMove() { - // Just return a random move. TODO: something smarter... - const moves = this.getAllValidMoves(); - return moves[randInt(moves.length)]; + const color = this.turn; + let moves = this.getAllValidMoves(); + for (let move of moves) { + move.eval = 0; //a priori... + + // Can I take something ? If yes, do it with some probability + if (move.vanish.length == 2 && move.vanish[1].c != color) { + // OK this isn't a castling move + const myPieceVal = V.VALUES[move.appear[0].p]; + const hisPieceVal = Object.keys(V.HIDDEN_DECODE).includes(move.vanish[1].p) + ? undefined + : V.VALUES[move.vanish[1].p]; + if (!hisPieceVal) { + // Opponent's piece is unknown: do not take too much risk + move.eval = -myPieceVal + 1.5; //so that pawns always take + } + // Favor captures + else if (myPieceVal <= hisPieceVal) + move.eval = hisPieceVal - myPieceVal + 1; + 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. + move.eval = hisPieceVal - myPieceVal; + } + } else { + // If no capture, favor small step moves, + // but sometimes move the knight anyway + const penalty = V.Decode(move.vanish[0].p) != V.KNIGHT + ? Math.abs(move.end.x - move.start.x) + Math.abs(move.end.y - move.start.y) + : (Math.random() < 0.5 ? 3 : 1); + move.eval -= penalty / (V.size.x + V.size.y - 1); + } + + // TODO: also favor movements toward the center? + } + + moves.sort((a, b) => b.eval - a.eval); + let candidates = [0]; + for (let j = 1; j < moves.length && moves[j].eval == moves[0].eval; j++) + candidates.push(j); + return moves[candidates[randInt(candidates.length)]]; + } + + getNotation(move) { + // Translate final square + const finalSquare = V.CoordsToSquare(move.end); + + const piece = this.getPiece(move.start.x, move.start.y); + if (piece == V.PAWN) { + // Pawn move + let notation = ""; + if (move.vanish.length > move.appear.length) { + // Capture + const startColumn = V.CoordToColumn(move.start.y); + notation = startColumn + "x" + finalSquare; + } + else notation = finalSquare; + if (move.appear.length > 0 && !["p","s"].includes(move.appear[0].p)) { + // Promotion + const appearPiece = V.Decode(move.appear[0].p); + notation += "=" + appearPiece.toUpperCase(); + } + return notation; + } + // Piece movement + return ( + piece.toUpperCase() + + (move.vanish.length > move.appear.length ? "x" : "") + + finalSquare + ); } };