1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { sample
, randInt
} from "@/utils/alea";
5 export class WildebeestRules
extends ChessRules
{
7 return { x: 10, y: 11 };
13 static get WILDEBEEST() {
18 return ChessRules
.PIECES
.concat([V
.CAMEL
, V
.WILDEBEEST
]);
41 static IsGoodEnpassant(enpassant
) {
42 if (enpassant
!= "-") {
43 const squares
= enpassant
.split(",");
44 if (squares
.length
> 2) return false;
45 for (let sq
of squares
) {
46 const ep
= V
.SquareToCoords(sq
);
47 if (isNaN(ep
.x
) || !V
.OnBoard(ep
)) return false;
54 return ([V
.CAMEL
, V
.WILDEBEEST
].includes(b
[1]) ? "Wildebeest/" : "") + b
;
57 // There may be 2 enPassant squares (if pawn jump 3 squares)
59 const L
= this.epSquares
.length
;
60 if (!this.epSquares
[L
- 1]) return "-"; //no en-passant
62 this.epSquares
[L
- 1].forEach(sq
=> {
63 res
+= V
.CoordsToSquare(sq
) + ",";
65 return res
.slice(0, -1); //remove last comma
68 // En-passant after 2-sq or 3-sq jumps
69 getEpSquare(moveOrSquare
) {
70 if (!moveOrSquare
) return undefined;
71 if (typeof moveOrSquare
=== "string") {
72 const square
= moveOrSquare
;
73 if (square
== "-") return undefined;
75 square
.split(",").forEach(sq
=> {
76 res
.push(V
.SquareToCoords(sq
));
80 // Argument is a move:
81 const move = moveOrSquare
;
82 const [sx
, sy
, ex
] = [move.start
.x
, move.start
.y
, move.end
.x
];
83 if (this.getPiece(sx
, sy
) == V
.PAWN
&& Math
.abs(sx
- ex
) >= 2) {
84 const step
= (ex
- sx
) / Math
.abs(ex
- sx
);
91 if (sx
+ 2 * step
!= ex
) {
100 return undefined; //default
103 getPotentialMovesFrom([x
, y
]) {
104 switch (this.getPiece(x
, y
)) {
106 return this.getPotentialCamelMoves([x
, y
]);
108 return this.getPotentialWildebeestMoves([x
, y
]);
110 return super.getPotentialMovesFrom([x
, y
]);
114 // Pawns jump 2 or 3 squares, and promote to queen or wildebeest
115 getPotentialPawnMoves([x
, y
]) {
116 const color
= this.turn
;
118 const [sizeX
, sizeY
] = [V
.size
.x
, V
.size
.y
];
119 const shiftX
= color
== "w" ? -1 : 1;
120 const startRanks
= color
== "w" ? [sizeX
- 2, sizeX
- 3] : [1, 2];
121 const lastRank
= color
== "w" ? 0 : sizeX
- 1;
122 const finalPieces
= x
+ shiftX
== lastRank
123 ? [V
.WILDEBEEST
, V
.QUEEN
]
126 if (this.board
[x
+ shiftX
][y
] == V
.EMPTY
) {
127 // One square forward
128 for (let piece
of finalPieces
)
130 this.getBasicMove([x
, y
], [x
+ shiftX
, y
], { c: color
, p: piece
})
132 if (startRanks
.includes(x
)) {
133 if (this.board
[x
+ 2 * shiftX
][y
] == V
.EMPTY
) {
135 moves
.push(this.getBasicMove([x
, y
], [x
+ 2 * shiftX
, y
]));
136 if (x
== startRanks
[0] && this.board
[x
+ 3 * shiftX
][y
] == V
.EMPTY
) {
137 // Three squares jump
138 moves
.push(this.getBasicMove([x
, y
], [x
+ 3 * shiftX
, y
]));
144 for (let shiftY
of [-1, 1]) {
147 y
+ shiftY
< sizeY
&&
148 this.board
[x
+ shiftX
][y
+ shiftY
] != V
.EMPTY
&&
149 this.canTake([x
, y
], [x
+ shiftX
, y
+ shiftY
])
151 for (let piece
of finalPieces
) {
153 this.getBasicMove([x
, y
], [x
+ shiftX
, y
+ shiftY
], {
163 const Lep
= this.epSquares
.length
;
164 const epSquare
= this.epSquares
[Lep
- 1];
166 for (let epsq
of epSquare
) {
167 // TODO: some redundant checks
168 if (epsq
.x
== x
+ shiftX
&& Math
.abs(epsq
.y
- y
) == 1) {
169 var enpassantMove
= this.getBasicMove([x
, y
], [epsq
.x
, epsq
.y
]);
170 // WARNING: the captured pawn may be diagonally behind us,
171 // if it's a 3-squares jump and we take on 1st passing square
172 const px
= this.board
[x
][epsq
.y
] != V
.EMPTY
? x : x
- shiftX
;
173 enpassantMove
.vanish
.push({
177 c: this.getColor(px
, epsq
.y
)
179 moves
.push(enpassantMove
);
187 // TODO: wildebeest castle
189 getPotentialCamelMoves(sq
) {
190 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.CAMEL
], "oneStep");
193 getPotentialWildebeestMoves(sq
) {
194 return this.getSlideNJumpMoves(
196 V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.CAMEL
]),
201 isAttacked(sq
, color
) {
203 super.isAttacked(sq
, color
) ||
204 this.isAttackedByCamel(sq
, color
) ||
205 this.isAttackedByWildebeest(sq
, color
)
209 isAttackedByCamel(sq
, color
) {
210 return this.isAttackedBySlideNJump(
219 isAttackedByWildebeest(sq
, color
) {
220 return this.isAttackedBySlideNJump(
224 V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.CAMEL
]),
230 if (this.atLeastOneMove()) return "*";
231 // No valid move: game is lost (stalemate is a win)
232 return this.turn
== "w" ? "0-1" : "1-0";
235 static get VALUES() {
236 return Object
.assign(
237 { c: 3, w: 7 }, //experimental
242 static get SEARCH_DEPTH() {
246 static GenRandInitFen(randomness
) {
247 if (!randomness
) randomness
= 2;
249 return "rnccwkqbbnr/ppppppppppp/92/92/92/92/92/92/PPPPPPPPPPP/RNBBQKWCCNR w 0 akak -";
251 let pieces
= { w: new Array(11), b: new Array(11) };
253 for (let c
of ["w", "b"]) {
254 if (c
== 'b' && randomness
== 1) {
255 pieces
['b'] = pieces
['w'];
260 let positions
= ArrayFun
.range(11);
262 // Get random squares for bishops + camels (different colors)
263 let randIndexes
= sample(ArrayFun
.range(6), 2).map(i
=> {
266 let bishop1Pos
= positions
[randIndexes
[0]];
267 let camel1Pos
= positions
[randIndexes
[1]];
268 // The second bishop (camel) must be on a square of different color
269 let randIndexes_tmp
= sample(ArrayFun
.range(5), 2).map(i
=> {
272 let bishop2Pos
= positions
[randIndexes_tmp
[0]];
273 let camel2Pos
= positions
[randIndexes_tmp
[1]];
274 for (let idx
of randIndexes
.concat(randIndexes_tmp
).sort((a
, b
) => {
277 // Largest indices first
278 positions
.splice(idx
, 1);
281 let randIndex
= randInt(7);
282 let knight1Pos
= positions
[randIndex
];
283 positions
.splice(randIndex
, 1);
284 randIndex
= randInt(6);
285 let knight2Pos
= positions
[randIndex
];
286 positions
.splice(randIndex
, 1);
288 randIndex
= randInt(5);
289 let queenPos
= positions
[randIndex
];
290 positions
.splice(randIndex
, 1);
292 // Random square for wildebeest
293 randIndex
= randInt(4);
294 let wildebeestPos
= positions
[randIndex
];
295 positions
.splice(randIndex
, 1);
297 let rook1Pos
= positions
[0];
298 let kingPos
= positions
[1];
299 let rook2Pos
= positions
[2];
301 pieces
[c
][rook1Pos
] = "r";
302 pieces
[c
][knight1Pos
] = "n";
303 pieces
[c
][bishop1Pos
] = "b";
304 pieces
[c
][queenPos
] = "q";
305 pieces
[c
][camel1Pos
] = "c";
306 pieces
[c
][camel2Pos
] = "c";
307 pieces
[c
][wildebeestPos
] = "w";
308 pieces
[c
][kingPos
] = "k";
309 pieces
[c
][bishop2Pos
] = "b";
310 pieces
[c
][knight2Pos
] = "n";
311 pieces
[c
][rook2Pos
] = "r";
312 flags
+= V
.CoordToColumn(rook1Pos
) + V
.CoordToColumn(rook2Pos
);
315 pieces
["b"].join("") +
316 "/ppppppppppp/92/92/92/92/92/92/PPPPPPPPPPP/" +
317 pieces
["w"].join("").toUpperCase() +
318 " w 0 " + flags
+ " -"