1 import { ChessRules
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { shuffle
} from "@/utils/alea";
5 export class SuicideRules
extends ChessRules
{
6 static get HasFlags() {
10 static get HasCastle() {
14 static get PawnSpecs() {
18 { promotions: ChessRules
.PawnSpecs
.promotions
.concat([V
.KING
]) }
22 static IsGoodPosition(position
) {
23 if (position
.length
== 0) return false;
24 const rows
= position
.split("/");
25 if (rows
.length
!= V
.size
.x
) return false;
26 // Just check that at least one piece of each color is there:
27 let pieces
= { "w": 0, "b": 0 };
28 for (let row
of rows
) {
30 for (let i
= 0; i
< row
.length
; i
++) {
31 const lowerRi
= row
[i
].toLowerCase();
32 if (V
.PIECES
.includes(lowerRi
)) {
33 pieces
[row
[i
] == lowerRi
? "b" : "w"]++;
36 const num
= parseInt(row
[i
]);
37 if (isNaN(num
)) return false;
41 if (sumElts
!= V
.size
.y
) return false;
43 if (Object
.values(pieces
).some(v
=> v
== 0)) return false;
49 // Trim all non-capturing moves (not the most efficient, but easy)
50 static KeepCaptures(moves
) {
51 return moves
.filter(m
=> m
.vanish
.length
== 2);
54 // Stop at the first capture found (if any)
56 const color
= this.turn
;
57 const oppCol
= V
.GetOppCol(color
);
58 for (let i
= 0; i
< V
.size
.x
; i
++) {
59 for (let j
= 0; j
< V
.size
.y
; j
++) {
61 this.board
[i
][j
] != V
.EMPTY
&&
62 this.getColor(i
, j
) != oppCol
&&
63 this.getPotentialMovesFrom([i
, j
]).some(m
=> m
.vanish
.length
== 2)
72 getPossibleMovesFrom(sq
) {
73 let moves
= this.getPotentialMovesFrom(sq
);
74 const captureMoves
= V
.KeepCaptures(moves
);
75 if (captureMoves
.length
> 0) return captureMoves
;
76 if (this.atLeastOneCapture()) return [];
85 const moves
= super.getAllValidMoves();
86 if (moves
.some(m
=> m
.vanish
.length
== 2)) return V
.KeepCaptures(moves
);
91 const color
= this.turn
;
92 for (let i
= 0; i
< V
.size
.x
; i
++) {
93 for (let j
= 0; j
< V
.size
.y
; j
++) {
95 this.getColor(i
, j
) == color
&&
96 this.getPotentialMovesFrom([i
, j
]).length
> 0
109 // No variables update because no royal king + no castling
116 if (this.atLeastOneMove()) return "*";
117 // No valid move: the side who cannot move wins
118 return this.turn
== "w" ? "1-0" : "0-1";
121 static get VALUES() {
132 static get SEARCH_DEPTH() {
137 // Less material is better:
138 return -super.evalPosition();
141 static GenRandInitFen(randomness
) {
143 return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
145 let pieces
= { w: new Array(8), b: new Array(8) };
146 for (let c
of ["w", "b"]) {
147 if (c
== 'b' && randomness
== 1) {
148 pieces
['b'] = pieces
['w'];
152 // Get random squares for every piece, totally freely
153 let positions
= shuffle(ArrayFun
.range(8));
154 const composition
= ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
155 const rem2
= positions
[0] % 2;
156 if (rem2
== positions
[1] % 2) {
157 // Fix bishops (on different colors)
158 for (let i
=2; i
<8; i
++) {
159 if (positions
[i
] % 2 != rem2
)
160 [positions
[1], positions
[i
]] = [positions
[i
], positions
[1]];
163 for (let i
= 0; i
< 8; i
++) pieces
[c
][positions
[i
]] = composition
[i
];
166 pieces
["b"].join("") +
167 "/pppppppp/8/8/8/8/PPPPPPPP/" +
168 pieces
["w"].join("").toUpperCase() +
169 // En-passant allowed, but no flags