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