selfAttack([x1, y1], [x2, y2]) {
    const color = this.getColor(x1, y1);
    const oppCol = V.GetOppCol(color);
    const sliderAttack = (allowedSteps, lancer) => {
      const deltaX = x2 - x1,
            deltaY = y2 - y1;
      const absDeltaX = Math.abs(deltaX),
            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) {
        // NOTE: no need to check OnBoard in this special case
        if (this.board[sq[0]][sq[1]] != V.EMPTY) {
          const p = this.getPiece(sq[0], sq[1]);
          const pc = this.getColor(sq[0], sq[1]);
          if (
            // Enemy sentry on the way will be gone:
            (p != V.SENTRY || pc != oppCol) &&
            // Lancer temporarily "changed color":
            (!lancer || pc == color)
          ) {
            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;
  }
// Helper to check sentries attacks:
Helper to check sentries attacks.
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,
            deltaY = y2 - y1;
      const absDeltaX = Math.abs(deltaX),
            absDeltaY = Math.abs(deltaY);
      const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ];
      if (
        (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 (this.board[sq[0]][sq[1]] != V.EMPTY) {
          const p = this.getPiece(sq[0], sq[1]);
          const pc = this.getColor(sq[0], sq[1]);
          if (
            (p != V.SENTRY || pc != oppCol) &&
            (!lancer || pc == color)
          ) {
            return false;
          }
        }
        sq[0] += step[0];
        sq[1] += step[1];
      }
      return true;
    };
    switch (this.getPiece(x1, y1)) {
      case V.PAWN: {
        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: {
        const allowedStep = V.LANCER_DIRS[this.board[x1][y1].charAt(1)];
        return sliderAttack([allowedStep], "lancer");
      }
    }
    return false;
  }
selfAttack ( [ x1 , y1 ] , [ x2 , y2 ] ) { const color = this . getColor ( x1 , y1 ) ; const oppCol = V . GetOppCol ( color ) ; const sliderAttack = ( allowedSteps , lancer ) => { const deltaX = x2 - x1 , deltaY = y2 - y1 ; const absDeltaX = Math . abs ( deltaX ) , absDeltaY = Math . abs ( deltaY ) ; const step = [ deltaX / absDeltaX || 0 , deltaY / absDeltaY || 0 ] ; if ( ( 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 ( this . board [ sq [ 0 ] ] [ sq [ 1 ] ] != V . EMPTY ) { const p = this . getPiece ( sq [ 0 ] , sq [ 1 ] ) ; const pc = this . getColor ( sq [ 0 ] , sq [ 1 ] ) ; if ( ( p != V . SENTRY || pc != oppCol ) && ( ! lancer || pc == color ) ) { return false ; } } sq [ 0 ] += step [ 0 ] ; sq [ 1 ] += step [ 1 ] ; } return true ; } ; switch ( this . getPiece ( x1 , y1 ) ) { case V . PAWN : { 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 : { const allowedStep = V . LANCER_DIRS [ this . board [ x1 ] [ y1 ] . charAt ( 1 ) ] ; return sliderAttack ( [ allowedStep ] , "lancer" ) ; } } return false ; }
Helper to check sentries attacks:
Helper to check sentries attacks :
// Check that the step is a priori valid: // NOTE: no need to check OnBoard in this special case // Enemy sentry on the way will be gone: // Lancer temporarily "changed color": // Pushed pawns move as enemy pawns // Special case: as long as no enemy units stands in-between, // it attacks (if it points toward the king). // No sentries or jailer tests: they cannot self-capture