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() {
12 ChessRules
.PawnSpecs
.promotions
.concat(
13 [V
.C_ROOK
, V
.C_KNIGHT
, V
.C_BISHOP
, V
.C_QUEEN
])
19 if ([V
.C_ROOK
, V
.C_KNIGHT
, V
.C_BISHOP
, V
.C_QUEEN
].includes(b
[1]))
20 return "Colorbound/" + b
;
24 static GenRandInitFen(randomness
) {
26 return "dhaskahd/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -";
28 // Mapping white --> black (at least at start):
37 let pieces
= { w: new Array(8), b: new Array(8) };
39 // Shuffle pieces on first (and last rank if randomness == 2)
40 for (let c
of ["w", "b"]) {
41 if (c
== 'b' && randomness
== 1) {
42 pieces
['b'] = pieces
['w'].map(p
=> piecesMap
[p
]);
47 // TODO: same code as in base_rules. Should extract and factorize?
49 let positions
= ArrayFun
.range(8);
51 let randIndex
= 2 * randInt(4);
52 const bishop1Pos
= positions
[randIndex
];
53 let randIndex_tmp
= 2 * randInt(4) + 1;
54 const bishop2Pos
= positions
[randIndex_tmp
];
55 positions
.splice(Math
.max(randIndex
, randIndex_tmp
), 1);
56 positions
.splice(Math
.min(randIndex
, randIndex_tmp
), 1);
58 randIndex
= randInt(6);
59 const knight1Pos
= positions
[randIndex
];
60 positions
.splice(randIndex
, 1);
61 randIndex
= randInt(5);
62 const knight2Pos
= positions
[randIndex
];
63 positions
.splice(randIndex
, 1);
65 randIndex
= randInt(4);
66 const queenPos
= positions
[randIndex
];
67 positions
.splice(randIndex
, 1);
69 const rook1Pos
= positions
[0];
70 const kingPos
= positions
[1];
71 const rook2Pos
= positions
[2];
73 pieces
[c
][rook1Pos
] = "r";
74 pieces
[c
][knight1Pos
] = "n";
75 pieces
[c
][bishop1Pos
] = "b";
76 pieces
[c
][queenPos
] = "q";
77 pieces
[c
][kingPos
] = "k";
78 pieces
[c
][bishop2Pos
] = "b";
79 pieces
[c
][knight2Pos
] = "n";
80 pieces
[c
][rook2Pos
] = "r";
81 if (c
== 'b') pieces
[c
] = pieces
[c
].map(p
=> piecesMap
[p
]);
82 flags
+= V
.CoordToColumn(rook1Pos
) + V
.CoordToColumn(rook2Pos
);
84 // Add turn + flags + enpassant
86 pieces
["b"].join("") +
87 "/pppppppp/8/8/8/8/PPPPPPPP/" +
88 pieces
["w"].join("").toUpperCase() +
89 " w 0 " + flags
+ " -"
96 static get C_KNIGHT() {
99 static get C_BISHOP() {
102 static get C_QUEEN() {
106 static get PIECES() {
108 ChessRules
.PIECES
.concat([V
.C_ROOK
, V
.C_KNIGHT
, V
.C_BISHOP
, V
.C_QUEEN
])
112 getPotentialMovesFrom([x
, y
]) {
113 switch (this.getPiece(x
, y
)) {
115 return this.getPotentialC_rookMoves([x
, y
]);
117 return this.getPotentialC_knightMoves([x
, y
]);
119 return this.getPotentialC_bishopMoves([x
, y
]);
121 return this.getPotentialC_queenMoves([x
, y
]);
123 return super.getPotentialMovesFrom([x
, y
]);
129 return Object
.assign(
158 getPotentialC_rookMoves(sq
) {
160 this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]).concat(
161 this.getSlideNJumpMoves(sq
, V
.steps
['d'], "oneStep"))
165 getPotentialC_knightMoves(sq
) {
167 this.getSlideNJumpMoves(sq
, V
.steps
['a'], "oneStep").concat(
168 this.getSlideNJumpMoves(sq
, V
.steps
[V
.ROOK
], "oneStep"))
172 getPotentialC_bishopMoves(sq
) {
174 this.getSlideNJumpMoves(sq
, V
.steps
['d'], "oneStep").concat(
175 this.getSlideNJumpMoves(sq
, V
.steps
['a'], "oneStep")).concat(
176 this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
], "oneStep"))
180 getPotentialC_queenMoves(sq
) {
182 this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]).concat(
183 this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep"))
187 // TODO: really find a way to avoid duolicating most of the castling code
188 // each time: here just the queenside castling squares change for black.
189 getCastleMoves([x
, y
]) {
190 const c
= this.getColor(x
, y
);
191 if (x
!= (c
== "w" ? V
.size
.x
- 1 : 0) || y
!= this.INIT_COL_KING
[c
])
194 const oppCol
= V
.GetOppCol(c
);
198 const finalSquares
= [
199 // Black castle long in an unusual way:
200 (c
== 'w' ? [2, 3] : [1, 2]),
201 [V
.size
.y
- 2, V
.size
.y
- 3]
206 castleSide
++ //large, then small
208 if (this.castleFlags
[c
][castleSide
] >= V
.size
.y
) continue;
210 const rookPos
= this.castleFlags
[c
][castleSide
];
211 const castlingPiece
= this.getPiece(x
, rookPos
);
212 const finDist
= finalSquares
[castleSide
][0] - y
;
213 let step
= finDist
/ Math
.max(1, Math
.abs(finDist
));
217 this.isAttacked([x
, i
], oppCol
) ||
218 (this.board
[x
][i
] != V
.EMPTY
&&
219 (this.getColor(x
, i
) != c
||
220 ![V
.KING
, castlingPiece
].includes(this.getPiece(x
, i
))))
222 continue castlingCheck
;
225 } while (i
!= finalSquares
[castleSide
][0]);
227 step
= castleSide
== 0 ? -1 : 1;
228 for (i
= y
+ step
; i
!= rookPos
; i
+= step
) {
229 if (this.board
[x
][i
] != V
.EMPTY
) continue castlingCheck
;
232 for (i
= 0; i
< 2; i
++) {
234 finalSquares
[castleSide
][i
] != rookPos
&&
235 this.board
[x
][finalSquares
[castleSide
][i
]] != V
.EMPTY
&&
237 this.getPiece(x
, finalSquares
[castleSide
][i
]) != V
.KING
||
238 this.getColor(x
, finalSquares
[castleSide
][i
]) != c
241 continue castlingCheck
;
250 y: finalSquares
[castleSide
][0],
256 y: finalSquares
[castleSide
][1],
262 new PiPo({ x: x
, y: y
, p: V
.KING
, c: c
}),
263 new PiPo({ x: x
, y: rookPos
, p: castlingPiece
, c: c
})
266 Math
.abs(y
- rookPos
) <= 2
267 ? { x: x
, y: rookPos
}
268 : { x: x
, y: y
+ 2 * (castleSide
== 0 ? -1 : 1) }
276 isAttacked(sq
, color
) {
278 super.isAttacked(sq
, color
) ||
279 this.isAttackedByC_rook(sq
, color
) ||
280 this.isAttackedByC_knight(sq
, color
) ||
281 this.isAttackedByC_bishop(sq
, color
) ||
282 this.isAttackedByC_queen(sq
, color
)
286 isAttackedByC_rook(sq
, color
) {
288 this.isAttackedBySlideNJump(sq
, color
, V
.C_ROOK
, V
.steps
[V
.BISHOP
]) ||
289 this.isAttackedBySlideNJump(
290 sq
, color
, V
.C_ROOK
, V
.steps
['d'], "oneStep")
294 isAttackedByC_knight(sq
, color
) {
296 this.isAttackedBySlideNJump(
297 sq
, color
, V
.C_KNIGHT
, V
.steps
[V
.ROOK
], "oneStep") ||
298 this.isAttackedBySlideNJump(
299 sq
, color
, V
.C_KNIGHT
, V
.steps
['a'], "oneStep")
303 isAttackedByC_bishop(sq
, color
) {
305 this.isAttackedBySlideNJump(
306 sq
, color
, V
.C_BISHOP
, V
.steps
['d'], "oneStep") ||
307 this.isAttackedBySlideNJump(
308 sq
, color
, V
.C_BISHOP
, V
.steps
['a'], "oneStep") ||
309 this.isAttackedBySlideNJump(
310 sq
, color
, V
.C_BISHOP
, V
.steps
['f'], "oneStep")
314 isAttackedByC_queen(sq
, color
) {
316 this.isAttackedBySlideNJump(sq
, color
, V
.C_QUEEN
, V
.steps
[V
.BISHOP
]) ||
317 this.isAttackedBySlideNJump(
318 sq
, color
, V
.C_QUEEN
, V
.steps
[V
.KNIGHT
], "oneStep")
322 static get VALUES() {
323 return Object
.assign(