// Variants generally inherit from it, and modify some parts.
import { ArrayFun } from "@/utils/array";
-import { randInt, sample, shuffle } from "@/utils/alea";
+import { randInt, shuffle } from "@/utils/alea";
export const PiPo = class PiPo //Piece+Position
{
let candidates = [0]; //indices of candidates moves
for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
candidates.push(j);
- let currentBest = moves1[sample(candidates)];
+ let currentBest = moves1[candidates[randInt(candidates.length)]];
// Skip depth 3+ if we found a checkmate (or if we are checkmated in 1...)
if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE)
this.kingPos[c] = [move.start.x, move.start.y];
}
- checkGameEnd()
+ getCurrentScore()
{
- const pieces = Object.keys(V.ALICE_CODES);
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+
+ const pieces = Object.keys(V.ALICE_CODES);
const color = this.turn;
const kp = this.kingPos[color];
const mirrorSide = (pieces.includes(this.getPiece(kp[0],kp[1])) ? 1 : 2);
-class AntikingRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+import { ArrayFun} from "@/utils/array";
+import { randInt } from "@/utils/alea";
+
+export const VariantRules = class AntikingRules extends ChessRules
{
static getPpath(b)
{
this.antikingPos[c] = [move.start.x, move.start.y];
}
- checkGameEnd()
+ getCurrentScore()
{
- const color = this.turn;
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+
+ const color = this.turn;
const oppCol = V.GetOppCol(color);
if (!this.isAttacked(this.kingPos[color], [oppCol])
&& this.isAttacked(this.antikingPos[color], [oppCol]))
let antikingPos = { "w": -1, "b": -1 };
for (let c of ["w","b"])
{
- let positions = range(8);
+ let positions = ArrayFun.range(8);
// Get random squares for bishops, but avoid corners; because,
// if an antiking blocks a cornered bishop, it can never be checkmated
return pieces["b"].join("") + "/" + ranks23_black +
"/8/8/" +
ranks23_white + "/" + pieces["w"].join("").toUpperCase() +
- " w 1111 -";
+ " w 0 1111 -";
}
}
-export const V = class AtomicRules {
- show() {
- console.log("AtomicRules");
+import { ChessRules, PiPo } from "@/base_rules";
+
+export const VariantRules = class AtomicRules extends ChessRules
+{
+ getPotentialMovesFrom([x,y])
+ {
+ let moves = super.getPotentialMovesFrom([x,y]);
+
+ // Handle explosions
+ moves.forEach(m => {
+ if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles
+ {
+ // Explosion! TODO(?): drop moves which explode our king here
+ let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ];
+ for (let step of steps)
+ {
+ let x = m.end.x + step[0];
+ let y = m.end.y + step[1];
+ if (V.OnBoard(x,y) && this.board[x][y] != V.EMPTY
+ && this.getPiece(x,y) != V.PAWN)
+ {
+ m.vanish.push(
+ new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y}));
+ }
+ }
+ m.end = {x:m.appear[0].x, y:m.appear[0].y};
+ m.appear.pop(); //Nothin appears in this case
+ }
+ });
+
+ return moves;
+ }
+
+ getPotentialKingMoves([x,y])
+ {
+ // King cannot capture:
+ let moves = [];
+ const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+ for (let step of steps)
+ {
+ const i = x + step[0];
+ const j = y + step[1];
+ if (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
+ moves.push(this.getBasicMove([x,y], [i,j]));
+ }
+ return moves.concat(this.getCastleMoves([x,y]));
+ }
+
+ isAttacked(sq, colors)
+ {
+ if (this.getPiece(sq[0],sq[1]) == V.KING && this.isAttackedByKing(sq, colors))
+ return false; //king cannot take...
+ return (this.isAttackedByPawn(sq, colors)
+ || this.isAttackedByRook(sq, colors)
+ || this.isAttackedByKnight(sq, colors)
+ || this.isAttackedByBishop(sq, colors)
+ || this.isAttackedByQueen(sq, colors));
+ }
+
+ updateVariables(move)
+ {
+ super.updateVariables(move);
+ const color = move.vanish[0].c;
+ if (move.appear.length == 0) //capture
+ {
+ const firstRank = {"w": 7, "b": 0};
+ for (let c of ["w","b"])
+ {
+ // Did we explode king of color c ? (TODO: remove move earlier)
+ if (Math.abs(this.kingPos[c][0]-move.end.x) <= 1
+ && Math.abs(this.kingPos[c][1]-move.end.y) <= 1)
+ {
+ this.kingPos[c] = [-1,-1];
+ this.castleFlags[c] = [false,false];
+ }
+ else
+ {
+ // Now check if init rook(s) exploded
+ if (Math.abs(move.end.x-firstRank[c]) <= 1)
+ {
+ if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][0]) <= 1)
+ this.castleFlags[c][0] = false;
+ if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][1]) <= 1)
+ this.castleFlags[c][1] = false;
+ }
+ }
+ }
+ }
+ }
+
+ unupdateVariables(move)
+ {
+ super.unupdateVariables(move);
+ const c = move.vanish[0].c;
+ const oppCol = V.GetOppCol(c);
+ if ([this.kingPos[c][0],this.kingPos[oppCol][0]].some(e => { return e < 0; }))
+ {
+ // There is a chance that last move blowed some king away..
+ for (let psq of move.vanish)
+ {
+ if (psq.p == 'k')
+ this.kingPos[psq.c==c ? c : oppCol] = [psq.x, psq.y];
+ }
+ }
+ }
+
+ underCheck(color)
+ {
+ const oppCol = V.GetOppCol(color);
+ let res = undefined;
+ // If our king disappeared, move is not valid
+ if (this.kingPos[color][0] < 0)
+ res = true;
+ // If opponent king disappeared, move is valid
+ else if (this.kingPos[oppCol][0] < 0)
+ res = false;
+ // Otherwise, if we remain under check, move is not valid
+ else
+ res = this.isAttacked(this.kingPos[color], [oppCol]);
+ return res;
+ }
+
+ getCheckSquares(color)
+ {
+ let res = [ ];
+ if (this.kingPos[color][0] >= 0 //king might have exploded
+ && this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
+ {
+ res = [ JSON.parse(JSON.stringify(this.kingPos[color])) ]
+ }
+ return res;
+ }
+
+ getCurrentScore()
+ {
+ const color = this.turn;
+ const kp = this.kingPos[color];
+ if (kp[0] < 0) //king disappeared
+ return color == "w" ? "0-1" : "1-0";
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+ if (!this.isAttacked(kp, [V.GetOppCol(color)]))
+ return "1/2";
+ return color == "w" ? "0-1" : "1-0"; //checkmate
}
}
-//export default V = AtomicRules;
+++ /dev/null
-import { ChessRules } from "@/base_rules";
-export const VariantRules = class AtomicRules extends ChessRules
-{
- getPotentialMovesFrom([x,y])
- {
- let moves = super.getPotentialMovesFrom([x,y]);
-
- // Handle explosions
- moves.forEach(m => {
- if (m.vanish.length > 1 && m.appear.length <= 1) //avoid castles
- {
- // Explosion! TODO(?): drop moves which explode our king here
- let steps = [ [-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1] ];
- for (let step of steps)
- {
- let x = m.end.x + step[0];
- let y = m.end.y + step[1];
- if (V.OnBoard(x,y) && this.board[x][y] != V.EMPTY
- && this.getPiece(x,y) != V.PAWN)
- {
- m.vanish.push(
- new PiPo({p:this.getPiece(x,y),c:this.getColor(x,y),x:x,y:y}));
- }
- }
- m.end = {x:m.appear[0].x, y:m.appear[0].y};
- m.appear.pop(); //Nothin appears in this case
- }
- });
-
- return moves;
- }
-
- getPotentialKingMoves([x,y])
- {
- // King cannot capture:
- let moves = [];
- const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
- for (let step of steps)
- {
- const i = x + step[0];
- const j = y + step[1];
- if (V.OnBoard(i,j) && this.board[i][j] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [i,j]));
- }
- return moves.concat(this.getCastleMoves([x,y]));
- }
-
- isAttacked(sq, colors)
- {
- if (this.getPiece(sq[0],sq[1]) == V.KING && this.isAttackedByKing(sq, colors))
- return false; //king cannot take...
- return (this.isAttackedByPawn(sq, colors)
- || this.isAttackedByRook(sq, colors)
- || this.isAttackedByKnight(sq, colors)
- || this.isAttackedByBishop(sq, colors)
- || this.isAttackedByQueen(sq, colors));
- }
-
- updateVariables(move)
- {
- super.updateVariables(move);
- const color = move.vanish[0].c;
- if (move.appear.length == 0) //capture
- {
- const firstRank = {"w": 7, "b": 0};
- for (let c of ["w","b"])
- {
- // Did we explode king of color c ? (TODO: remove move earlier)
- if (Math.abs(this.kingPos[c][0]-move.end.x) <= 1
- && Math.abs(this.kingPos[c][1]-move.end.y) <= 1)
- {
- this.kingPos[c] = [-1,-1];
- this.castleFlags[c] = [false,false];
- }
- else
- {
- // Now check if init rook(s) exploded
- if (Math.abs(move.end.x-firstRank[c]) <= 1)
- {
- if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][0]) <= 1)
- this.castleFlags[c][0] = false;
- if (Math.abs(move.end.y-this.INIT_COL_ROOK[c][1]) <= 1)
- this.castleFlags[c][1] = false;
- }
- }
- }
- }
- }
-
- unupdateVariables(move)
- {
- super.unupdateVariables(move);
- const c = move.vanish[0].c;
- const oppCol = V.GetOppCol(c);
- if ([this.kingPos[c][0],this.kingPos[oppCol][0]].some(e => { return e < 0; }))
- {
- // There is a chance that last move blowed some king away..
- for (let psq of move.vanish)
- {
- if (psq.p == 'k')
- this.kingPos[psq.c==c ? c : oppCol] = [psq.x, psq.y];
- }
- }
- }
-
- underCheck(color)
- {
- const oppCol = V.GetOppCol(color);
- let res = undefined;
- // If our king disappeared, move is not valid
- if (this.kingPos[color][0] < 0)
- res = true;
- // If opponent king disappeared, move is valid
- else if (this.kingPos[oppCol][0] < 0)
- res = false;
- // Otherwise, if we remain under check, move is not valid
- else
- res = this.isAttacked(this.kingPos[color], [oppCol]);
- return res;
- }
-
- getCheckSquares(color)
- {
- let res = [ ];
- if (this.kingPos[color][0] >= 0 //king might have exploded
- && this.isAttacked(this.kingPos[color], [V.GetOppCol(color)]))
- {
- res = [ JSON.parse(JSON.stringify(this.kingPos[color])) ]
- }
- return res;
- }
-
- checkGameEnd()
- {
- const color = this.turn;
- const kp = this.kingPos[color];
- if (kp[0] < 0) //king disappeared
- return color == "w" ? "0-1" : "1-0";
- if (!this.isAttacked(kp, [V.GetOppCol(color)]))
- return "1/2";
- return color == "w" ? "0-1" : "1-0"; //checkmate
- }
-}
-class BaroqueRules extends ChessRules
+import { ChessRules, PiPo, Move } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { randInt } from "@/utils/alea";
+
+export const VariantRules = class BaroqueRules extends ChessRules
{
static get HasFlags() { return false; }
// Shuffle pieces on first and last rank
for (let c of ["w","b"])
{
- let positions = range(8);
+ let positions = ArrayFun.range(8);
// Get random squares for every piece, totally freely
let randIndex = randInt(8);
return pieces["b"].join("") +
"/pppppppp/8/8/8/8/PPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w";
+ " w 0";
}
getNotation(move)
-class BerolinaRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+
+export const VariantRules = class BerolinaRules extends ChessRules
{
// En-passant after 2-sq jump
getEpSquare(moveOrSquare)
-class CheckeredRules extends ChessRules
+// TODO: to detect oppositeMoves, we need last move --> encoded in FEN
+// + local moves stack (for AlphaBeta) + lastMove (in FEN)
+
+import { ChessRules } from "@/base_rules";
+
+export const VariantRules = class CheckeredRules extends ChessRules
{
static getPpath(b)
{
this.pawnFlags[move.start.x==6 ? "w" : "b"][move.start.y] = false;
}
- checkGameEnd()
+ getCurrentScore()
{
- const color = this.turn;
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+
+ const color = this.turn;
// Artifically change turn, for checkered pawns
this.turn = V.GetOppCol(this.turn);
const res = this.isAttacked(this.kingPos[color], [V.GetOppCol(color),'c'])
{
const randFen = ChessRules.GenRandInitFen();
// Add 16 pawns flags:
- return randFen.replace(" w 1111", " w 11111111111111111111");
+ return randFen.replace(" w 0 1111", " w 0 11111111111111111111");
}
getFlagsFen()
-class CrazyhouseRules extends ChessRules
+import { ChessRules, PiPo, Move } from "@/base_rules";
+import { ArrayFun} from "@/utils/array";
+
+export const VariantRules = class CrazyhouseRules extends ChessRules
{
static IsGoodFen(fen)
{
[V.QUEEN]: parseInt(fenParsed.reserve[9]),
}
};
- this.promoted = doubleArray(V.size.x, V.size.y, false);
+ this.promoted = ArrayFun.init(V.size.x, V.size.y, false);
if (fenParsed.promoted != "-")
{
for (let square of fenParsed.promoted.split(","))
-class DarkRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+import { ArrayFun} from "@/utils/array";
+import { randInt } from "@/utils/alea";
+
+export const VariantRules = class DarkRules extends ChessRules
{
// Standard rules, in the shadow
setOtherVariables(fen)
super.setOtherVariables(fen);
const [sizeX,sizeY] = [V.size.x,V.size.y];
this.enlightened = {
- "w": doubleArray(sizeX,sizeY),
- "b": doubleArray(sizeX,sizeY)
+ "w": ArrayFun.init(sizeX,sizeY),
+ "b": ArrayFun.init(sizeX,sizeY)
};
// Setup enlightened: squares reachable by each side
// (TODO: one side would be enough ?)
this.updateEnlightened();
}
- checkGameEnd()
- {
- // No valid move: our king disappeared
- return this.turn == "w" ? "0-1" : "1-0";
+ getCurrentScore()
+ {
+ const color = this.turn;
+ const kp = this.kingPos[color];
+ if (kp[0] < 0) //king disappeared
+ return (color == "w" ? "0-1" : "1-0");
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+ return "1/2"; //no moves but kings still there (seems impossible)
}
static get THRESHOLD_MATE()
let candidates = [0];
for (let j=1; j<moves.length && moves[j].eval == moves[0].eval; j++)
candidates.push(j);
- return moves[sample(candidates)];
+ return moves[candidates[randInt(candidates.length)]];
}
}
-class ExtinctionRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+
+export const VariantRules = class ExtinctionRules extends ChessRules
{
setOtherVariables(fen)
{
this.material[move.vanish[1].c][move.vanish[1].p]++;
}
- checkGameOver()
+ getCurrentScore()
{
- if (this.checkRepetition())
- return "1/2";
-
if (this.atLeastOneMove()) // game not over?
{
const color = this.turn;
if (Object.keys(this.material[color]).some(
p => { return this.material[color][p] == 0; }))
{
- return this.checkGameEnd();
+ return (this.turn == "w" ? "0-1" : "1-0");
}
return "*";
}
- return this.checkGameEnd(); //NOTE: currently unreachable...
- }
-
- checkGameEnd()
- {
- return (this.turn == "w" ? "0-1" : "1-0");
+ return (this.turn == "w" ? "0-1" : "1-0"); //NOTE: currently unreachable...
}
evalPosition()
+import { ChessRules } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { randInt } from "@/utils/alea";
+
// NOTE: initial setup differs from the original; see
// https://www.chessvariants.com/large.dir/freeling.html
-class GrandRules extends ChessRules
+export const VariantRules = class GrandRules extends ChessRules
{
static getPpath(b)
{
// Shuffle pieces on first and last rank
for (let c of ["w","b"])
{
- let positions = range(10);
+ let positions = ArrayFun.range(10);
// Get random squares for bishops
let randIndex = 2 * randInt(5);
return pieces["b"].join("") +
"/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 1111 - 00000000000000";
+ " w 0 1111 - 00000000000000";
}
}
-class LosersRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { randInt } from "@/utils/alea";
+
+export const VariantRules = class LosersRules extends ChessRules
{
static get HasFlags() { return false; }
updateVariables(move) { }
unupdateVariables(move) { }
- checkGameEnd()
- {
- // No valid move: you win!
- return this.turn == "w" ? "1-0" : "0-1";
+ getCurrentScore()
+ {
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+
+ // No valid move: the side who cannot move wins
+ return (this.turn == "w" ? "1-0" : "0-1");
}
static get VALUES()
// Shuffle pieces on first and last rank
for (let c of ["w","b"])
{
- let positions = range(8);
+ let positions = ArrayFun.range(8);
// Get random squares for bishops
let randIndex = 2 * randInt(4);
return pieces["b"].join("") +
"/pppppppp/8/8/8/8/PPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w -"; //no en-passant
+ " w 0 -"; //en-passant allowed, but no flags
}
}
-class MagneticRules extends ChessRules
+import { ChessRules, PiPo } from "@/base_rules";
+
+export const VariantRules = class MagneticRules extends ChessRules
{
static get HasEnpassant() { return false; }
}
}
- checkGameEnd()
+ getCurrentScore()
{
- // No valid move: our king disappeared
- return this.turn == "w" ? "0-1" : "1-0";
+ const color = this.turn;
+ const kp = this.kingPos[color];
+ if (kp[0] < 0) //king disappeared
+ return (color == "w" ? "0-1" : "1-0");
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+ return "1/2"; //no moves but kings still there
}
static get THRESHOLD_MATE()
};
}
+ // TODO: this is wrong: revise following base_rules.getComputerMove()
// No alpha-beta here, just adapted min-max at depth 2(+1)
getComputerMove()
{
let moves = this.getAllValidMoves();
if (moves.length == 0)
{
- const score = this.checkGameEnd();
+ const score = this.getCurrentScore();
if (score == "1/2")
return 0;
return maxeval * (score == "1-0" ? 1 : -1);
// Otherwise it's color,1. In both cases the next test makes sense
if (!this.atLeastOneMove())
{
- const score = this.checkGameEnd();
+ const score = this.getCurrentScore();
if (score == "1/2")
res = (oppCol == "w" ? Math.max(res, 0) : Math.min(res, 0));
else
+++ /dev/null
-class SwitchingRules extends ChessRules
-{
- // Build switch move between squares x1,y1 and x2,y2
- getSwitchMove_s([x1,y1],[x2,y2])
- {
- const c = this.getColor(x1,y1); //same as color at square 2
- const p1 = this.getPiece(x1,y1);
- const p2 = this.getPiece(x2,y2);
- if (p1 == V.KING && p2 == V.ROOK)
- return []; //avoid duplicate moves (potential conflict with castle)
- let move = new Move({
- appear: [
- new PiPo({x:x2,y:y2,c:c,p:p1}),
- new PiPo({x:x1,y:y1,c:c,p:p2})
- ],
- vanish: [
- new PiPo({x:x1,y:y1,c:c,p:p1}),
- new PiPo({x:x2,y:y2,c:c,p:p2})
- ],
- start: {x:x1,y:y1},
- end: {x:x2,y:y2}
- });
- // Move completion: promote switched pawns (as in Magnetic)
- const lastRank = (c == "w" ? 0 : V.size.x-1);
- let moves = [];
- if ((p1==V.PAWN && x2==lastRank) || (p2==V.PAWN && x1==lastRank))
- {
- const idx = (p1==V.PAWN ? 0 : 1);
- move.appear[idx].p = V.ROOK;
- moves.push(move);
- for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
- {
- let cmove = JSON.parse(JSON.stringify(move));
- cmove.appear[idx].p = piece;
- moves.push(cmove);
- }
- if (idx == 1)
- {
- // Swap moves[i].appear[0] and [1] for moves presentation [TODO...]
- moves.forEach(m => {
- let tmp = m.appear[0];
- m.appear[0] = m.appear[1];
- m.appear[1] = tmp;
- });
- }
- }
- else //other cases
- moves.push(move);
- return moves;
- }
-
- getPotentialMovesFrom([x,y], computer)
- {
- let moves = super.getPotentialMovesFrom([x,y]);
- // Add switches: respecting chessboard ordering if "computer" is on
- const color = this.turn;
- const piece = this.getPiece(x,y);
- const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
- const kp = this.kingPos[color];
- const oppCol = V.GetOppCol(color);
- for (let step of steps)
- {
- let [i,j] = [x+step[0],y+step[1]];
- if (!!computer && (i<x || (i==x && j<y)))
- continue; //only switch with superior indices
- if (V.OnBoard(i,j) && this.board[i][j]!=V.EMPTY
- && this.getColor(i,j)==color && this.getPiece(i,j)!=piece
- // No switching under check (theoretically non-king pieces could, but not)
- && !this.isAttacked(kp, [oppCol]))
- {
- let switchMove_s = this.getSwitchMove_s([x,y],[i,j]);
- if (switchMove_s.length == 1)
- moves.push(switchMove_s[0]);
- else //promotion
- moves = moves.concat(switchMove_s);
- }
- }
- return moves;
- }
-
- getAllValidMoves(computer)
- {
- const color = this.turn;
- const oppCol = V.GetOppCol(color);
- 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], computer));
- }
- }
- }
- return this.filterValid(potentialMoves);
- }
-
- updateVariables(move)
- {
- super.updateVariables(move);
- if (move.appear.length == 2 && move.vanish.length == 2
- && move.appear[1].p == V.KING)
- {
- // Switch with the king; not castle, and not handled by main class
- const color = move.vanish[0].c;
- this.kingPos[color] = [move.appear[1].x, move.appear[1].y];
- }
- }
-
- unupdateVariables(move)
- {
- super.unupdateVariables(move);
- if (move.appear.length == 2 && move.vanish.length == 2
- && move.appear[1].p == V.KING)
- {
- const color = move.vanish[0].c;
- this.kingPos[color] = [move.appear[0].x, move.appear[0].y];
- }
- }
-
- static get SEARCH_DEPTH() { return 2; } //high branching factor
-
- getNotation(move)
- {
- if (move.appear.length == 1)
- return super.getNotation(move); //no switch
- // Switch or castle
- if (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK)
- return (move.end.y < move.start.y ? "0-0-0" : "0-0");
- // Switch:
- return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
- }
-}
-class UpsidedownRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+import { randInt } from "@/utils/alea";
+import { ArrayFun } from "@/utils/array";
+
+export const VariantRules = class UpsidedownRules extends ChessRules
{
static get HasFlags() { return false; }
let pieces = { "w": new Array(8), "b": new Array(8) };
for (let c of ["w","b"])
{
- let positions = range(8);
+ let positions = ArrayFun.range(8);
let randIndex = randInt(8);
const kingPos = positions[randIndex];
else if (kingPos == V.size.y-1)
knight1Pos = V.size.y-2;
else
- knight1Pos = kingPos + (Math.randInt() < 0.5 ? 1 : -1);
+ knight1Pos = kingPos + (Math.random() < 0.5 ? 1 : -1);
// Search for knight1Pos index in positions and remove it
const knight1Index = positions.indexOf(knight1Pos);
positions.splice(knight1Index, 1);
return pieces["w"].join("").toUpperCase() +
"/PPPPPPPP/8/8/8/8/pppppppp/" +
pieces["b"].join("") +
- " w"; //no castle, no en-passant
+ " w 0"; //no castle, no en-passant
}
}
-class WildebeestRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+import { ArrayFun } from "@/utils/array";
+import { sample, randInt } from "@/utils/alea";
+
+export const VariantRules = class WildebeestRules extends ChessRules
{
static getPpath(b)
{
V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]), "oneStep");
}
- checkGameEnd()
- {
+ getCurrentScore()
+ {
+ if (this.atLeastOneMove()) // game not over
+ return "*";
+
// No valid move: game is lost (stalemate is a win)
- return this.turn == "w" ? "0-1" : "1-0";
+ return (this.turn == "w" ? "0-1" : "1-0");
}
static get VALUES() {
let pieces = { "w": new Array(10), "b": new Array(10) };
for (let c of ["w","b"])
{
- let positions = range(11);
+ let positions = ArrayFun.range(11);
// Get random squares for bishops + camels (different colors)
- let randIndexes = sample(range(6), 2).map(i => { return 2*i; });
+ let randIndexes = sample(ArrayFun.range(6), 2)
+ .map(i => { return 2*i; });
let bishop1Pos = positions[randIndexes[0]];
let camel1Pos = positions[randIndexes[1]];
// The second bishop (camel) must be on a square of different color
- let randIndexes_tmp = sample(range(5), 2).map(i => { return 2*i+1; });
+ let randIndexes_tmp = sample(ArrayFun.range(5), 2)
+ .map(i => { return 2*i+1; });
let bishop2Pos = positions[randIndexes_tmp[0]];
let camel2Pos = positions[randIndexes_tmp[1]];
for (let idx of randIndexes.concat(randIndexes_tmp)
return pieces["b"].join("") +
"/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 1111 -";
+ " w 0 1111 -";
}
}
-class ZenRules extends ChessRules
+import { ChessRules } from "@/base_rules";
+
+export const VariantRules = class ZenRules extends ChessRules
{
// NOTE: enPassant, if enabled, would need to redefine carefully getEpSquare
static get HasEnpassant() { return false; }