Add Koopa chess, fix Apocalypse and Dice variants
[vchess.git] / client / src / variants / Dice.js
1 import { ChessRules, Move } from "@/base_rules";
2 import { randInt } from "@/utils/alea";
3
4 export class DiceRules extends ChessRules {
5 static get CanAnalyze() {
6 return false;
7 }
8
9 static ParseFen(fen) {
10 const fenParts = fen.split(" ");
11 return Object.assign(
12 ChessRules.ParseFen(fen),
13 { toplay: fenParts[5] }
14 );
15 }
16
17 setOtherVariables(fen) {
18 super.setOtherVariables(fen);
19 this.p2play = [];
20 const toplay = V.ParseFen(fen).toplay;
21 if (toplay != "-") this.p2play.push(toplay);
22 }
23
24 getFen() {
25 return super.getFen() + " " + this.getToplayFen();
26 }
27
28 getFen() {
29 return super.getFenForRepeat() + "_" + this.getToplayFen();
30 }
31
32 getToplayFen() {
33 const L = this.p2play.length;
34 return (L > 0 ? this.p2play[L-1] : "-");
35 }
36
37 static GenRandInitFen(randomness) {
38 return ChessRules.GenRandInitFen(randomness) + " -";
39 }
40
41 canMove(piece, color, [x, y]) {
42 const oppCol = V.GetOppCol(color);
43 if (piece == V.PAWN) {
44 const forward = (color == 'w' ? -1 : 1);
45 if (this.board[x + forward][y] == V.EMPTY) return true;
46 for (let shift of [-1, 1]) {
47 const [i, j] = [x + forward, y + shift];
48 if (
49 V.OnBoard(i, j) &&
50 this.board[i][j] != V.EMPTY &&
51 this.getColor(i, j) == oppCol
52 ) {
53 return true;
54 }
55 }
56 }
57 else {
58 const steps =
59 [V.KING, V.QUEEN].includes(piece)
60 ? V.steps[V.ROOK].concat(V.steps[V.BISHOP])
61 : V.steps[piece];
62 for (let s of steps) {
63 const [i, j] = [x + s[0], y + s[1]];
64 if (
65 V.OnBoard(i, j) &&
66 (this.board[i][j] == V.EMPTY || this.getColor(i, j) == oppCol)
67 ) {
68 return true;
69 }
70 }
71 }
72 return false;
73 }
74
75 getRandPiece(color) {
76 // Find pieces which can move and roll a dice
77 let canMove = {};
78 for (let i=0; i<8; i++) {
79 for (let j=0; j<8; j++) {
80 if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == color) {
81 const piece = this.getPiece(i, j);
82 if (!canMove[piece] && this.canMove(piece, color, [i, j]))
83 canMove[piece] = [i, j];
84 }
85 }
86 }
87 const options = Object.keys(canMove);
88 const randPiece = options[randInt(options.length)];
89 return [randPiece, canMove[randPiece]];
90 }
91
92 getPotentialMovesFrom([x, y]) {
93 const color = this.turn;
94 let moves = undefined;
95 if (this.movesCount == 0) moves = super.getPotentialMovesFrom([x, y]);
96 else {
97 const L = this.p2play.length; //L is >= 1
98 const piece = this.getPiece(x, y);
99 if (
100 piece == V.PAWN &&
101 this.p2play[L-1] != V.PAWN &&
102 ((color == 'w' && x == 1) || (color == 'b' && x == 6))
103 ) {
104 // The piece is a pawn about to promote
105 const destX = (color == 'w' ? 0 : 7);
106 moves = [];
107 if (this.board[destX][y] == V.EMPTY) {
108 moves.push(
109 this.getBasicMove(
110 [x, y], [destX, y], { c: color, p: this.p2play[L-1] })
111 );
112 }
113 for (let shift of [-1, 1]) {
114 const [i, j] = [destX, y + shift];
115 if (
116 V.OnBoard(i, j) &&
117 this.board[i][j] != V.EMPTY &&
118 this.getColor(i, j) != color
119 ) {
120 moves.push(
121 this.getBasicMove(
122 [x, y], [i, j], { c: color, p: this.p2play[L-1] })
123 );
124 }
125 }
126 }
127 else if (piece != this.p2play[L-1])
128 // The piece type must match last p2play
129 return [];
130 else moves = super.getPotentialMovesFrom([x, y]);
131 }
132 // Decide which piece the opponent will play:
133 const oppCol = V.GetOppCol(color);
134 moves.forEach(m => {
135 V.PlayOnBoard(this.board, m);
136 const [piece, square] = this.getRandPiece(oppCol);
137 m.start.toplay = square;
138 m.end.piece = piece;
139 V.UndoOnBoard(this.board, m);
140 });
141 return moves;
142 }
143
144 filterValid(moves) {
145 return moves;
146 }
147
148 getCheckSquares() {
149 return [];
150 }
151
152 getCurrentScore() {
153 const color = this.turn;
154 if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0");
155 return "*";
156 }
157
158 postPlay(move) {
159 this.p2play.push(move.end.piece);
160 if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
161 this.kingPos[move.vanish[1].c] = [-1, -1];
162 // Castle flags for captured king won't be updated (not important...)
163 super.postPlay(move);
164 }
165
166 postUndo(move) {
167 this.p2play.pop();
168 if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
169 this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y];
170 super.postUndo(move);
171 }
172
173 static get SEARCH_DEPTH() {
174 return 1;
175 }
176
177 getNotation(move) {
178 return super.getNotation(move) + "/" + move.end.piece.toUpperCase();
179 }
180 };