1 import ChessRules
from "/base_rules.js";
2 import GiveawayRules
from "/variants/Giveaway/class.js";
3 import {Random
} from "/utils/alea.js";
4 import PiPo
from "/utils/PiPo.js";
5 import Move
from "/utils/Move.js";
7 export default class BaroqueRules
extends ChessRules
{
11 select: C
.Options
.Select
,
14 label: "Capture king",
40 genRandInitBaseFen() {
41 if (this.options
["randomness"] == 0)
42 return "rnbkqbnm/pppppppp/8/8/8/8/PPPPPPPP/MNBQKBNR";
43 const options
= Object
.assign({mode: "suicide"}, this.options
);
44 const gr
= new GiveawayRules({options: options
, genFenOnly: true});
45 let res
= gr
.genRandInitBaseFen();
47 for (let c
of ['w', 'b']) {
48 const rookChar
= (c
== 'w' ? 'R' : 'r');
49 switch (Random
.randInt(2)) {
51 immPos
[c
] = res
.fen
.indexOf(rookChar
);
54 immPos
[c
] = res
.fen
.lastIndexOf(rookChar
);
58 res
.fen
= res
.fen
.substring(0, immPos
['b']) + 'i' +
59 res
.fen
.substring(immPos
['b'] + 1, immPos
['w']) + 'I' +
60 res
.fen
.substring(immPos
['w'] + 1);
64 // Although other pieces keep their names here for coding simplicity,
66 // - a "rook" is a coordinator, capturing by coordinating with the king
67 // - a "knight" is a long-leaper, capturing as in draughts
68 // - a "bishop" is a chameleon, capturing as its prey
69 // - a "queen" is a withdrawer, capturing by moving away from pieces
72 return Object
.assign({},
78 {steps: [[0, 1], [0, -1], [1, 0], [-1, 0]]}
86 [1, 0], [0, 1], [-1, 0], [0, -1],
87 [1, 1], [1, -1], [-1, 1], [-1, -1]
105 "class": "immobilizer",
112 // Is piece on square (x,y) immobilized?
113 isImmobilized([x
, y
]) {
114 const piece
= this.getPiece(x
, y
);
115 const color
= this.getColor(x
, y
);
116 const oppCol
= C
.GetOppCol(color
);
117 const adjacentSteps
= this.pieces()['k'].moves
[0].steps
;
118 for (let step
of adjacentSteps
) {
119 const [i
, j
] = [x
+ step
[0], this.getY(y
+ step
[1])];
121 this.onBoard(i
, j
) &&
122 this.board
[i
][j
] != "" &&
123 this.getColor(i
, j
) == oppCol
125 const oppPiece
= this.getPiece(i
, j
);
126 if (oppPiece
== 'i') {
127 // Moving is possible only if this immobilizer is neutralized
128 for (let step2
of adjacentSteps
) {
129 const [i2
, j2
] = [i
+ step2
[0], this.getY(j
+ step2
[1])];
130 if (i2
== x
&& j2
== y
)
131 continue; //skip initial piece!
133 this.onBoard(i2
, j2
) &&
134 this.board
[i2
][j2
] != "" &&
135 this.getColor(i2
, j2
) == color
137 if (['b', 'i'].includes(this.getPiece(i2
, j2
)))
141 return true; //immobilizer isn't neutralized
143 // Chameleons can't be immobilized twice,
144 // because there is only one immobilizer
145 if (oppPiece
== 'b' && piece
== 'i')
152 canTake([x1
, y1
], [x2
, y2
]) {
153 // Deactivate standard captures, except for king:
155 this.getPiece(x1
, y1
) == 'k' &&
156 this.getColor(x1
, y1
) != this.getColor(x2
, y2
)
160 postProcessPotentialMoves(moves
) {
161 if (moves
.length
== 0)
163 switch (moves
[0].vanish
[0].p
) {
165 this.addPawnCaptures(moves
);
168 this.addRookCaptures(moves
);
171 const [x
, y
] = [moves
[0].start
.x
, moves
[0].start
.y
];
172 moves
= moves
.concat(this.getKnightCaptures([x
, y
]));
175 moves
= this.getBishopCaptures(moves
);
178 this.addQueenCaptures(moves
);
184 // Modify capturing moves among listed pawn moves
185 addPawnCaptures(moves
, byChameleon
) {
186 const steps
= this.pieces()['p'].moves
[0].steps
;
187 const color
= this.turn
;
188 const oppCol
= C
.GetOppCol(color
);
190 if (byChameleon
&& m
.start
.x
!= m
.end
.x
&& m
.start
.y
!= m
.end
.y
)
191 // Chameleon not moving as pawn
193 // Try capturing in every direction
194 for (let step
of steps
) {
195 const sq2
= [m
.end
.x
+ 2 * step
[0], this.getY(m
.end
.y
+ 2 * step
[1])];
197 this.onBoard(sq2
[0], sq2
[1]) &&
198 this.board
[sq2
[0]][sq2
[1]] != "" &&
199 this.getColor(sq2
[0], sq2
[1]) == color
202 const sq1
= [m
.end
.x
+ step
[0], this.getY(m
.end
.y
+ step
[1])];
204 this.board
[sq1
[0]][sq1
[1]] != "" &&
205 this.getColor(sq1
[0], sq1
[1]) == oppCol
207 const piece1
= this.getPiece(sq1
[0], sq1
[1]);
208 if (!byChameleon
|| piece1
== 'p') {
224 addRookCaptures(moves
, byChameleon
) {
225 const color
= this.turn
;
226 const oppCol
= V
.GetOppCol(color
);
227 const kp
= this.searchKingPos(color
)[0];
229 // Check piece-king rectangle (if any) corners for enemy pieces
230 if (m
.end
.x
== kp
[0] || m
.end
.y
== kp
[1])
231 return; //"flat rectangle"
232 const corner1
= [m
.end
.x
, kp
[1]];
233 const corner2
= [kp
[0], m
.end
.y
];
234 for (let [i
, j
] of [corner1
, corner2
]) {
235 if (this.board
[i
][j
] != "" && this.getColor(i
, j
) == oppCol
) {
236 const piece
= this.getPiece(i
, j
);
237 if (!byChameleon
|| piece
== 'r') {
252 getKnightCaptures(startSquare
, byChameleon
) {
253 // Look in every direction for captures
254 const steps
= this.pieces()['r'].moves
[0].steps
;
255 const color
= this.turn
;
256 const oppCol
= C
.GetOppCol(color
);
258 const [x
, y
] = [startSquare
[0], startSquare
[1]];
259 const piece
= this.getPiece(x
, y
); //might be a chameleon!
260 outerLoop: for (let step
of steps
) {
261 let [i
, j
] = [x
+ step
[0], this.getY(y
+ step
[1])];
262 while (this.onBoard(i
, j
) && this.board
[i
][j
] == "")
263 [i
, j
] = [i
+ step
[0], this.getY(j
+ step
[1])];
265 !this.onBoard(i
, j
) ||
266 this.getColor(i
, j
) == color
||
267 (byChameleon
&& this.getPiece(i
, j
) != 'n')
271 // last(thing), cur(thing) : stop if "cur" is our color,
272 // or beyond board limits, or if "last" isn't empty and cur neither.
273 // Otherwise, if cur is empty then add move until cur square;
274 // if cur is occupied then stop if !!byChameleon and the square not
275 // occupied by a leaper.
277 let cur
= [i
+ step
[0], this.getY(j
+ step
[1])];
278 let vanished
= [new PiPo({x: x
, y: y
, c: color
, p: piece
})];
279 while (this.onBoard(cur
[0], cur
[1])) {
280 if (this.board
[last
[0]][last
[1]] != "") {
281 const oppPiece
= this.getPiece(last
[0], last
[1]);
282 if (!!byChameleon
&& oppPiece
!= 'n')
286 new PiPo({x: last
[0], y: last
[1], c: oppCol
, p: oppPiece
})
289 if (this.board
[cur
[0]][cur
[1]] != "") {
291 this.getColor(cur
[0], cur
[1]) == color
||
292 this.board
[last
[0]][last
[1]] != ""
294 //TODO: redundant test
301 appear: [new PiPo({x: cur
[0], y: cur
[1], c: color
, p: piece
})],
302 vanish: JSON
.parse(JSON
.stringify(vanished
)), //TODO: required?
304 end: {x: cur
[0], y: cur
[1]}
308 last
= [last
[0] + step
[0], this.getY(last
[1] + step
[1])];
309 cur
= [cur
[0] + step
[0], this.getY(cur
[1] + step
[1])];
316 getBishopCaptures(moves
) {
317 const [x
, y
] = [moves
[0].start
.x
, moves
[0].start
.y
];
318 moves
= moves
.concat(this.getKnightCaptures([x
, y
], "asChameleon"));
319 // No "king capture" because king cannot remain under check
320 this.addPawnCaptures(moves
, "asChameleon");
321 this.addRookCaptures(moves
, "asChameleon");
322 this.addQueenCaptures(moves
, "asChameleon");
323 // Post-processing: merge similar moves, concatenating vanish arrays
324 let mergedMoves
= {};
326 const key
= m
.end
.x
+ this.size
.x
* m
.end
.y
;
327 if (!mergedMoves
[key
])
328 mergedMoves
[key
] = m
;
330 for (let i
= 1; i
< m
.vanish
.length
; i
++)
331 mergedMoves
[key
].vanish
.push(m
.vanish
[i
]);
334 return Object
.values(mergedMoves
);
337 addQueenCaptures(moves
, byChameleon
) {
338 if (moves
.length
== 0)
340 const [x
, y
] = [moves
[0].start
.x
, moves
[0].start
.y
];
341 const adjacentSteps
= this.pieces()['r'].moves
[0].steps
;
342 let capturingDirections
= {};
343 const color
= this.turn
;
344 const oppCol
= C
.GetOppCol(color
);
345 adjacentSteps
.forEach(step
=> {
346 const [i
, j
] = [x
- step
[0], this.getY(y
- step
[1])];
348 this.onBoard(i
, j
) &&
349 this.board
[i
][j
] != "" &&
350 this.getColor(i
, j
) == oppCol
&&
351 (!byChameleon
|| this.getPiece(i
, j
) == 'q')
353 capturingDirections
[step
[0] + "." + step
[1]] = true;
358 m
.end
.x
!= x
? (m
.end
.x
- x
) / Math
.abs(m
.end
.x
- x
) : 0,
359 m
.end
.y
!= y
? (m
.end
.y
- y
) / Math
.abs(m
.end
.y
- y
) : 0
361 if (capturingDirections
[step
[0] + "." + step
[1]]) {
362 const [i
, j
] = [x
- step
[0], this.getY(y
- step
[1])];
367 p: this.getPiece(i
, j
),
375 underAttack([x
, y
], oppCol
) {
376 // Generate all potential opponent moves, check if king captured.
377 // TODO: do it more efficiently.
378 const color
= this.getColor(x
, y
);
379 for (let i
= 0; i
< this.size
.x
; i
++) {
380 for (let j
= 0; j
< this.size
.y
; j
++) {
382 this.board
[i
][j
] != "" && this.getColor(i
, j
) == oppCol
&&
383 this.getPotentialMovesFrom([i
, j
]).some(m
=> {
385 m
.vanish
.length
>= 2 &&
386 [1, m
.vanish
.length
- 1].some(k
=> m
.vanish
[k
].p
== 'k')