+ // 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;
+ }
+
+ isAttackedBySentry([x, y], color) {
+ // Attacked by sentry means it can self-take our king.
+ // Just check diagonals of enemy sentry(ies), and if it reaches
+ // one of our pieces: can I self-take?
+ const myColor = V.GetOppCol(color);
+ let candidates = [];
+ for (let i=0; i<V.size.x; i++) {
+ for (let j=0; j<V.size.y; j++) {
+ if (
+ this.getPiece(i,j) == V.SENTRY &&
+ this.getColor(i,j) == color &&
+ !this.isImmobilized([i, j])
+ ) {
+ for (let step of V.steps[V.BISHOP]) {
+ let sq = [ i + step[0], j + step[1] ];
+ while (
+ V.OnBoard(sq[0], sq[1]) &&
+ this.board[sq[0]][sq[1]] == V.EMPTY
+ ) {
+ sq[0] += step[0];
+ sq[1] += step[1];
+ }
+ if (
+ V.OnBoard(sq[0], sq[1]) &&
+ this.getColor(sq[0], sq[1]) == myColor
+ ) {
+ candidates.push([ sq[0], sq[1] ]);
+ }
+ }
+ }
+ }
+ }
+ for (let c of candidates)
+ if (this.selfAttack(c, [x, y])) return true;
+ return false;
+ }
+
+ // Jailer doesn't capture or give check
+