1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { sample
, randInt
} from "@/utils/alea";
5 export const VariantRules
= class WildebeestRules
extends ChessRules
9 return ([V
.CAMEL
,V
.WILDEBEEST
].includes(b
[1]) ? "Wildebeest/" : "") + b
;
12 static get size() { return {x:10,y:11}; }
14 static get CAMEL() { return 'c'; }
15 static get WILDEBEEST() { return 'w'; }
19 return ChessRules
.PIECES
.concat([V
.CAMEL
,V
.WILDEBEEST
]);
25 ChessRules
.steps
, //add camel moves:
26 {'c': [ [-3,-1],[-3,1],[-1,-3],[-1,3],[1,-3],[1,3],[3,-1],[3,1] ]}
30 static IsGoodEnpassant(enpassant
)
34 const squares
= enpassant
.split(",");
35 if (squares
.length
> 2)
37 for (let sq
of squares
)
39 const ep
= V
.SquareToCoords(sq
);
40 if (isNaN(ep
.x
) || !V
.OnBoard(ep
))
47 // There may be 2 enPassant squares (if pawn jump 3 squares)
50 const L
= this.epSquares
.length
;
51 if (!this.epSquares
[L
-1])
52 return "-"; //no en-passant
54 this.epSquares
[L
-1].forEach(sq
=> {
55 res
+= V
.CoordsToSquare(sq
) + ",";
57 return res
.slice(0,-1); //remove last comma
60 // En-passant after 2-sq or 3-sq jumps
61 getEpSquare(moveOrSquare
)
65 if (typeof moveOrSquare
=== "string")
67 const square
= moveOrSquare
;
71 square
.split(",").forEach(sq
=> {
72 res
.push(V
.SquareToCoords(sq
));
76 // Argument is a move:
77 const move = moveOrSquare
;
78 const [sx
,sy
,ex
] = [move.start
.x
,move.start
.y
,move.end
.x
];
79 if (this.getPiece(sx
,sy
) == V
.PAWN
&& Math
.abs(sx
- ex
) >= 2)
81 const step
= (ex
-sx
) / Math
.abs(ex
-sx
);
86 if (sx
+ 2*step
!= ex
) //3-squares move
95 return undefined; //default
98 getPotentialMovesFrom([x
,y
])
100 switch (this.getPiece(x
,y
))
103 return this.getPotentialCamelMoves([x
,y
]);
105 return this.getPotentialWildebeestMoves([x
,y
]);
107 return super.getPotentialMovesFrom([x
,y
])
111 // Pawns jump 2 or 3 squares, and promote to queen or wildebeest
112 getPotentialPawnMoves([x
,y
])
114 const color
= this.turn
;
116 const [sizeX
,sizeY
] = [V
.size
.x
,V
.size
.y
];
117 const shiftX
= (color
== "w" ? -1 : 1);
118 const startRanks
= (color
== "w" ? [sizeX
-2,sizeX
-3] : [1,2]);
119 const lastRank
= (color
== "w" ? 0 : sizeX
-1);
120 const finalPieces
= x
+ shiftX
== lastRank
121 ? [V
.ROOK
,V
.KNIGHT
,V
.BISHOP
,V
.QUEEN
]
124 if (this.board
[x
+shiftX
][y
] == V
.EMPTY
)
126 // One square forward
127 for (let piece
of finalPieces
)
128 moves
.push(this.getBasicMove([x
,y
], [x
+shiftX
,y
], {c:color
,p:piece
}));
129 if (startRanks
.includes(x
))
131 if (this.board
[x
+2*shiftX
][y
] == V
.EMPTY
)
134 moves
.push(this.getBasicMove([x
,y
], [x
+2*shiftX
,y
]));
135 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])
146 if (y
+ shiftY
>= 0 && y
+ shiftY
< sizeY
147 && this.board
[x
+shiftX
][y
+shiftY
] != V
.EMPTY
148 && this.canTake([x
,y
], [x
+shiftX
,y
+shiftY
]))
150 for (let piece
of finalPieces
)
152 moves
.push(this.getBasicMove([x
,y
], [x
+shiftX
,y
+shiftY
],
159 const Lep
= this.epSquares
.length
;
160 const epSquare
= this.epSquares
[Lep
-1];
163 for (let epsq
of epSquare
)
165 // TODO: some redundant checks
166 if (epsq
.x
== x
+shiftX
&& Math
.abs(epsq
.y
- y
) == 1)
168 var enpassantMove
= this.getBasicMove([x
,y
], [epsq
.x
,epsq
.y
]);
169 // WARNING: the captured pawn may be diagonally behind us,
170 // if it's a 3-squares jump and we take on 1st passing square
171 const px
= (this.board
[x
][epsq
.y
] != V
.EMPTY
? x : x
- shiftX
);
172 enpassantMove
.vanish
.push({
176 c: this.getColor(px
,epsq
.y
)
178 moves
.push(enpassantMove
);
186 // TODO: wildebeest castle
188 getPotentialCamelMoves(sq
)
190 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.CAMEL
], "oneStep");
193 getPotentialWildebeestMoves(sq
)
195 return this.getSlideNJumpMoves(
196 sq
, V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.CAMEL
]), "oneStep");
199 isAttacked(sq
, colors
)
201 return super.isAttacked(sq
, colors
)
202 || this.isAttackedByCamel(sq
, colors
)
203 || this.isAttackedByWildebeest(sq
, colors
);
206 isAttackedByCamel(sq
, colors
)
208 return this.isAttackedBySlideNJump(sq
, colors
,
209 V
.CAMEL
, V
.steps
[V
.CAMEL
], "oneStep");
212 isAttackedByWildebeest(sq
, colors
)
214 return this.isAttackedBySlideNJump(sq
, colors
, V
.WILDEBEEST
,
215 V
.steps
[V
.KNIGHT
].concat(V
.steps
[V
.CAMEL
]), "oneStep");
220 if (this.atLeastOneMove()) // game not over
223 // No valid move: game is lost (stalemate is a win)
224 return (this.turn
== "w" ? "0-1" : "1-0");
227 static get VALUES() {
228 return Object
.assign(
230 {'c': 3, 'w': 7} //experimental
234 static get SEARCH_DEPTH() { return 2; }
236 static GenRandInitFen()
238 let pieces
= { "w": new Array(10), "b": new Array(10) };
239 for (let c
of ["w","b"])
241 let positions
= ArrayFun
.range(11);
243 // Get random squares for bishops + camels (different colors)
244 let randIndexes
= sample(ArrayFun
.range(6), 2)
245 .map(i
=> { return 2*i
; });
246 let bishop1Pos
= positions
[randIndexes
[0]];
247 let camel1Pos
= positions
[randIndexes
[1]];
248 // The second bishop (camel) must be on a square of different color
249 let randIndexes_tmp
= sample(ArrayFun
.range(5), 2)
250 .map(i
=> { return 2*i
+1; });
251 let bishop2Pos
= positions
[randIndexes_tmp
[0]];
252 let camel2Pos
= positions
[randIndexes_tmp
[1]];
253 for (let idx
of randIndexes
.concat(randIndexes_tmp
)
254 .sort((a
,b
) => { return b
-a
; })) //largest indices first
256 positions
.splice(idx
, 1);
259 let randIndex
= randInt(7);
260 let knight1Pos
= positions
[randIndex
];
261 positions
.splice(randIndex
, 1);
262 randIndex
= randInt(6);
263 let knight2Pos
= positions
[randIndex
];
264 positions
.splice(randIndex
, 1);
266 randIndex
= randInt(5);
267 let queenPos
= positions
[randIndex
];
268 positions
.splice(randIndex
, 1);
270 // Random square for wildebeest
271 randIndex
= randInt(4);
272 let wildebeestPos
= positions
[randIndex
];
273 positions
.splice(randIndex
, 1);
275 let rook1Pos
= positions
[0];
276 let kingPos
= positions
[1];
277 let rook2Pos
= positions
[2];
279 pieces
[c
][rook1Pos
] = 'r';
280 pieces
[c
][knight1Pos
] = 'n';
281 pieces
[c
][bishop1Pos
] = 'b';
282 pieces
[c
][queenPos
] = 'q';
283 pieces
[c
][camel1Pos
] = 'c';
284 pieces
[c
][camel2Pos
] = 'c';
285 pieces
[c
][wildebeestPos
] = 'w';
286 pieces
[c
][kingPos
] = 'k';
287 pieces
[c
][bishop2Pos
] = 'b';
288 pieces
[c
][knight2Pos
] = 'n';
289 pieces
[c
][rook2Pos
] = 'r';
291 return pieces
["b"].join("") +
292 "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" +
293 pieces
["w"].join("").toUpperCase() +