1 import { ChessRules
} from "@/base_rules";
3 export class Knightrelay1Rules
extends ChessRules
{
5 static get HasEnpassant() {
9 static IsGoodPosition(position
) {
10 if (!ChessRules
.IsGoodPosition(position
)) return false;
11 // Check that (at least) 2 knights per side are on the board
12 const rows
= position
.split("/");
13 let knights
= { 'N': 0, 'n': 0 };
14 for (let row
of rows
) {
15 for (let i
= 0; i
< row
.length
; i
++) {
16 if (['N','n'].includes(row
[i
])) knights
[row
[i
]]++;
19 if (Object
.values(knights
).some(v
=> v
< 2)) return false;
23 getPotentialMovesFrom([x
, y
]) {
24 let moves
= super.getPotentialMovesFrom([x
, y
]);
26 // Expand possible moves if guarded by a knight, and is not a king:
27 const piece
= this.getPiece(x
,y
);
28 if (![V
.KNIGHT
,V
.KING
].includes(piece
)) {
29 const color
= this.turn
;
30 let guardedByKnight
= false;
31 for (const step
of V
.steps
[V
.KNIGHT
]) {
33 V
.OnBoard(x
+step
[0],y
+step
[1]) &&
34 this.getPiece(x
+step
[0],y
+step
[1]) == V
.KNIGHT
&&
35 this.getColor(x
+step
[0],y
+step
[1]) == color
37 guardedByKnight
= true;
41 if (guardedByKnight
) {
42 const lastRank
= (color
== "w" ? 0 : V
.size
.x
- 1);
43 for (const step
of V
.steps
[V
.KNIGHT
]) {
45 V
.OnBoard(x
+step
[0],y
+step
[1]) &&
46 this.getColor(x
+step
[0],y
+step
[1]) != color
&&
47 // Pawns cannot promote by knight-relay
48 (piece
!= V
.PAWN
|| x
+step
[0] != lastRank
)
50 moves
.push(this.getBasicMove([x
,y
], [x
+step
[0],y
+step
[1]]));
56 // Forbid captures of knights (invincible in this variant)
57 return moves
.filter(m
=> {
59 m
.vanish
.length
== 1 ||
60 m
.appear
.length
== 2 ||
61 m
.vanish
[1].p
!= V
.KNIGHT
66 getPotentialKnightMoves(sq
) {
67 // Knights don't capture:
68 return super.getPotentialKnightMoves(sq
).filter(m
=> m
.vanish
.length
== 1);
71 isAttacked(sq
, color
) {
72 if (super.isAttacked(sq
, color
)) return true;
74 // Check if a (non-knight) piece at knight distance
75 // is guarded by a knight (and thus attacking)
76 // --> Except for kings, and pawns targetting last rank.
79 // Last rank for me, that is to say oppCol of color:
80 const lastRank
= (color
== 'w' ? V
.size
.x
- 1 : 0);
81 for (const step
of V
.steps
[V
.KNIGHT
]) {
83 V
.OnBoard(x
+step
[0],y
+step
[1]) &&
84 this.getColor(x
+step
[0],y
+step
[1]) == color
86 const piece
= this.getPiece(x
+step
[0],y
+step
[1]);
88 ![V
.KNIGHT
, V
.KING
].includes(piece
) &&
89 (piece
!= V
.PAWN
|| x
!= lastRank
)
91 for (const step2
of V
.steps
[V
.KNIGHT
]) {
92 const xx
= x
+step
[0]+step2
[0],
93 yy
= y
+step
[1]+step2
[1];
96 this.getColor(xx
,yy
) == color
&&
97 this.getPiece(xx
,yy
) == V
.KNIGHT
109 isAttackedByKnight(sq
, color
) {
110 // Knights don't attack
114 static get VALUES() {
118 n: 0, //the knight isn't captured - value doesn't matter
125 static get SEARCH_DEPTH() {
130 if (move.appear
.length
== 2 && move.appear
[0].p
== V
.KING
)
132 return move.end
.y
< move.start
.y
? "0-0-0" : "0-0";
134 // Translate final and initial square
135 const initSquare
= V
.CoordsToSquare(move.start
);
136 const finalSquare
= V
.CoordsToSquare(move.end
);
137 const piece
= this.getPiece(move.start
.x
, move.start
.y
);
139 // Since pieces and pawns could move like knight,
140 // indicate start and end squares
142 piece
.toUpperCase() +
144 (move.vanish
.length
> move.appear
.length
? "x" : "") +
149 move.appear
.length
> 0 &&
150 move.appear
[0].p
!= V
.PAWN
153 notation
+= "=" + move.appear
[0].p
.toUpperCase();