1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
3 export const VariantRules
= class BenedictRules
extends ChessRules
{
4 static get HasEnpassant() {
8 // TODO(?): some duplicated code in 2 next functions
9 getSlideNJumpMoves([x
, y
], steps
, oneStep
) {
11 outerLoop: for (let loop
= 0; loop
< steps
.length
; loop
++) {
12 const step
= steps
[loop
];
15 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
16 moves
.push(this.getBasicMove([x
, y
], [i
, j
]));
17 if (oneStep
) continue outerLoop
;
21 // No capture check: handled elsewhere (next method)
26 // Find possible captures from a square
27 // follow steps from x,y until something is met.
28 findCaptures([x
, y
]) {
29 const color
= this.getColor(x
, y
);
30 const piece
= this.getPiece(x
, y
);
34 ? [V
.QUEEN
,V
.KING
].includes(piece
)
35 ? V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
])
46 const oneStep
= [V
.KNIGHT
,V
.PAWN
,V
.KING
].includes(piece
);
47 outerLoop: for (let loop
= 0; loop
< steps
.length
; loop
++) {
48 const step
= steps
[loop
];
51 while (V
.OnBoard(i
, j
) && this.board
[i
][j
] == V
.EMPTY
) {
52 if (oneStep
) continue outerLoop
;
58 this.getColor(i
, j
) == V
.GetOppCol(color
)
67 getPotentialPawnMoves([x
, y
]) {
68 const color
= this.getColor(x
, y
);
70 const sizeY
= V
.size
.y
;
71 const shift
= color
== "w" ? -1 : 1;
72 const startRank
= color
== "w" ? sizeY
- 2 : 1;
73 const firstRank
= color
== "w" ? sizeY
- 1 : 0;
74 const lastRank
= color
== "w" ? 0 : sizeY
- 1;
76 if (x
+ shift
!= lastRank
) {
78 if (this.board
[x
+ shift
][y
] == V
.EMPTY
) {
79 moves
.push(this.getBasicMove([x
, y
], [x
+ shift
, y
]));
81 [startRank
, firstRank
].includes(x
) &&
82 this.board
[x
+ 2 * shift
][y
] == V
.EMPTY
85 moves
.push(this.getBasicMove([x
, y
], [x
+ 2 * shift
, y
]));
91 let promotionPieces
= [V
.ROOK
, V
.KNIGHT
, V
.BISHOP
, V
.QUEEN
];
92 promotionPieces
.forEach(p
=> {
94 if (this.board
[x
+ shift
][y
] == V
.EMPTY
)
96 this.getBasicMove([x
, y
], [x
+ shift
, y
], { c: color
, p: p
})
101 // No en passant here
106 getPotentialRookMoves(sq
) {
107 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.ROOK
]);
110 getPotentialKnightMoves(sq
) {
111 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.KNIGHT
], "oneStep");
114 getPotentialBishopMoves(sq
) {
115 return this.getSlideNJumpMoves(sq
, V
.steps
[V
.BISHOP
]);
118 getPotentialQueenMoves(sq
) {
119 return this.getSlideNJumpMoves(
121 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
])
125 getPotentialKingMoves(sq
) {
126 // Initialize with normal (non-capturing) moves
127 let noCaptures
= this.getSlideNJumpMoves(
129 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]),
132 return noCaptures
.concat(this.getCastleMoves(sq
));
135 // No "under check" verifications:
136 getCastleMoves([x
, y
]) {
137 const c
= this.getColor(x
, y
);
138 if (x
!= (c
== "w" ? V
.size
.x
- 1 : 0) || y
!= this.INIT_COL_KING
[c
])
139 return []; //x isn't first rank, or king has moved (shortcut)
142 const oppCol
= V
.GetOppCol(c
);
146 const finalSquares
= [
148 [V
.size
.y
- 2, V
.size
.y
- 3]
153 castleSide
++ //large, then small
155 if (this.castleFlags
[c
][castleSide
] >= 8) continue;
156 // If this code is reached, rooks and king are on initial position
158 const rookPos
= this.castleFlags
[c
][castleSide
];
159 if (this.getColor(x
, rookPos
) != c
)
160 // Rook is here but changed color
163 // Nothing on the path of the king ?
164 const finDist
= finalSquares
[castleSide
][0] - y
;
165 let step
= finDist
/ Math
.max(1, Math
.abs(finDist
));
166 for (let i
= y
; i
!= finalSquares
[castleSide
][0]; i
+= step
) {
168 this.board
[x
][i
] != V
.EMPTY
&&
169 // NOTE: next check is enough, because of chessboard constraints
170 (this.getColor(x
, i
) != c
||
171 ![V
.KING
, V
.ROOK
].includes(this.getPiece(x
, i
)))
173 continue castlingCheck
;
177 // Nothing on the path to the rook?
178 step
= castleSide
== 0 ? -1 : 1;
179 for (i
= y
+ step
; i
!= rookPos
; i
+= step
) {
180 if (this.board
[x
][i
] != V
.EMPTY
) continue castlingCheck
;
183 // Nothing on final squares, except maybe king and castling rook?
184 for (i
= 0; i
< 2; i
++) {
186 this.board
[x
][finalSquares
[castleSide
][i
]] != V
.EMPTY
&&
187 this.getPiece(x
, finalSquares
[castleSide
][i
]) != V
.KING
&&
188 finalSquares
[castleSide
][i
] != rookPos
190 continue castlingCheck
;
194 // If this code is reached, castle is valid
198 new PiPo({ x: x
, y: finalSquares
[castleSide
][0], p: V
.KING
, c: c
}),
199 new PiPo({ x: x
, y: finalSquares
[castleSide
][1], p: V
.ROOK
, c: c
})
202 new PiPo({ x: x
, y: y
, p: V
.KING
, c: c
}),
203 new PiPo({ x: x
, y: rookPos
, p: V
.ROOK
, c: c
})
206 Math
.abs(y
- rookPos
) <= 2
207 ? { x: x
, y: rookPos
}
208 : { x: x
, y: y
+ 2 * (castleSide
== 0 ? -1 : 1) }
216 // TODO: appear/vanish description of a move is too verbose for Benedict.
217 // => Would need a new "flipped" array, to be passed in Game.vue...
218 getPotentialMovesFrom([x
, y
]) {
219 const color
= this.turn
;
220 const oppCol
= V
.GetOppCol(color
);
221 // Get all moves from x,y without captures:
222 let moves
= super.getPotentialMovesFrom([x
, y
]);
227 V
.PlayOnBoard(this.board
, m
);
228 // If castling, m.appear has 2 elements.
229 // In this case, consider the attacks of moving units only.
230 // (Sometimes the king or rook doesn't move).
231 for (let i
= 0; i
< m
.appear
.length
; i
++) {
232 const a
= m
.appear
[i
];
233 if (m
.vanish
[i
].x
!= a
.x
|| m
.vanish
[i
].y
!= a
.y
) {
234 const flipped
= this.findCaptures([a
.x
, a
.y
]);
235 flipped
.forEach(sq
=> {
236 const piece
= this.getPiece(sq
[0],sq
[1]);
237 const pipoA
= new PiPo({
243 const pipoV
= new PiPo({
249 newAppear
.push(pipoA
);
250 newVanish
.push(pipoV
);
254 Array
.prototype.push
.apply(m
.appear
, newAppear
);
255 Array
.prototype.push
.apply(m
.vanish
, newVanish
);
256 V
.UndoOnBoard(this.board
, m
);
261 // Moves cannot flip our king's color, so all are valid
266 // No notion of check here:
271 // Stop at the first move found
273 const color
= this.turn
;
274 const oppCol
= V
.GetOppCol(color
);
275 for (let i
= 0; i
< V
.size
.x
; i
++) {
276 for (let j
= 0; j
< V
.size
.y
; j
++) {
277 if (this.board
[i
][j
] != V
.EMPTY
&& this.getColor(i
, j
) != oppCol
) {
278 const moves
= this.getPotentialMovesFrom([i
, j
]);
279 if (moves
.length
> 0)
288 const color
= this.turn
;
289 // Did a king change color?
290 const kp
= this.kingPos
[color
];
291 if (this.getColor(kp
[0], kp
[1]) != color
)
292 return color
== "w" ? "0-1" : "1-0";
293 if (this.atLeastOneMove())
300 // Just remove flips:
302 appear: [move.appear
[0]],
303 vanish: [move.vanish
[0]],
307 return super.getNotation(basicMove
);