Add Knightrelay1. Some fixes. Move odd 'isAttackedBy_multiple_colors' to Checkered...
[vchess.git] / client / src / variants / Knightrelay1.js
1 import { ChessRules } from "@/base_rules";
2
3 export const VariantRules = class Knightrelay1Rules extends ChessRules {
4 static get HasEnpassant() {
5 return false;
6 }
7
8 getPotentialMovesFrom([x, y]) {
9 let moves = super.getPotentialMovesFrom([x, y]);
10
11 // Expand possible moves if guarded by a knight, and is not a king:
12 const piece = this.getPiece(x,y);
13 if (![V.KNIGHT,V.KING].includes(piece)) {
14 const color = this.turn;
15 let guardedByKnight = false;
16 for (const step of V.steps[V.KNIGHT]) {
17 if (
18 V.OnBoard(x+step[0],y+step[1]) &&
19 this.getPiece(x+step[0],y+step[1]) == V.KNIGHT &&
20 this.getColor(x+step[0],y+step[1]) == color
21 ) {
22 guardedByKnight = true;
23 break;
24 }
25 }
26 if (guardedByKnight) {
27 const lastRank = (color == "w" ? 0 : V.size.x - 1);
28 for (const step of V.steps[V.KNIGHT]) {
29 if (
30 V.OnBoard(x+step[0],y+step[1]) &&
31 this.getColor(x+step[0],y+step[1]) != color &&
32 // Pawns cannot promote by knight-relay
33 (piece != V.PAWN || x+step[0] != lastRank)
34 ) {
35 moves.push(this.getBasicMove([x,y], [x+step[0],y+step[1]]));
36 }
37 }
38 }
39 }
40
41 // Forbid captures of knights (invincible in this variant)
42 return moves.filter(m => {
43 return (
44 m.vanish.length == 1 ||
45 m.appear.length == 2 ||
46 m.vanish[1].p != V.KNIGHT
47 );
48 });
49 }
50
51 getPotentialKnightMoves(sq) {
52 // Knights don't capture:
53 return super.getPotentialKnightMoves(sq).filter(m => m.vanish.length == 1);
54 }
55
56 isAttacked(sq, color) {
57 if (super.isAttacked(sq, color)) return true;
58
59 // Check if a (non-knight) piece at knight distance
60 // is guarded by a knight (and thus attacking)
61 // --> Except for pawns targetting last rank.
62 const x = sq[0],
63 y = sq[1];
64 // Last rank for me, that is to say oppCol of color:
65 const lastRank = (color == 'w' ? V.size.x - 1 : 0);
66 for (const step of V.steps[V.KNIGHT]) {
67 if (
68 V.OnBoard(x+step[0],y+step[1]) &&
69 this.getColor(x+step[0],y+step[1]) == color
70 ) {
71 const piece = this.getPiece(x+step[0],y+step[1]);
72 if (piece != V.KNIGHT && (piece != V.PAWN || x != lastRank)) {
73 for (const step2 of V.steps[V.KNIGHT]) {
74 const xx = x+step[0]+step2[0],
75 yy = y+step[1]+step2[1];
76 if (
77 V.OnBoard(xx,yy) &&
78 this.getColor(xx,yy) == color &&
79 this.getPiece(xx,yy) == V.KNIGHT
80 ) {
81 return true;
82 }
83 }
84 }
85 }
86 }
87
88 return false;
89 }
90
91 isAttackedByKnight(sq, color) {
92 // Knights don't attack
93 return false;
94 }
95
96 static get VALUES() {
97 return {
98 p: 1,
99 r: 5,
100 n: 7, //the knight is valuable
101 b: 3,
102 q: 9,
103 k: 1000
104 };
105 }
106
107 static get SEARCH_DEPTH() {
108 return 2;
109 }
110
111 getNotation(move) {
112 if (move.appear.length == 2 && move.appear[0].p == V.KING)
113 // Castle
114 return move.end.y < move.start.y ? "0-0-0" : "0-0";
115
116 // Translate final and initial square
117 const initSquare = V.CoordsToSquare(move.start);
118 const finalSquare = V.CoordsToSquare(move.end);
119 const piece = this.getPiece(move.start.x, move.start.y);
120
121 // Since pieces and pawns could move like knight, indicate start and end squares
122 let notation =
123 piece.toUpperCase() +
124 initSquare +
125 (move.vanish.length > move.appear.length ? "x" : "") +
126 finalSquare
127
128 if (
129 piece == V.PAWN &&
130 move.appear.length > 0 &&
131 move.appear[0].p != V.PAWN
132 ) {
133 // Promotion
134 notation += "=" + move.appear[0].p.toUpperCase();
135 }
136
137 return notation;
138 }
139 };