- filterValid(moves) {
- // Disable check tests when subTurn == 2, because the move isn't finished
- if (this.subTurn == 2) return moves;
- const filteredMoves = super.filterValid(moves);
- // If at least one full move made, everything is allowed:
- if (this.movesCount >= 2) return filteredMoves;
- // Else, forbid check and captures:
- const oppCol = V.GetOppCol(this.turn);
- return filteredMoves.filter(m => {
- if (m.vanish.length == 2 && m.appear.length == 1) return false;
- this.play(m);
- const res = !this.underCheck(oppCol);
- this.undo(m);
- return res;
- });
+ // Helper to check sentries attacks:
+ selfAttack([x1, y1], [x2, y2]) {
+ const color = this.getColor(x1, y1);
+ const oppCol = V.GetOppCol(color);
+ const sliderAttack = (allowedSteps, lancer) => {
+ const deltaX = x2 - x1,
+ absDeltaX = Math.abs(deltaX);
+ const deltaY = y2 - y1,
+ absDeltaY = Math.abs(deltaY);
+ const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ];
+ if (
+ // Check that the step is a priori valid:
+ (absDeltaX != absDeltaY && deltaX != 0 && deltaY != 0) ||
+ allowedSteps.every(st => st[0] != step[0] || st[1] != step[1])
+ ) {
+ return false;
+ }
+ let sq = [ x1 + step[0], y1 + step[1] ];
+ while (sq[0] != x2 || sq[1] != y2) {
+ if (
+ // NOTE: no need to check OnBoard in this special case
+ (!lancer && this.board[sq[0]][sq[1]] != V.EMPTY) ||
+ (!!lancer && this.getColor(sq[0], sq[1]) == oppCol)
+ ) {
+ return false;
+ }
+ sq[0] += step[0];
+ sq[1] += step[1];
+ }
+ return true;
+ };
+ switch (this.getPiece(x1, y1)) {
+ case V.PAWN: {
+ // Pushed pawns move as enemy pawns
+ const shift = (color == 'w' ? 1 : -1);
+ return (x1 + shift == x2 && Math.abs(y1 - y2) == 1);
+ }
+ case V.KNIGHT: {
+ const deltaX = Math.abs(x1 - x2);
+ const deltaY = Math.abs(y1 - y2);
+ return (
+ deltaX + deltaY == 3 &&
+ [1, 2].includes(deltaX) &&
+ [1, 2].includes(deltaY)
+ );
+ }
+ case V.ROOK:
+ return sliderAttack(V.steps[V.ROOK]);
+ case V.BISHOP:
+ return sliderAttack(V.steps[V.BISHOP]);
+ case V.QUEEN:
+ return sliderAttack(V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
+ case V.LANCER: {
+ // Special case: as long as no enemy units stands in-between,
+ // it attacks (if it points toward the king).
+ const allowedStep = V.LANCER_DIRS[this.board[x1][y1].charAt(1)];
+ return sliderAttack([allowedStep], "lancer");
+ }
+ // No sentries or jailer tests: they cannot self-capture
+ }
+ return false;