X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=variants%2FChakart%2Fclass.js;h=9b62150b8baffe25531aa31818801106ea4619e9;hb=b0cf998b1f63aa23916ab35a3d978b6972d273c5;hp=ef2ef21544cd7763ecd981dd890ce289bc8f2531;hpb=2b9b90da84838a759b0ca382929bc70b1c83a296;p=xogo.git diff --git a/variants/Chakart/class.js b/variants/Chakart/class.js index ef2ef21..9b62150 100644 --- a/variants/Chakart/class.js +++ b/variants/Chakart/class.js @@ -1,12 +1,7 @@ import ChessRules from "/base_rules"; -import { SuicideRules } from "/variants/Suicide"; ////////:TODO generalize genRandInitFen ?! -// constraints satisfaction ? + Chakart display bonus messages -// + animation + multi-moves for bananas/bombs/mushrooms - - - -import { ArrayFun } from "/utils/array"; -import { randInt } from "/utils/alea"; +import GiveawayRules from "/variants/Giveaway"; +import { ArrayFun } from "/utils/array.js"; +import { Random } from "/utils/alea.js"; import PiPo from "/utils/PiPo.js"; import Move from "/utils/Move.js"; @@ -29,9 +24,10 @@ export class ChakartRules extends ChessRules { }; } - static get PawnSpecs() { - return SuicideRules.PawnSpecs; + get pawnPromotions() { + return ['q', 'r', 'n', 'b', 'k']; } + get hasCastle() { return false; } @@ -79,97 +75,30 @@ export class ChakartRules extends ChessRules { return 'm'; } - static fen2board(f) { + genRandInitFen(seed) { + const gr = new GiveawayRules({mode: "suicide"}, true); return ( - f.charCodeAt() <= 90 - ? "w" + f.toLowerCase() - : (['w', 'd', 'e', 'm'].includes(f) ? "a" : "b") + f + gr.genRandInitFen(seed).slice(0, -1) + + // Add Peach + Mario flags + capture counts + '{"flags": "1111", "ccount": "000000000000"}' ); } - static get PIECES() { + fen2board(f) { return ( - ChessRules.PIECES.concat( - Object.keys(V.IMMOBILIZE_DECODE)).concat( - [V.BANANA, V.BOMB, V.EGG, V.MUSHROOM, V.INVISIBLE_QUEEN]) - ); - } - - getPpath(b) { - let prefix = ""; - if ( - b[0] == 'a' || - b[1] == V.INVISIBLE_QUEEN || - Object.keys(V.IMMOBILIZE_DECODE).includes(b[1]) - ) { - prefix = "Chakart/"; - } - return prefix + b; - } - - getPPpath(m) { - if (!!m.promoteInto) return m.promoteInto; - if (m.appear.length == 0 && m.vanish.length == 1) - // King 'remote shell capture', on an adjacent square: - return this.getPpath(m.vanish[0].c + m.vanish[0].p); - let piece = m.appear[0].p; - if (Object.keys(V.IMMOBILIZE_DECODE).includes(piece)) - // Promotion by capture into immobilized piece: do not reveal! - piece = V.IMMOBILIZE_DECODE[piece]; - return this.getPpath(m.appear[0].c + piece); - } - - static ParseFen(fen) { - const fenParts = fen.split(" "); - return Object.assign( - ChessRules.ParseFen(fen), - { captured: fenParts[4] } + f.charCodeAt() <= 90 + ? "w" + f.toLowerCase() + : (['w', 'd', 'e', 'm'].includes(f) ? "a" : "b") + f ); } - static IsGoodFen(fen) { - if (!ChessRules.IsGoodFen(fen)) return false; - const captured = V.ParseFen(fen).captured; - if (!captured || !captured.match(/^[0-9]{12,12}$/)) return false; - return true; - } - - // King can be l or L (immobilized) --> similar to Alice variant - static IsGoodPosition(position) { - if (position.length == 0) return false; - const rows = position.split("/"); - if (rows.length != V.size.x) return false; - let kings = { "k": 0, "K": 0, 'l': 0, 'L': 0 }; - for (let row of rows) { - let sumElts = 0; - for (let i = 0; i < row.length; i++) { - if (['K', 'k', 'L', 'l'].includes(row[i])) kings[row[i]]++; - if (V.PIECES.includes(row[i].toLowerCase())) sumElts++; - else { - const num = parseInt(row[i], 10); - if (isNaN(num)) return false; - sumElts += num; - } - } - if (sumElts != V.size.y) return false; - } - if (kings['k'] + kings['l'] == 0 || kings['K'] + kings['L'] == 0) - return false; - return true; - } - - static IsGoodFlags(flags) { - // 4 for Peach + Mario w, b - return !!flags.match(/^[01]{4,4}$/); - } - setFlags(fenflags) { // King can send shell? Queen can be invisible? this.powerFlags = { - w: { 'k': false, 'q': false }, - b: { 'k': false, 'q': false } + w: {k: false, q: false}, + b: {k: false, q: false} }; - for (let c of ["w", "b"]) { + for (let c of ['w', 'b']) { for (let p of ['k', 'q']) { this.powerFlags[c][p] = fenflags.charAt((c == "w" ? 0 : 2) + (p == 'k' ? 0 : 1)) == "1"; @@ -189,92 +118,56 @@ export class ChakartRules extends ChessRules { return super.getFen() + " " + this.getCapturedFen(); } - getFenForRepeat() { - return super.getFenForRepeat() + "_" + this.getCapturedFen(); + getFlagsFen() { + return ['w', 'b'].map(c => { + return ['k', 'q'].map(p => this.powerFlags[c][p] ? "1" : "0").join(""); + }).join(""); } getCapturedFen() { - let counts = [...Array(12).fill(0)]; - let i = 0; - for (let p of V.RESERVE_PIECES) { - counts[i] = this.captured["w"][p]; - counts[6 + i] = this.captured["b"][p]; - i++; - } - return counts.join(""); + const res = ['w', 'b'].map(c => { + Object.values(this.captured[c]) + }); + return res[0].concat(res[1]).join(""); } - scanKings() {} - - setOtherVariables(fen) { - super.setOtherVariables(fen); + setOtherVariables(fenParsed) { + super.setOtherVariables(fenParsed); // Initialize captured pieces' counts from FEN - const captured = - V.ParseFen(fen).captured.split("").map(x => parseInt(x, 10)); + const allCapts = fenParsed.captured.split("").map(x => parseInt(x, 10)); + const pieces = ['p', 'r', 'n', 'b', 'q', 'k']; this.captured = { - w: { - [V.PAWN]: captured[0], - [V.ROOK]: captured[1], - [V.KNIGHT]: captured[2], - [V.BISHOP]: captured[3], - [V.QUEEN]: captured[4], - [V.KING]: captured[5] - }, - b: { - [V.PAWN]: captured[6], - [V.ROOK]: captured[7], - [V.KNIGHT]: captured[8], - [V.BISHOP]: captured[9], - [V.QUEEN]: captured[10], - [V.KING]: captured[11] - } + w: Array.toObject(pieces, allCapts.slice(0, 6)), + b: Array.toObject(pieces, allCapts.slice(6, 12)) }; - this.effects = []; - this.subTurn = 1; - } - - getFlagsFen() { - let fen = ""; - // Add power flags - for (let c of ["w", "b"]) - for (let p of ['k', 'q']) fen += (this.powerFlags[c][p] ? "1" : "0"); - return fen; - } - - getColor(i, j) { - if (i >= V.size.x) return i == V.size.x ? "w" : "b"; - return this.board[i][j].charAt(0); - } - - getPiece(i, j) { - if (i >= V.size.x) return V.RESERVE_PIECES[j]; - return this.board[i][j].charAt(1); - } - - getReservePpath(index, color) { - return color + V.RESERVE_PIECES[index]; - } - - static get RESERVE_PIECES() { - return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING]; + this.reserve = { w: {}, b: {} }; //to be replaced by this.captured + this.moveStack = []; } - getReserveMoves([x, y]) { - const color = this.turn; - const p = V.RESERVE_PIECES[y]; - if (this.reserve[color][p] == 0) return []; + // For Toadette bonus + getDropMovesFrom([c, p]) { + if (this.reserve[c][p] == 0) + return []; let moves = []; - const start = (color == 'w' && p == V.PAWN ? 1 : 0); - const end = (color == 'b' && p == V.PAWN ? 7 : 8); + const start = (c == 'w' && p == 'p' ? 1 : 0); + const end = (color == 'b' && p == 'p' ? 7 : 8); for (let i = start; i < end; i++) { - for (let j = 0; j < V.size.y; j++) { + for (let j = 0; j < this.size.y; j++) { + const pieceIJ = this.getPiece(i, j); if ( - this.board[i][j] == V.EMPTY || + this.board[i][j] == "" || this.getColor(i, j) == 'a' || - this.getPiece(i, j) == V.INVISIBLE_QUEEN + pieceIJ == V.INVISIBLE_QUEEN ) { - let m = this.getBasicMove({ p: p, x: i, y: j}); - m.start = { x: x, y: y }; + let m = new Move({ + start: {x: c, y: p}, + end: {x: i, y: j}, + appear: [new PiPo({x: i, y: j, c: c, p: p})], + vanish: [] + }); + // A drop move may remove a bonus (or hidden queen!) + if (this.board[i][j] != "") + m.vanish.push(new PiPo({x: i, y: j, c: 'a', p: pieceIJ})); moves.push(m); } } @@ -282,10 +175,68 @@ export class ChakartRules extends ChessRules { return moves; } + + + + + + + + + +// TODO: rethink from here: + + +// allow pawns + // queen invisible move, king shell: special functions + +// prevent pawns from capturing invisible queen (post) +// post-process: + +//events : playPlusVisual after mouse up, playReceived (include animation) on opp move +// ==> if move.cont (banana...) self re-call playPlusVisual (rec ?) + + // Initial call: effects resolved after playing getPotentialMovesFrom([x, y]) { let moves = []; - if (this.subTurn == 1) { - moves = super.getPotentialMovesFrom([x, y]); + switch (this.getPiece(x, y)) { + case 'p': + moves = this.getPawnMovesFrom([x, y]); //apply promotions + break; + case 'q': + moves = this.getQueenMovesFrom([x, y]); + break; + case 'k', + moves = this.getKingMoves([x, y]); + break; + default: + moves = super.getPotentialMovesFrom([x, y]); + } + return moves; + } + + + + tryMoveFollowup(move) { + if (this.getColor(move.end.x, move.end.y) == 'a') { + // effect, or bonus/malus + const endType = this.getPiece(m.end.x, m.end.y); + if (endType == V.EGG) + this.applyRandomBonus(m); + else { + this.moveStack.push(m); + switch (endType) { + case V.BANANA: + this.randomRedirect( + case V.BOMB: + case V.MUSHROOM: + // aller dans direction, saut par dessus pièce adverse + // ou amie (tjours), new step si roi caval pion + } + } + } + + const finalPieces = V.PawnSpecs.promotions; const color = this.turn; const lastRank = (color == "w" ? 0 : 7); @@ -367,8 +318,8 @@ export class ChakartRules extends ChessRules { // Helper for getBasicMove(): banana/bomb effect getRandomSquare([x, y], steps) { - const validSteps = steps.filter(s => V.OnBoard(x + s[0], y + s[1])); - const step = validSteps[randInt(validSteps.length)]; + const validSteps = steps.filter(s => this.onBoard(x + s[0], y + s[1])); + const step = validSteps[Random.randInt(validSteps.length)]; return [x + step[0], y + step[1]]; } @@ -1010,30 +961,23 @@ export class ChakartRules extends ChessRules { return moves; } - play(move) { -// if (!this.states) this.states = []; -// const stateFen = this.getFen(); -// this.states.push(stateFen); - move.flags = JSON.stringify(this.aggregateFlags()); - V.PlayOnBoard(this.board, move); - move.turn = [this.turn, this.subTurn]; - if (["kingboo", "toadette", "daisy"].includes(move.end.effect)) { - this.effects.push(move.end.effect); - this.subTurn = 2; - } - else { - this.turn = V.GetOppCol(this.turn); - this.movesCount++; - this.subTurn = 1; - } - this.postPlay(move); - } - postPlay(move) { - if (move.end.effect == "toadette") this.reserve = this.captured; - else this.reserve = undefined; - const color = move.turn[0]; + + + + + + + +/// if any of my pieces was immobilized, it's not anymore. + //if play set a piece immobilized, then mark it + prePlay(move) { + if (move.effect == "toadette") + this.reserve = this.captured; + else + this.reserve = { w: {}, b: {} };; + const color = this.turn; if ( move.vanish.length == 2 && move.vanish[1].c != 'a' && @@ -1041,7 +985,8 @@ export class ChakartRules extends ChessRules { ) { // Capture: update this.captured let capturedPiece = move.vanish[1].p; - if (capturedPiece == V.INVISIBLE_QUEEN) capturedPiece = V.QUEEN; + if (capturedPiece == V.INVISIBLE_QUEEN) + capturedPiece = V.QUEEN; else if (Object.keys(V.IMMOBILIZE_DECODE).includes(capturedPiece)) capturedPiece = V.IMMOBILIZE_DECODE[capturedPiece]; this.captured[move.vanish[1].c][capturedPiece]++; @@ -1099,232 +1044,25 @@ export class ChakartRules extends ChessRules { } } - undo(move) { - this.disaggregateFlags(JSON.parse(move.flags)); - V.UndoOnBoard(this.board, move); - if (["kingboo", "toadette", "daisy"].includes(move.end.effect)) - this.effects.pop(); - else this.movesCount--; - this.turn = move.turn[0]; - this.subTurn = move.turn[1]; - this.postUndo(move); - -// const stateFen = this.getFen(); -// if (stateFen != this.states[this.states.length-1]) debugger; -// this.states.pop(); - } - - postUndo(move) { - if (!!move.wasImmobilized) { - const [i, j] = move.wasImmobilized; - this.board[i][j] = - this.getColor(i, j) + V.IMMOBILIZE_CODE[this.getPiece(i, j)]; - } - if (!!move.wasInvisible) { - const [i, j] = move.wasInvisible; - this.board[i][j] = this.getColor(i, j) + V.INVISIBLE_QUEEN; - } - if (move.vanish.length == 2 && move.vanish[1].c != 'a') { - let capturedPiece = move.vanish[1].p; - if (capturedPiece == V.INVISIBLE_QUEEN) capturedPiece = V.QUEEN; - else if (Object.keys(V.IMMOBILIZE_DECODE).includes(capturedPiece)) - capturedPiece = V.IMMOBILIZE_DECODE[capturedPiece]; - this.captured[move.vanish[1].c][capturedPiece]--; - } - else if (move.vanish.length == 0) { - if (move.appear.length == 0 || move.appear[0].c == 'a') return; - // A piece was back on board - this.captured[move.appear[0].c][move.appear[0].p]++; + play(move) { + this.prePlay(move); + this.playOnBoard(move); + if (["kingboo", "toadette", "daisy"].includes(move.effect)) { + this.effect = move.effect; + this.subTurn = 2; } - else if (move.appear.length == 0 && move.end.effect == "chomp") - this.captured[move.vanish[0].c][move.vanish[0].p]--; - if (move.vanish.length == 0) this.reserve = this.captured; - else this.reserve = undefined; - } - - getCheckSquares() { - return []; - } - - getCurrentScore() { - // Find kings (not tracked in this variant) - let kingThere = { w: false, b: false }; - for (let i=0; i<8; i++) { - for (let j=0; j<8; j++) { - if ( - this.board[i][j] != V.EMPTY && - ['k', 'l'].includes(this.getPiece(i, j)) - ) { - kingThere[this.getColor(i, j)] = true; - } - } + else { + this.turn = C.GetOppCol(this.turn); + this.movesCount++; + this.subTurn = 1; } - if (!kingThere['w']) return "0-1"; - if (!kingThere['b']) return "1-0"; - if (!this.atLeastOneMove()) return (this.turn == 'w' ? "0-1" : "1-0"); - return "*"; - } - - static GenRandInitFen(options) { - return ( - SuicideRules.GenRandInitFen(options).slice(0, -1) + - // Add Peach + Mario flags + capture counts - "1111 000000000000" - ); } filterValid(moves) { return moves; } - static get VALUES() { - return Object.assign( - {}, - ChessRules.VALUES, - { - s: 1, - u: 5, - o: 3, - c: 3, - t: 9, - l: 1000, - e: 0, - d: 0, - w: 0, - m: 0 - } - ); - } - - static get SEARCH_DEPTH() { - return 1; - } - - getComputerMove() { - const moves = this.getAllValidMoves(); - // Split into "normal" and "random" moves: - // (Next splitting condition is OK because cannot take self object - // without a banana or bomb on the way). - const deterministicMoves = moves.filter(m => { - return m.vanish.every(a => a.c != 'a' || a.p == V.MUSHROOM); - }); - const randomMoves = moves.filter(m => { - return m.vanish.some(a => a.c == 'a' && a.p != V.MUSHROOM); - }); - if (Math.random() < deterministicMoves.length / randomMoves.length) - // Play a deterministic one: capture king or material if possible - return super.getComputerMove(deterministicMoves); - // Play a random effect move, at random: - let move1 = randomMoves[randInt(randomMoves.length)]; - this.play(move1); - let move2 = undefined; - if (this.subTurn == 2) { - const moves2 = this.getAllValidMoves(); - move2 = moves2[randInt(moves2.length)]; - } - this.undo(move1); - if (!move2) return move1; - return [move1, move2]; - } - - getNotation(move) { - if (move.vanish.length == 0 && move.appear.length == 0) return "-"; - if ( - !move.end.effect && - move.appear.length > 0 && - move.appear[0].p == V.INVISIBLE_QUEEN - ) { - return "Q??"; - } - const finalSquare = V.CoordsToSquare(move.end); - // Next condition also includes Toadette placements: - if (move.appear.length > 0 && move.vanish.every(a => a.c == 'a')) { - const piece = - move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; - return piece + "@" + finalSquare; - } - else if (move.appear.length == 0) { - const piece = this.getPiece(move.start.x, move.start.y); - if (piece == V.KING && !move.end.effect) - // King remote capture - return "Kx" + finalSquare; - // Koopa or Chomp, or loopback after bananas, bombs & mushrooms: - return ( - piece.toUpperCase() + "x" + finalSquare + - ( - !!move.end.effect - ? "*" + (move.end.effect == "koopa" ? "K" : "C") - : "" - ) - ); - } - if (move.appear.length == 1 && move.vanish.length == 1) { - const moveStart = move.appear[0].p.toUpperCase() + "@"; - if (move.appear[0].c == 'a' && move.vanish[0].c == 'a') - // Bonus replacement: - return moveStart + finalSquare; - if ( - move.vanish[0].p == V.INVISIBLE_QUEEN && - move.appear[0].x == move.vanish[0].x && - move.appear[0].y == move.vanish[0].y - ) { - // Toadette takes invisible queen - return moveStart + "Q" + finalSquare; - } - } - if ( - move.appear.length == 2 && - move.vanish.length == 2 && - move.appear.every(a => a.c != 'a') && - move.vanish.every(v => v.c != 'a') - ) { - // King Boo exchange - return V.CoordsToSquare(move.start) + finalSquare; - } - const piece = move.vanish[0].p; - let notation = undefined; - if (piece == V.PAWN) { - // Pawn move - if (this.board[move.end.x][move.end.y] != V.EMPTY) { - // Capture - const startColumn = V.CoordToColumn(move.start.y); - notation = startColumn + "x" + finalSquare; - } - else notation = finalSquare; - if (move.appear[0].p != V.PAWN) - // Promotion - notation += "=" + move.appear[0].p.toUpperCase(); - } - else { - notation = - piece.toUpperCase() + - (this.board[move.end.x][move.end.y] != V.EMPTY ? "x" : "") + - finalSquare; - } - if (!!move.end.effect) { - switch (move.end.effect) { - case "kingboo": - notation += "*B"; - break; - case "toadette": - notation += "*T"; - break; - case "daisy": - notation += "*D"; - break; - case "bowser": - notation += "*M"; - break; - case "luigi": - case "waluigi": - const lastAppear = move.appear[move.appear.length - 1]; - const effectOn = - V.CoordsToSquare({ x: lastAppear.x, y : lastAppear.y }); - notation += "*" + move.end.effect[0].toUpperCase() + effectOn; - break; - } - } - return notation; - } + // TODO + display bonus messages + // + animation + multi-moves for bananas/bombs/mushrooms };