X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FBario.js;h=3564e7487b22344dcbe416ddc621392cb16c6b2a;hb=fbc3e6f984492143625e438436f9c0a94e42c713;hp=5b5b33c39ba28326448a6dd4b1571758fba9c891;hpb=2a0672a98b555f0fecfd951d583e69419769d411;p=vchess.git diff --git a/client/src/variants/Bario.js b/client/src/variants/Bario.js index 5b5b33c3..3564e748 100644 --- a/client/src/variants/Bario.js +++ b/client/src/variants/Bario.js @@ -2,13 +2,11 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; -// TODO: issue with undo of specialisation to cover check, subTurn decremented to 0 - export class BarioRules extends ChessRules { // Does not really seem necessary (although the author mention it) // Instead, first move = pick a square for the king. - static get HasCastle() { + static get HasFlags() { return false; } @@ -37,7 +35,7 @@ export class BarioRules extends ChessRules { ); } - hoverHighlight(x, y) { + hoverHighlight([x, y]) { const c = this.turn; return ( this.movesCount <= 1 && @@ -48,6 +46,14 @@ export class BarioRules extends ChessRules { ); } + onlyClick([x, y]) { + return ( + this.movesCount <= 1 || + // TODO: next line theoretically shouldn't be required... + (this.movesCount == 2 && this.getColor(x, y) != this.turn) + ); + } + // Initiate the game by choosing a square for the king: doClick(square) { const c = this.turn; @@ -64,8 +70,10 @@ export class BarioRules extends ChessRules { appear: [ new PiPo({ x: square[0], y: square[1], c: c, p: V.KING }) ], - vanish: [], - start: { x: -1, y: -1 }, + vanish: [ + new PiPo({ x: square[0], y: square[1], c: c, p: V.UNDEFINED }) + ], + start: { x: -1, y: -1 } }); } @@ -111,8 +119,8 @@ export class BarioRules extends ChessRules { getReserveFen() { let counts = new Array(8); for (let i = 0; i < V.RESERVE_PIECES.length; i++) { - counts[i] = this.reserve["w"][V.PIECES[i]]; - counts[4 + i] = this.reserve["b"][V.PIECES[i]]; + counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]]; + counts[4 + i] = this.reserve["b"][V.RESERVE_PIECES[i]]; } return counts.join(""); } @@ -140,7 +148,7 @@ export class BarioRules extends ChessRules { } static GenRandInitFen() { - return "8/pppppppp/8/8/8/8/PPPPPPPP/8 w 0 - 22212221 -"; + return "uuuuuuuu/pppppppp/8/8/8/8/PPPPPPPP/UUUUUUUU w 0 - 22212221 -"; } setOtherVariables(fen) { @@ -194,17 +202,15 @@ export class BarioRules extends ChessRules { if (this.subTurn == 0) { const L = this.captureUndefined.length; const cu = this.captureUndefined[L-1]; - return ( + return [ + // Nothing changes on the board, just mark start.p for reserve update new Move({ - appear: [ - new PiPo({ x: cu.x, y: cu.y, c: color, p: p }) - ], - vanish: [ - new PiPo({ x: cu.x, y: cu.y, c: color, p: V.UNDEFINED }) - ], - start: { x: x, y: y } + appear: [], + vanish: [], + start: { x: x, y: y, p: p }, + end: { x: cu.x, y: cu.y } }) - ); + ]; } // or, subTurn == 1 => target any undefined piece that we own. let moves = []; @@ -252,10 +258,25 @@ export class BarioRules extends ChessRules { return super.getPotentialMovesFrom([x, y]); } - getAllValidMoves() { + getAllPotentialMoves() { + const color = this.turn; + if (this.movesCount <= 1) { + // Just put the king on the board + const firstRank = (color == 'w' ? 7 : 0); + return [...Array(8)].map((x, j) => { + return new Move({ + appear: [ + new PiPo({ x: firstRank, y: j, c: color, p: V.KING }) + ], + vanish: [ + new PiPo({ x: firstRank, y: j, c: color, p: V.UNDEFINED }) + ], + start: { x: -1, y: -1 } + }); + }); + } const getAllReserveMoves = () => { let moves = []; - const color = this.turn; for (let i = 0; i < V.RESERVE_PIECES.length; i++) { moves = moves.concat( this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i]) @@ -271,12 +292,33 @@ export class BarioRules extends ChessRules { } filterValid(moves) { + if (this.movesCount <= 1) return moves; const color = this.turn; return moves.filter(m => { - if (m.vanish.length == 0) return true; + if (m.vanish.length == 0) { + // subTurn == 0: need to check if a move exists at subTurn == 1 + this.play(m); + const res = this.filterValid(this.getAllPotentialMoves()).length > 0; + this.undo(m); + return res; + } const start = { x: m.vanish[0].x, y: m.vanish[0].y }; const end = { x: m.appear[0].x, y: m.appear[0].y }; - if (start.x == end.x && start.y == end.y) return true; //unfinished turn + if (start.x == end.x && start.y == end.y) { + // Unfinished turn: require careful check + this.play(m); + let res = false; + if (this.subTurn == 1) + // Can either play a move, or specialize a piece + res = this.filterValid(this.getAllPotentialMoves()).length > 0; + else { + // subTurn == 2: can only play a specialized piece + res = this.filterValid( + this.getPotentialMovesFrom([m.end.x, m.end.y])).length > 0; + } + this.undo(m); + return res; + } this.play(m); const res = !this.underCheck(color); this.undo(m); @@ -294,9 +336,10 @@ export class BarioRules extends ChessRules { } return false; }; - if (this.subTurn == 0) return true; //always one reserve for an undefined - if (!super.atLeastOneMove()) return atLeastOneReserveMove(); - return true; + if (this.subTurn == 0) return atLeastOneReserveMove(); + const canMoveSomething = super.atLeastOneMove(); + if (this.subTurn == 2) return canMoveSomething; + return (canMoveSomething || atLeastOneReserveMove()); } underCheck(color) { @@ -326,7 +369,7 @@ export class BarioRules extends ChessRules { ]; let [i, j] = [x1 + step[0], y1 + step[1]]; while (i != x2 || j != y2) { - if (this.board[i][j] != V.EMPTY) return false; + if (!V.OnBoard(i, j) || this.board[i][j] != V.EMPTY) return false; i += step[0]; j += step[1]; } @@ -355,7 +398,13 @@ export class BarioRules extends ChessRules { return false; } + getCheckSquares() { + if (this.movesCount <= 2) return []; + return super.getCheckSquares(); + } + play(move) { + move.turn = [this.turn, this.subTurn]; //easier undo (TODO?) const toNextPlayer = () => { V.PlayOnBoard(this.board, move); this.turn = V.GetOppCol(this.turn); @@ -364,42 +413,37 @@ export class BarioRules extends ChessRules { this.movesCount++; this.postPlay(move); }; - if (move.vanish.length == 0) { - toNextPlayer(); - return; + if (this.movesCount <= 1) toNextPlayer(); + else if (move.vanish.length == 0) { + // Removal (subTurn == 0 --> 1) + this.reserve[this.turn][move.start.p]--; + this.subTurn++; } - const start = { x: move.vanish[0].x, y: move.vanish[0].y }; - const end = { x: move.appear[0].x, y: move.appear[0].y }; - if (start.x == end.x && start.y == end.y) { - // Specialisation (subTurn == 1 before 2), or Removal (subTurn == 0). - // In both cases, turn not over, and a piece removed from reserve - this.reserve[this.turn][move.appear[0].p]--; - if (move.appear[0].c == move.vanish[0].c) { - // Specialisation: play "move" on board + else { + const start = { x: move.vanish[0].x, y: move.vanish[0].y }; + const end = { x: move.appear[0].x, y: move.appear[0].y }; + if (start.x == end.x && start.y == end.y) { + // Specialisation (subTurn == 1 before 2) + this.reserve[this.turn][move.appear[0].p]--; V.PlayOnBoard(this.board, move); this.definitions.push(move.end); + this.subTurn++; + } + else { + // Normal move (subTurn 1 or 2: change turn) + this.epSquares.push(this.getEpSquare(move)); + toNextPlayer(); } - this.subTurn++; - } - else { - // Normal move (subTurn 1 or 2: change turn) - this.epSquares.push(this.getEpSquare(move)); - toNextPlayer(); } } postPlay(move) { const color = V.GetOppCol(this.turn); - if (move.vanish.length == 0) { - this.kingPos[color] = [move.end.x, move.end.y]; - const firstRank = (color == 'w' ? 7 : 0); - for (let j = 0; j < 8; j++) { - if (j != move.end.y) this.board[firstRank][j] = color + V.UNDEFINED; - } - } + if (this.movesCount <= 2) this.kingPos[color] = [move.end.x, move.end.y]; else { if (move.vanish.length == 2 && move.vanish[1].p == V.UNDEFINED) this.captureUndefined.push(move.end); + else this.captureUndefined.push(null); if (move.appear[0].p == V.KING) super.postPlay(move); else { // If now all my pieces are defined, back to undefined state, @@ -417,31 +461,55 @@ export class BarioRules extends ChessRules { }) ) { const piecesList = [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]; - let myPieces = {}; + const oppCol = this.turn; + let definedPieces = { w: {}, b: {} }; for (let i=0; i<8; i++) { for (let j=0; j<8; j++) { - if ( - this.board[i][j] != V.EMPTY && - this.getColor(i, j) == color - ) { + if (this.board[i][j] != V.EMPTY) { const p = this.getPiece(i, j); - if (piecesList.includes(p)) - myPieces[p] = (!myPieces[p] ? 1 : myPieces[p] + 1); + const c = this.getColor(i, j); + if (piecesList.includes(p)) { + definedPieces[c][p] = + (!definedPieces[c][p] ? 1 : definedPieces[c][p] + 1); + } } } } - const pk = Object.keys(myPieces); - if (pk.length >= 2) { + const my_pk = Object.keys(definedPieces[color]); + const opp_pk = Object.keys(definedPieces[oppCol]); + const oppRevert = ( + opp_pk.length >= 2 || + ( + // Only one opponent's piece is defined, but + // at least a different piece wait in reserve: + opp_pk.length == 1 && + Object.keys(this.reserve[oppCol]).some(k => { + return (k != opp_pk[0] && this.reserve[oppCol][k] >= 1); + }) + ) + ); + if (my_pk.length >= 2 || oppRevert) { + // NOTE: necessary HACK... because the move is played already. + V.UndoOnBoard(this.board, move); move.position = this.getBaseFen(); - for (let p of pk) this.reserve[color][p] = myPieces[p]; - for (let i=0; i<8; i++) { - for (let j=0; j<8; j++) { - if ( - this.board[i][j] != V.EMPTY && - this.getColor(i, j) == color && - piecesList.includes(this.getPiece(i, j)) - ) { - this.board[i][j] = color + V.UNDEFINED; + move.reserve = JSON.parse(JSON.stringify(this.reserve)); + V.PlayOnBoard(this.board, move); + for ( + let cp of [{ c: color, pk: my_pk }, { c: oppCol, pk: opp_pk }] + ) { + if (cp.pk.length >= 2 || (cp.c == oppCol && oppRevert)) { + for (let p of cp.pk) + this.reserve[cp.c][p] += definedPieces[cp.c][p]; + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + if ( + this.board[i][j] != V.EMPTY && + this.getColor(i, j) == cp.c && + piecesList.includes(this.getPiece(i, j)) + ) { + this.board[i][j] = cp.c + V.UNDEFINED; + } + } } } } @@ -454,67 +522,75 @@ export class BarioRules extends ChessRules { undo(move) { const toPrevPlayer = () => { V.UndoOnBoard(this.board, move); - this.turn = V.GetOppCol(this.turn); + [this.turn, this.subTurn] = move.turn; this.movesCount--; this.postUndo(move); }; - if (move.vanish.length == 0) { - toPrevPlayer(); - return; + if (this.movesCount <= 2 && move.appear[0].p == V.KING) toPrevPlayer(); + else if (move.vanish.length == 0) { + this.reserve[this.turn][move.start.p]++; + this.subTurn = move.turn[1]; } - const start = { x: move.vanish[0].x, y: move.vanish[0].y }; - const end = { x: move.appear[0].x, y: move.appear[0].y }; - if (start.x == end.x && start.y == end.y) { - this.reserve[this.turn][move.appear[0].p]++; - if (move.appear[0].c == move.vanish[0].c) { + else { + const start = { x: move.vanish[0].x, y: move.vanish[0].y }; + const end = { x: move.appear[0].x, y: move.appear[0].y }; + if (start.x == end.x && start.y == end.y) { + this.reserve[this.turn][move.appear[0].p]++; V.UndoOnBoard(this.board, move); this.definitions.pop(); + this.subTurn = move.turn[1]; + } + else { + this.epSquares.pop(); + toPrevPlayer(); } - this.subTurn--; - } - else { - this.epSquares.pop(); - toPrevPlayer(); } } postUndo(move) { const color = this.turn; - if (move.vanish.length == 0) { - this.kingPos[color] = [-1, -1]; - const firstRank = (color == 'w' ? 7 : 0); - for (let j = 0; j < 8; j++) this.board[firstRank][j] = ""; - } + if (this.movesCount <= 1) this.kingPos[color] = [-1, -1]; else { - if (move.vanish.length == 2 && move.vanish[1].p == V.UNDEFINED) - this.captureUndefined.pop(); + this.captureUndefined.pop(); if (move.appear[0].p == V.KING) super.postUndo(move); else { if (!!move.position) { this.board = V.GetBoard(move.position); - this.reserve[color] = { - [V.ROOK]: 0, - [V.KNIGHT]: 0, - [V.BISHOP]: 0, - [V.QUEEN]: 0 - } + this.reserve = move.reserve; } } } } getComputerMove() { + let initMoves = this.getAllValidMoves(); + if (initMoves.length == 0) return null; + // Loop until valid move is found (no un-specifiable piece...) const color = this.turn; - // Just play at random for now... - let mvArray = []; - while (this.turn == color) { - const moves = this.getAllValidMoves(); - const choice = moves[randInt(moves.length)]; - mvArray.push(choice); - this.play(choice); + while (true) { + let moves = JSON.parse(JSON.stringify(initMoves)); + let mvArray = []; + let mv = null; + // Just play random moves (for now at least. TODO?) + while (moves.length > 0) { + mv = moves[randInt(moves.length)]; + mvArray.push(mv); + this.play(mv); + if (this.turn == color) { + if (this.subTurn == 1) moves = this.getAllValidMoves(); + else { + // subTurn == 2 + moves = this.filterValid( + this.getPotentialMovesFrom([mv.end.x, mv.end.y])); + } + } + else break; + } + const thisIsTheEnd = (this.turn != color); + for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); + if (thisIsTheEnd) return (mvArray.length > 1 ? mvArray : mvArray[0]); } - for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); - return (mvArray.length == 1? mvArray[0] : mvArray); + return null; //never reached } static get VALUES() { @@ -524,19 +600,16 @@ export class BarioRules extends ChessRules { // NOTE: evalPosition is wrong, but unused (random mover) getNotation(move) { - const end = { x: move.appear[0].x, y: move.appear[0].y }; + const end = { x: move.end.x, y: move.end.y }; const endSquare = V.CoordsToSquare(end); + if (move.appear.length == 0) + // Removal + return move.start.p.toUpperCase() + endSquare + "X"; if (move.vanish.length == 0) return "K@" + endSquare; const start = { x: move.vanish[0].x, y: move.vanish[0].y }; - if (start.x == end.x && start.y == end.y) { - // Something is specialized, or removed - const symbol = move.appear[0].p.toUpperCase(); - if (move.appear[0].c == move.vanish[0].c) - // Specialisation - return symbol + "@" + endSquare; - // Removal: - return symbol + endSquare + "X"; - } + if (start.x == end.x && start.y == end.y) + // Something is specialized + return move.appear[0].p.toUpperCase() + "@" + endSquare; // Normal move return super.getNotation(move); }