1 import { ChessRules
, Move
, PiPo
} from "@/base_rules";
2 import { ArrayFun
} from "@/utils/array";
3 import { randInt
} from "@/utils/alea";
5 export class ColorboundRules
extends ChessRules
{
6 static get PawnSpecs() {
10 { promotions: V
.PIECES
}
15 if ([V
.C_ROOK
, V
.C_KNIGHT
, V
.C_BISHOP
, V
.C_QUEEN
].includes(b
[1]))
16 return "Colorbound/" + b
;
20 static GenRandInitFen(randomness
) {
22 return "dhaskahd/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -";
24 // Mapping white --> black (at least at start):
33 let pieces
= { w: new Array(8), b: new Array(8) };
35 // Shuffle pieces on first (and last rank if randomness == 2)
36 for (let c
of ["w", "b"]) {
37 if (c
== 'b' && randomness
== 1) {
38 pieces
['b'] = pieces
['w'].map(p
=> piecesMap
[p
]);
43 // TODO: same code as in base_rules. Should extract and factorize?
45 let positions
= ArrayFun
.range(8);
47 let randIndex
= 2 * randInt(4);
48 const bishop1Pos
= positions
[randIndex
];
49 let randIndex_tmp
= 2 * randInt(4) + 1;
50 const bishop2Pos
= positions
[randIndex_tmp
];
51 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
52 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
54 randIndex
= randInt(6);
55 const knight1Pos
= positions
[randIndex
];
56 positions
.splice(randIndex
, 1);
57 randIndex
= randInt(5);
58 const knight2Pos
= positions
[randIndex
];
59 positions
.splice(randIndex
, 1);
61 randIndex
= randInt(4);
62 const queenPos
= positions
[randIndex
];
63 positions
.splice(randIndex
, 1);
65 const rook1Pos
= positions
[0];
66 const kingPos
= positions
[1];
67 const rook2Pos
= positions
[2];
69 pieces
[c
][rook1Pos
] = "r";
70 pieces
[c
][knight1Pos
] = "n";
71 pieces
[c
][bishop1Pos
] = "b";
72 pieces
[c
][queenPos
] = "q";
73 pieces
[c
][kingPos
] = "k";
74 pieces
[c
][bishop2Pos
] = "b";
75 pieces
[c
][knight2Pos
] = "n";
76 pieces
[c
][rook2Pos
] = "r";
77 if (c
== 'b') pieces
[c
] = pieces
[c
].map(p
=> piecesMap
[p
]);
78 flags
+= V
.CoordToColumn(rook1Pos
) + V
.CoordToColumn(rook2Pos
);
80 // Add turn + flags + enpassant
82 pieces
["b"].join("") +
83 "/pppppppp/8/8/8/8/PPPPPPPP/" +
84 pieces
["w"].join("").toUpperCase() +
85 " w 0 " + flags
+ " -"
92 static get C_KNIGHT() {
95 static get C_BISHOP() {
98 static get C_QUEEN() {
102 static get PIECES() {
104 ChessRules
.PIECES
.concat([V
.C_ROOK
, V
.C_KINGHT
, V
.C_BISHOP
, V
.C_QUEEN
])
108 getPotentialMovesFrom([x
, y
]) {
109 switch (this.getPiece(x
, y
)) {
111 return this.getPotentialC_rookMoves([x
, y
]);
113 return this.getPotentialC_knightMoves([x
, y
]);
115 return this.getPotentialC_bishopMoves([x
, y
]);
117 return this.getPotentialC_queenMoves([x
, y
]);
119 return super.getPotentialMovesFrom([x
, y
]);
125 return Object
.assign(
154 getPotentialC_rookMoves(sq
) {
156 this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]).concat(
157 this.getSlideNJumpMoves(sq
, V
.steps
['d'], "oneStep"))
161 getPotentialC_knightMoves(sq
) {
163 this.getSlideNJumpMoves(sq
, V
.steps
['a'], "oneStep").concat(
164 this.getSlideNJumpMoves(sq
, V
.steps
[V
.ROOK
], "oneStep"))
168 getPotentialC_bishopMoves(sq
) {
170 this.getSlideNJumpMoves(sq
, V
.steps
['d'], "oneStep").concat(
171 this.getSlideNJumpMoves(sq
, V
.steps
['a'], "oneStep")).concat(
172 this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
], "oneStep"))
176 getPotentialC_queenMoves(sq
) {
178 this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]).concat(
179 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep"))
183 // TODO: really find a way to avoid duolicating most of the castling code
184 // each time: here just the queenside castling squares change for black.
185 getCastleMoves([x
, y
]) {
186 const c
= this.getColor(x
, y
);
187 if (x
!= (c
== "w" ? V
.size
.x
- 1 : 0) || y
!= this.INIT_COL_KING
[c
])
190 const oppCol
= V
.GetOppCol(c
);
194 const finalSquares
= [
195 // Black castle long in an unusual way:
196 (c
== 'w' ? [2, 3] : [1, 2]),
197 [V
.size
.y
- 2, V
.size
.y
- 3]
202 castleSide
++ //large, then small
204 if (this.castleFlags
[c
][castleSide
] >= V
.size
.y
) continue;
206 const rookPos
= this.castleFlags
[c
][castleSide
];
207 const castlingPiece
= this.getPiece(x
, rookPos
);
208 const finDist
= finalSquares
[castleSide
][0] - y
;
209 let step
= finDist
/ Math
.max(1, Math
.abs(finDist
));
213 this.isAttacked([x
, i
], oppCol
) ||
214 (this.board
[x
][i
] != V
.EMPTY
&&
215 (this.getColor(x
, i
) != c
||
216 ![V
.KING
, castlingPiece
].includes(this.getPiece(x
, i
))))
218 continue castlingCheck
;
221 } while (i
!= finalSquares
[castleSide
][0]);
223 step
= castleSide
== 0 ? -1 : 1;
224 for (i
= y
+ step
; i
!= rookPos
; i
+= step
) {
225 if (this.board
[x
][i
] != V
.EMPTY
) continue castlingCheck
;
228 for (i
= 0; i
< 2; i
++) {
230 finalSquares
[castleSide
][i
] != rookPos
&&
231 this.board
[x
][finalSquares
[castleSide
][i
]] != V
.EMPTY
&&
233 this.getPiece(x
, finalSquares
[castleSide
][i
]) != V
.KING
||
234 this.getColor(x
, finalSquares
[castleSide
][i
]) != c
237 continue castlingCheck
;
246 y: finalSquares
[castleSide
][0],
252 y: finalSquares
[castleSide
][1],
258 new PiPo({ x: x
, y: y
, p: V
.KING
, c: c
}),
259 new PiPo({ x: x
, y: rookPos
, p: castlingPiece
, c: c
})
262 Math
.abs(y
- rookPos
) <= 2
263 ? { x: x
, y: rookPos
}
264 : { x: x
, y: y
+ 2 * (castleSide
== 0 ? -1 : 1) }
272 isAttacked(sq
, color
) {
274 super.isAttacked(sq
, color
) ||
275 this.isAttackedByC_rook(sq
, color
) ||
276 this.isAttackedByC_knight(sq
, color
) ||
277 this.isAttackedByC_bishop(sq
, color
) ||
278 this.isAttackedByC_queen(sq
, color
)
282 isAttackedByC_rook(sq
, color
) {
284 this.isAttackedBySlideNJump(sq
, color
, V
.C_ROOK
, V
.steps
[V
.BISHOP
]) ||
285 this.isAttackedBySlideNJump(
286 sq
, color
, V
.C_ROOK
, V
.steps
['d'], "oneStep")
290 isAttackedByC_knight(sq
, color
) {
292 this.isAttackedBySlideNJump(
293 sq
, color
, V
.C_KNIGHT
, V
.steps
[V
.ROOK
], "oneStep") ||
294 this.isAttackedBySlideNJump(
295 sq
, color
, V
.C_KNIGHT
, V
.steps
['a'], "oneStep")
299 isAttackedByC_bishop(sq
, color
) {
301 this.isAttackedBySlideNJump(
302 sq
, color
, V
.C_BISHOP
, V
.steps
['d'], "oneStep") ||
303 this.isAttackedBySlideNJump(
304 sq
, color
, V
.C_BISHOP
, V
.steps
['a'], "oneStep") ||
305 this.isAttackedBySlideNJump(
306 sq
, color
, V
.C_BISHOP
, V
.steps
['f'], "oneStep")
310 isAttackedByC_queen(sq
, color
) {
312 this.isAttackedBySlideNJump(sq
, color
, V
.C_QUEEN
, V
.steps
[V
.BISHOP
]) ||
313 this.isAttackedBySlideNJump(
314 sq
, color
, V
.C_ROOK
, V
.steps
[V
.KNIGHT
], "oneStep")
318 static get VALUES() {
319 return Object
.assign(