'update'
[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 kings, and 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 (
88 ![V.KNIGHT, V.KING].includes(piece) &&
89 (piece != V.PAWN || x != lastRank)
90 ) {
91 for (const step2 of V.steps[V.KNIGHT]) {
92 const xx = x+step[0]+step2[0],
93 yy = y+step[1]+step2[1];
94 if (
95 V.OnBoard(xx,yy) &&
96 this.getColor(xx,yy) == color &&
97 this.getPiece(xx,yy) == V.KNIGHT
98 ) {
99 return true;
100 }
101 }
102 }
103 }
104 }
105
106 return false;
107 }
108
109 isAttackedByKnight(sq, color) {
110 // Knights don't attack
111 return false;
112 }
113
114 static get VALUES() {
115 return {
116 p: 1,
117 r: 5,
118 n: 0, //the knight isn't captured - value doesn't matter
119 b: 3,
120 q: 9,
121 k: 1000
122 };
123 }
124
125 static get SEARCH_DEPTH() {
126 return 2;
127 }
128
129 getNotation(move) {
130 if (move.appear.length == 2 && move.appear[0].p == V.KING)
131 // Castle
132 return move.end.y < move.start.y ? "0-0-0" : "0-0";
133
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);
138
139 // Since pieces and pawns could move like knight,
140 // indicate start and end squares
141 let notation =
142 piece.toUpperCase() +
143 initSquare +
144 (move.vanish.length > move.appear.length ? "x" : "") +
145 finalSquare
146
147 if (
148 piece == V.PAWN &&
149 move.appear.length > 0 &&
150 move.appear[0].p != V.PAWN
151 ) {
152 // Promotion
153 notation += "=" + move.appear[0].p.toUpperCase();
154 }
155
156 return notation;
157 }
158
159 };