+++ /dev/null
-import { ChessRules, Move, PiPo } from "@/base_rules";
-import { randInt } from "@/utils/alea";
-import { ArrayFun } from "@/utils/array";
-
-export class PandemoniumRules extends ChessRules {
-
- loseOnRepetition() {
- // If current side is under check: lost
- return this.underCheck(this.turn);
- }
-
- static get GILDING() {
- return "g";
- }
-
- static get SCEPTER() {
- return "s";
- }
-
- static get HORSE() {
- return "h";
- }
-
- static get DRAGON() {
- return "d";
- }
-
- static get CARDINAL() {
- return "c";
- }
-
- static get WHOLE() {
- return "w";
- }
-
- static get MARSHAL() {
- return "m";
- }
-
- static get APRICOT() {
- return "a";
- }
-
- static get PIECES() {
- return (
- ChessRules.PIECES.concat([
- V.GILDING, V.SCEPTER, V.HORSE, V.DRAGON,
- V.CARDINAL, V.WHOLE, V.MARSHAL, V.APRICOT])
- );
- }
-
- getPpath(b) {
- const prefix = (ChessRules.PIECES.includes(b[1]) ? "" : "Pandemonium/");
- return prefix + b;
- }
-
- static get size() {
- return { x: 10, y: 10};
- }
-
- 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);
- }
-
- setOtherVariables(fen) {
- super.setOtherVariables(fen);
- // Sub-turn is useful only at first move...
- this.subTurn = 1;
- // Also init reserves (used by the interface to show landable pieces)
- const reserve =
- V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10));
- this.reserve = {
- w: {
- [V.PAWN]: reserve[0],
- [V.ROOK]: reserve[1],
- [V.KNIGHT]: reserve[2],
- [V.BISHOP]: reserve[3],
- [V.QUEEN]: reserve[4],
- [V.CARDINAL]: reserve[5],
- [V.MARSHAL]: reserve[6],
- },
- b: {
- [V.PAWN]: reserve[7],
- [V.ROOK]: reserve[8],
- [V.KNIGHT]: reserve[9],
- [V.BISHOP]: reserve[10],
- [V.QUEEN]: reserve[11],
- [V.CARDINAL]: reserve[12],
- [V.MARSHAL]: reserve[13]
- }
- };
- }
-
- static IsGoodEnpassant(enpassant) {
- if (enpassant != "-") {
- const squares = enpassant.split(",");
- if (squares.length > 2) return false;
- for (let sq of squares) {
- if (!sq.match(/[a-j0-9]/)) return false;
- }
- }
- return true;
- }
-
- static IsGoodFen(fen) {
- if (!ChessRules.IsGoodFen(fen)) return false;
- const fenParsed = V.ParseFen(fen);
- // Check reserves
- if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{14,14}$/))
- return false;
- return true;
- }
-
- static ParseFen(fen) {
- const fenParts = fen.split(" ");
- return Object.assign(
- ChessRules.ParseFen(fen),
- { reserve: fenParts[5] }
- );
- }
-
- getFen() {
- return super.getFen() + " " + this.getReserveFen();
- }
-
- getFenForRepeat() {
- return super.getFenForRepeat() + "_" + this.getReserveFen();
- }
-
- getReserveFen() {
- let counts = new Array(14);
- for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
- counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]];
- counts[7 + i] = this.reserve["b"][V.RESERVE_PIECES[i]];
- }
- return counts.join("");
- }
-
- static GenRandInitFen(randomness) {
- if (randomness == 0) {
- return (
- "rnbqkmcbnr/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/RNBQKMCBNR " +
- "w 0 ajaj - 00000000000000"
- );
- }
-
- let pieces = { w: new Array(10), b: new Array(10) };
- let flags = "";
- for (let c of ["w", "b"]) {
- if (c == 'b' && randomness == 1) {
- pieces['b'] = pieces['w'];
- flags += flags;
- break;
- }
-
- let positions = ArrayFun.range(10);
-
- // Get random squares for bishops (different colors)
- let randIndex = 2 * randInt(5);
- let bishop1Pos = positions[randIndex];
- let randIndex_tmp = 2 * randInt(5) + 1;
- let bishop2Pos = positions[randIndex_tmp];
- positions.splice(Math.max(randIndex, randIndex_tmp), 1);
- positions.splice(Math.min(randIndex, randIndex_tmp), 1);
-
- randIndex = randInt(8);
- let knight1Pos = positions[randIndex];
- positions.splice(randIndex, 1);
- randIndex = randInt(7);
- let knight2Pos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- randIndex = randInt(6);
- let queenPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- // Random squares for cardinal + marshal
- randIndex = randInt(5);
- let cardinalPos = positions[randIndex];
- positions.splice(randIndex, 1);
- randIndex = randInt(4);
- let marshalPos = positions[randIndex];
- positions.splice(randIndex, 1);
-
- let rook1Pos = positions[0];
- let kingPos = positions[1];
- let rook2Pos = positions[2];
-
- pieces[c][rook1Pos] = "r";
- pieces[c][knight1Pos] = "n";
- pieces[c][bishop1Pos] = "b";
- pieces[c][queenPos] = "q";
- pieces[c][kingPos] = "k";
- pieces[c][marshalPos] = "m";
- pieces[c][cardinalPos] = "c";
- pieces[c][bishop2Pos] = "b";
- pieces[c][knight2Pos] = "n";
- pieces[c][rook2Pos] = "r";
- flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos);
- }
- return (
- pieces["b"].join("") +
- "/pppppppppp/91/91/91/91/91/91/PPPPPPPPPP/" +
- pieces["w"].join("").toUpperCase() +
- " w 0 " + flags + " - 00000000000000"
- );
- }
-
- getEnpassantFen() {
- const L = this.epSquares.length;
- if (!this.epSquares[L - 1]) return "-"; //no en-passant
- let res = "";
- this.epSquares[L - 1].forEach(sq => {
- res += V.CoordsToSquare(sq) + ",";
- });
- return res.slice(0, -1); //remove last comma
- }
-
- getEpSquare(moveOrSquare) {
- if (!moveOrSquare) return undefined;
- if (typeof moveOrSquare === "string") {
- const square = moveOrSquare;
- if (square == "-") return undefined;
- let res = [];
- square.split(",").forEach(sq => {
- res.push(V.SquareToCoords(sq));
- });
- return res;
- }
- // Argument is a move:
- const move = moveOrSquare;
- const [sx, sy, ex] = [move.start.x, move.start.y, move.end.x];
- if (this.getPiece(sx, sy) == V.PAWN && Math.abs(sx - ex) >= 2) {
- const step = (ex - sx) / Math.abs(ex - sx);
- let res = [{
- x: sx + step,
- y: sy
- }];
- if (sx + 2 * step != ex) {
- // 3-squares jump
- res.push({
- x: sx + 2 * step,
- y: sy
- });
- }
- return res;
- }
- return undefined; //default
- }
-
- getReservePpath(index, color) {
- const p = V.RESERVE_PIECES[index];
- const prefix = (ChessRules.PIECES.includes(p) ? "" : "Pandemonium/");
- return prefix + color + p;;
- }
-
- // Ordering on reserve pieces
- static get RESERVE_PIECES() {
- return (
- [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.CARDINAL, V.MARSHAL]
- );
- }
-
- getReserveMoves([x, y]) {
- const color = this.turn;
- const oppCol = V.GetOppCol(color);
- const p = V.RESERVE_PIECES[y];
- if (this.reserve[color][p] == 0) return [];
- const bounds = (p == V.PAWN ? [1, V.size.x - 1] : [0, V.size.x]);
- let moves = [];
- for (let i = bounds[0]; i < bounds[1]; i++) {
- for (let j = 0; j < V.size.y; j++) {
- if (this.board[i][j] == V.EMPTY) {
- let mv = new Move({
- appear: [
- new PiPo({
- x: i,
- y: j,
- c: color,
- p: p
- })
- ],
- vanish: [],
- start: { x: x, y: y }, //a bit artificial...
- end: { x: i, y: j }
- });
- if (p == V.PAWN) {
- // Do not drop on checkmate:
- this.play(mv);
- const res = (
- this.underCheck(oppCol) && !this.atLeastOneMove("noReserve")
- );
- this.undo(mv);
- if (res) continue;
- }
- moves.push(mv);
- }
- }
- }
- return moves;
- }
-
- static get PromoteMap() {
- return {
- r: 'd',
- n: 's',
- b: 'h',
- c: 'w',
- m: 'a'
- };
- }
-
- getPotentialMovesFrom([x, y]) {
- const c = this.getColor(x, y);
- const oppCol = V.GetOppCol(c);
- if (this.movesCount <= 1) {
- if (this.kingPos[c][0] == x && this.kingPos[c][1] == y) {
- // Pass (if setup is ok)
- return [
- new Move({
- appear: [],
- vanish: [],
- start: { x: this.kingPos[c][0], y: this.kingPos[c][1] },
- end: { x: this.kingPos[oppCol][0], y: this.kingPos[oppCol][1] }
- })
- ];
- }
- const firstRank = (this.movesCount == 0 ? 9 : 0);
- if (x != firstRank || this.getPiece(x, y) != V.KNIGHT) return [];
- // Swap with who? search for matching bishop:
- let knights = [],
- bishops = [];
- for (let i = 0; i < 10; i++) {
- const elt = this.board[x][i][1];
- if (elt == 'n') knights.push(i);
- else if (elt == 'b') bishops.push(i);
- }
- const destFile = (knights[0] == y ? bishops[0] : bishops[1]);
- return [
- new Move({
- appear: [
- new PiPo({
- x: x,
- y: destFile,
- c: c,
- p: V.KNIGHT
- }),
- new PiPo({
- x: x,
- y: y,
- c: c,
- p: V.BISHOP
- })
- ],
- vanish: [
- new PiPo({
- x: x,
- y: y,
- c: c,
- p: V.KNIGHT
- }),
- new PiPo({
- x: x,
- y: destFile,
- c: c,
- p: V.BISHOP
- })
- ],
- start: { x: x, y: y },
- end: { x: x, y: destFile }
- })
- ];
- }
- // Normal move (after initial setup)
- if (x >= V.size.x) return this.getReserveMoves([x, y]);
- const p = this.getPiece(x, y);
- const sq = [x, y];
- let moves = [];
- if (ChessRules.PIECES.includes(p))
- moves = super.getPotentialMovesFrom(sq);
- if ([V.GILDING, V.APRICOT, V.WHOLE].includes(p))
- moves = super.getPotentialQueenMoves(sq);
- switch (p) {
- case V.SCEPTER:
- moves = this.getPotentialScepterMoves(sq);
- break;
- case V.HORSE:
- moves = this.getPotentialHorseMoves(sq);
- break;
- case V.DRAGON:
- moves = this.getPotentialDragonMoves(sq);
- break;
- case V.CARDINAL:
- moves = this.getPotentialCardinalMoves(sq);
- break;
- case V.MARSHAL:
- moves = this.getPotentialMarshalMoves(sq);
- break;
- }
- // Maybe apply promotions:
- if (Object.keys(V.PromoteMap).includes(p)) {
- const promoted = V.PromoteMap[p];
- const lastRanks = (c == 'w' ? [0, 1] : [9, 8]);
- let promotions = [];
- moves.forEach(m => {
- if (lastRanks.includes(m.start.x) || lastRanks.includes(m.end.x)) {
- let pMove = JSON.parse(JSON.stringify(m));
- pMove.appear[0].p = promoted;
- promotions.push(pMove);
- }
- });
- Array.prototype.push.apply(moves, promotions);
- }
- return moves;
- }
-
- addPawnMoves([x1, y1], [x2, y2], moves) {
- const color = this.turn;
- const lastRanks = (color == "w" ? [0, 1] : [9, 8]);
- if (!lastRanks.includes(x2)) {
- moves.push(this.getBasicMove([x1, y1], [x2, y2]));
- return;
- }
- let finalPieces = [V.GILDING];
- if (x2 == lastRanks[1]) finalPieces.push(V.PAWN);
- for (let piece of finalPieces) {
- const tr = (piece != V.PAWN ? { c: color, p: piece } : null);
- moves.push(this.getBasicMove([x1, y1], [x2, y2], tr));
- }
- }
-
- getPotentialPawnMoves([x, y]) {
- const color = this.turn;
- const shiftX = (color == 'w' ? -1 : 1);
- let moves = [];
- if (this.board[x + shiftX][y] == V.EMPTY) {
- this.addPawnMoves([x, y], [x + shiftX, y], moves);
- if ((color == 'w' && x >= V.size.x - 3) || (color == 'b' && x <= 2)) {
- if (this.board[x + 2 * shiftX][y] == V.EMPTY) {
- moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
- if (
- (
- (color == 'w' && x == V.size.x - 2) ||
- (color == 'b' && x == 1)
- )
- &&
- this.board[x + 3 * shiftX][y] == V.EMPTY
- ) {
- moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y]));
- }
- }
- }
- }
- for (let shiftY of [-1, 1]) {
- if (y + shiftY >= 0 && y + shiftY < V.size.y) {
- if (
- this.board[x + shiftX][y + shiftY] != V.EMPTY &&
- this.canTake([x, y], [x + shiftX, y + shiftY])
- ) {
- this.addPawnMoves([x, y], [x + shiftX, y + shiftY], moves);
- }
- }
- }
- Array.prototype.push.apply(
- moves,
- this.getEnpassantCaptures([x, y], shiftX)
- );
- return moves;
- }
-
- getPotentialMarshalMoves(sq) {
- return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
- this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
- );
- }
-
- getPotentialCardinalMoves(sq) {
- return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
- this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep")
- );
- }
-
- getPotentialScepterMoves(sq) {
- const steps =
- V.steps[V.KNIGHT].concat(V.steps[V.BISHOP]).concat(V.steps[V.ROOK]);
- return this.getSlideNJumpMoves(sq, steps, "oneStep");
- }
-
- getPotentialHorseMoves(sq) {
- return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
- this.getSlideNJumpMoves(sq, V.steps[V.ROOK], "oneStep"));
- }
-
- getPotentialDragonMoves(sq) {
- return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
- this.getSlideNJumpMoves(sq, V.steps[V.BISHOP], "oneStep"));
- }
-
- getEnpassantCaptures([x, y], shiftX) {
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep - 1];
- let moves = [];
- if (!!epSquare) {
- for (let epsq of epSquare) {
- // TODO: some redundant checks
- if (epsq.x == x + shiftX && Math.abs(epsq.y - y) == 1) {
- let enpassantMove = this.getBasicMove([x, y], [epsq.x, epsq.y]);
- // WARNING: the captured pawn may be diagonally behind us,
- // if it's a 3-squares jump and we take on 1st passing square
- const px = this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX;
- enpassantMove.vanish.push({
- x: px,
- y: epsq.y,
- p: "p",
- c: this.getColor(px, epsq.y)
- });
- moves.push(enpassantMove);
- }
- }
- }
- return moves;
- }
-
- getPotentialKingMoves(sq) {
- // Initialize with normal moves
- let moves = this.getSlideNJumpMoves(
- sq,
- V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
- "oneStep"
- );
- const c = this.turn;
- if (
- this.castleFlags[c][0] < V.size.y ||
- this.castleFlags[c][1] < V.size.y
- ) {
- const finalSquares = [
- [1, 2],
- [7, 6]
- ];
- moves = moves.concat(super.getCastleMoves(sq, finalSquares));
- }
- return moves;
- }
-
- isAttacked(sq, color) {
- return (
- this.isAttackedByPawn(sq, color) ||
- this.isAttackedByRook(sq, color) ||
- this.isAttackedByKnight(sq, color) ||
- this.isAttackedByBishop(sq, color) ||
- this.isAttackedByKing(sq, color) ||
- this.isAttackedByQueens(sq, color) ||
- this.isAttackedByScepter(sq, color) ||
- this.isAttackedByDragon(sq, color) ||
- this.isAttackedByHorse(sq, color) ||
- this.isAttackedByMarshal(sq, color) ||
- this.isAttackedByCardinal(sq, color)
- );
- }
-
- isAttackedByQueens([x, y], color) {
- // pieces: because queen = gilding = whole = apricot
- const pieces = [V.QUEEN, V.GILDING, V.WHOLE, V.APRICOT];
- const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
- for (let step of steps) {
- let rx = x + step[0],
- ry = y + step[1];
- while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY) {
- rx += step[0];
- ry += step[1];
- }
- if (
- V.OnBoard(rx, ry) &&
- this.board[rx][ry] != V.EMPTY &&
- pieces.includes(this.getPiece(rx, ry)) &&
- this.getColor(rx, ry) == color
- ) {
- return true;
- }
- }
- return false;
- }
-
- isAttackedByScepter(sq, color) {
- const steps =
- V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]);
- return (
- super.isAttackedBySlideNJump(sq, color, V.SCEPTER, steps, "oneStep")
- );
- }
-
- isAttackedByHorse(sq, color) {
- return (
- super.isAttackedBySlideNJump(sq, color, V.HORSE, V.steps[V.BISHOP]) ||
- super.isAttackedBySlideNJump(
- sq, color, V.HORSE, V.steps[V.ROOK], "oneStep")
- );
- }
-
- isAttackedByDragon(sq, color) {
- return (
- super.isAttackedBySlideNJump(sq, color, V.DRAGON, V.steps[V.ROOK]) ||
- super.isAttackedBySlideNJump(
- sq, color, V.DRAGON, V.steps[V.BISHOP], "oneStep")
- );
- }
-
- isAttackedByMarshal(sq, color) {
- return (
- super.isAttackedBySlideNJump(sq, color, V.MARSHAL, V.steps[V.ROOK]) ||
- super.isAttackedBySlideNJump(
- sq,
- color,
- V.MARSHAL,
- V.steps[V.KNIGHT],
- "oneStep"
- )
- );
- }
-
- isAttackedByCardinal(sq, color) {
- return (
- super.isAttackedBySlideNJump(sq, color, V.CARDINAL, V.steps[V.BISHOP]) ||
- super.isAttackedBySlideNJump(
- sq,
- color,
- V.CARDINAL,
- V.steps[V.KNIGHT],
- "oneStep"
- )
- );
- }
-
- getAllValidMoves() {
- let moves = super.getAllPotentialMoves();
- if (this.movesCount >= 2) {
- 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])
- );
- }
- }
- return this.filterValid(moves);
- }
-
- atLeastOneMove(noReserve) {
- if (!super.atLeastOneMove()) {
- if (!noReserve) {
- // Search one reserve move
- for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
- let moves = this.filterValid(
- this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i])
- );
- if (moves.length > 0) return true;
- }
- }
- return false;
- }
- return true;
- }
-
- // Reverse 'PromoteMap'
- static get P_CORRESPONDANCES() {
- return {
- d: 'r',
- s: 'n',
- h: 'b',
- w: 'c',
- a: 'm',
- g: 'p'
- };
- }
-
- static MayDecode(piece) {
- if (Object.keys(V.P_CORRESPONDANCES).includes(piece))
- return V.P_CORRESPONDANCES[piece];
- return piece;
- }
-
- play(move) {
- move.subTurn = this.subTurn; //much easier
- if (this.movesCount >= 2 || this.subTurn == 2 || move.vanish.length == 0) {
- this.turn = V.GetOppCol(this.turn);
- this.subTurn = 1;
- this.movesCount++;
- }
- else this.subTurn = 2;
- move.flags = JSON.stringify(this.aggregateFlags());
- this.epSquares.push(this.getEpSquare(move));
- V.PlayOnBoard(this.board, move);
- this.postPlay(move);
- }
-
- postPlay(move) {
- if (move.vanish.length == 0 && move.appear.length == 0) return;
- super.postPlay(move);
- const color = move.appear[0].c;
- if (move.vanish.length == 0)
- // Drop unpromoted piece:
- this.reserve[color][move.appear[0].p]--;
- else if (move.vanish.length == 2 && move.appear.length == 1)
- // May capture a promoted piece:
- this.reserve[color][V.MayDecode(move.vanish[1].p)]++;
- }
-
- undo(move) {
- this.epSquares.pop();
- this.disaggregateFlags(JSON.parse(move.flags));
- V.UndoOnBoard(this.board, move);
- if (this.movesCount >= 2 || this.subTurn == 1 || move.vanish.length == 0) {
- this.turn = V.GetOppCol(this.turn);
- this.movesCount--;
- }
- this.subTurn = move.subTurn;
- this.postUndo(move);
- }
-
- postUndo(move) {
- if (move.vanish.length == 0 && move.appear.length == 0) return;
- super.postUndo(move);
- const color = move.appear[0].c;
- if (move.vanish.length == 0)
- this.reserve[color][move.appear[0].p]++;
- else if (move.vanish.length == 2 && move.appear.length == 1)
- this.reserve[color][V.MayDecode(move.vanish[1].p)]--;
- }
-
- static get VALUES() {
- return Object.assign(
- {},
- ChessRules.VALUES,
- {
- n: 2.5, //knight is weaker
- g: 9,
- s: 5,
- h: 6,
- d: 7,
- c: 7,
- w: 9,
- m: 8,
- a: 9
- }
- );
- }
-
- static get SEARCH_DEPTH() {
- return 2;
- }
-
- getComputerMove() {
- if (this.movesCount <= 1) {
- // Special case: swap and pass at random
- const moves1 = this.getAllValidMoves();
- const m1 = moves1[randInt(moves1.length)];
- this.play(m1);
- if (m1.vanish.length == 0) {
- this.undo(m1);
- return m1;
- }
- const moves2 = this.getAllValidMoves();
- const m2 = moves2[randInt(moves2.length)];
- this.undo(m1);
- return [m1, m2];
- }
- return super.getComputerMove();
- }
-
- evalPosition() {
- let evaluation = super.evalPosition();
- // Add reserves:
- for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
- const p = V.RESERVE_PIECES[i];
- evaluation += this.reserve["w"][p] * V.VALUES[p];
- evaluation -= this.reserve["b"][p] * V.VALUES[p];
- }
- return evaluation;
- }
-
- getNotation(move) {
- if (move.vanish.length == 0) {
- if (move.appear.length == 0) return "pass";
- const pieceName =
- (move.appear[0].p == V.PAWN ? "" : move.appear[0].p.toUpperCase());
- return pieceName + "@" + V.CoordsToSquare(move.end);
- }
- if (move.appear.length == 2) {
- if (move.appear[0].p != V.KING)
- return V.CoordsToSquare(move.start) + "S" + V.CoordsToSquare(move.end);
- return (move.end.y < move.start.y ? "0-0" : "0-0-0");
- }
- let notation = super.getNotation(move);
- if (move.vanish[0].p != V.PAWN && move.appear[0].p != move.vanish[0].p)
- // Add promotion indication:
- notation += "=" + move.appear[0].p.toUpperCase();
- return notation;
- }
-
-};