aba952905069a08367310accc078c9a4da5139f9
[vchess.git] / client / src / variants / Knightrelay1.js
1 import { ChessRules } from "@/base_rules";
2
3 export class Knightrelay1Rules extends ChessRules {
4
5 static get HasEnpassant() {
6 return false;
7 }
8
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]]++;
17 }
18 }
19 if (Object.values(knights).some(v => v < 2)) return false;
20 return true;
21 }
22
23 getPotentialMovesFrom([x, y]) {
24 let moves = super.getPotentialMovesFrom([x, y]);
25
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]) {
32 if (
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
36 ) {
37 guardedByKnight = true;
38 break;
39 }
40 }
41 if (guardedByKnight) {
42 const lastRank = (color == "w" ? 0 : V.size.x - 1);
43 for (const step of V.steps[V.KNIGHT]) {
44 if (
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)
49 ) {
50 moves.push(this.getBasicMove([x,y], [x+step[0],y+step[1]]));
51 }
52 }
53 }
54 }
55
56 // Forbid captures of knights (invincible in this variant)
57 return moves.filter(m => {
58 return (
59 m.vanish.length == 1 ||
60 m.appear.length == 2 ||
61 m.vanish[1].p != V.KNIGHT
62 );
63 });
64 }
65
66 getPotentialKnightMoves(sq) {
67 // Knights don't capture:
68 return super.getPotentialKnightMoves(sq).filter(m => m.vanish.length == 1);
69 }
70
71 isAttacked(sq, color) {
72 if (super.isAttacked(sq, color)) return true;
73
74 // Check if a (non-knight) piece at knight distance
75 // is guarded by a knight (and thus attacking)
76 // --> Except for pawns targetting last rank.
77 const x = sq[0],
78 y = sq[1];
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]) {
82 if (
83 V.OnBoard(x+step[0],y+step[1]) &&
84 this.getColor(x+step[0],y+step[1]) == color
85 ) {
86 const piece = this.getPiece(x+step[0],y+step[1]);
87 if (piece != V.KNIGHT && (piece != V.PAWN || x != lastRank)) {
88 for (const step2 of V.steps[V.KNIGHT]) {
89 const xx = x+step[0]+step2[0],
90 yy = y+step[1]+step2[1];
91 if (
92 V.OnBoard(xx,yy) &&
93 this.getColor(xx,yy) == color &&
94 this.getPiece(xx,yy) == V.KNIGHT
95 ) {
96 return true;
97 }
98 }
99 }
100 }
101 }
102
103 return false;
104 }
105
106 isAttackedByKnight(sq, color) {
107 // Knights don't attack
108 return false;
109 }
110
111 static get VALUES() {
112 return {
113 p: 1,
114 r: 5,
115 n: 0, //the knight isn't captured - value doesn't matter
116 b: 3,
117 q: 9,
118 k: 1000
119 };
120 }
121
122 static get SEARCH_DEPTH() {
123 return 2;
124 }
125
126 getNotation(move) {
127 if (move.appear.length == 2 && move.appear[0].p == V.KING)
128 // Castle
129 return move.end.y < move.start.y ? "0-0-0" : "0-0";
130
131 // Translate final and initial square
132 const initSquare = V.CoordsToSquare(move.start);
133 const finalSquare = V.CoordsToSquare(move.end);
134 const piece = this.getPiece(move.start.x, move.start.y);
135
136 // Since pieces and pawns could move like knight,
137 // indicate start and end squares
138 let notation =
139 piece.toUpperCase() +
140 initSquare +
141 (move.vanish.length > move.appear.length ? "x" : "") +
142 finalSquare
143
144 if (
145 piece == V.PAWN &&
146 move.appear.length > 0 &&
147 move.appear[0].p != V.PAWN
148 ) {
149 // Promotion
150 notation += "=" + move.appear[0].p.toUpperCase();
151 }
152
153 return notation;
154 }
155
156 };