Started code review + some fixes (unfinished)
[vchess.git] / client / src / variants / Magnetic.js
1 import { ChessRules, PiPo } from "@/base_rules";
2
3 export const VariantRules = class MagneticRules extends ChessRules {
4 static get HasEnpassant() {
5 return false;
6 }
7
8 getPotentialMovesFrom([x, y]) {
9 let standardMoves = super.getPotentialMovesFrom([x, y]);
10 let moves = [];
11 standardMoves.forEach(m => {
12 let newMove_s = this.applyMagneticLaws(m);
13 if (newMove_s.length == 1) moves.push(newMove_s[0]);
14 //promotion
15 else moves = moves.concat(newMove_s);
16 });
17 return moves;
18 }
19
20 // Complete a move with magnetic actions
21 // TODO: job is done multiple times for (normal) promotions.
22 applyMagneticLaws(move) {
23 if (move.appear[0].p == V.KING && move.appear.length == 1) return [move]; //kings are not charged
24 const aIdx = move.appear[0].p != V.KING ? 0 : 1; //if castling, rook is charged
25 const [x, y] = [move.appear[aIdx].x, move.appear[aIdx].y];
26 const color = this.turn;
27 const lastRank = color == "w" ? 0 : 7;
28 const standardMove = JSON.parse(JSON.stringify(move));
29 this.play(standardMove);
30 for (let step of [
31 [-1, 0],
32 [1, 0],
33 [0, -1],
34 [0, 1]
35 ]) {
36 let [i, j] = [x + step[0], y + step[1]];
37 while (V.OnBoard(i, j)) {
38 if (this.board[i][j] != V.EMPTY) {
39 // Found something. Same color or not?
40 if (this.getColor(i, j) != color) {
41 // Attraction
42 if (
43 (Math.abs(i - x) >= 2 || Math.abs(j - y) >= 2) &&
44 this.getPiece(i, j) != V.KING
45 ) {
46 move.vanish.push(
47 new PiPo({
48 p: this.getPiece(i, j),
49 c: this.getColor(i, j),
50 x: i,
51 y: j
52 })
53 );
54 move.appear.push(
55 new PiPo({
56 p: this.getPiece(i, j),
57 c: this.getColor(i, j),
58 x: x + step[0],
59 y: y + step[1]
60 })
61 );
62 }
63 } else {
64 // Repulsion
65 if (this.getPiece(i, j) != V.KING) {
66 // Push it until we meet an obstacle or edge of the board
67 let [ii, jj] = [i + step[0], j + step[1]];
68 while (V.OnBoard(ii, jj)) {
69 if (this.board[ii][jj] != V.EMPTY) break;
70 ii += step[0];
71 jj += step[1];
72 }
73 ii -= step[0];
74 jj -= step[1];
75 if (Math.abs(ii - i) >= 1 || Math.abs(jj - j) >= 1) {
76 move.vanish.push(
77 new PiPo({
78 p: this.getPiece(i, j),
79 c: this.getColor(i, j),
80 x: i,
81 y: j
82 })
83 );
84 move.appear.push(
85 new PiPo({
86 p: this.getPiece(i, j),
87 c: this.getColor(i, j),
88 x: ii,
89 y: jj
90 })
91 );
92 }
93 }
94 }
95 break;
96 }
97 i += step[0];
98 j += step[1];
99 }
100 }
101 this.undo(standardMove);
102 let moves = [];
103 // Scan move for pawn (max 1) on 8th rank
104 for (let i = 1; i < move.appear.length; i++) {
105 if (
106 move.appear[i].p == V.PAWN &&
107 move.appear[i].c == color &&
108 move.appear[i].x == lastRank
109 ) {
110 move.appear[i].p = V.ROOK;
111 moves.push(move);
112 for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN]) {
113 let cmove = JSON.parse(JSON.stringify(move));
114 cmove.appear[i].p = piece;
115 moves.push(cmove);
116 }
117 // Swap appear[i] and appear[0] for moves presentation (TODO: this is awkward)
118 moves.forEach(m => {
119 let tmp = m.appear[0];
120 m.appear[0] = m.appear[i];
121 m.appear[i] = tmp;
122 });
123 break;
124 }
125 }
126 if (moves.length == 0)
127 //no pawn on 8th rank
128 moves.push(move);
129 return moves;
130 }
131
132 atLeastOneMove() {
133 if (this.kingPos[this.turn][0] < 0) return false;
134 return true; //TODO: is it right?
135 }
136
137 underCheck() {
138 return false; //there is no check
139 }
140
141 getCheckSquares() {
142 return [];
143 }
144
145 updateVariables(move) {
146 super.updateVariables(move);
147 const c = move.vanish[0].c;
148 if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) {
149 // We took opponent king !
150 const oppCol = V.GetOppCol(c);
151 this.kingPos[oppCol] = [-1, -1];
152 this.castleFlags[oppCol] = [false, false];
153 }
154 // Did we magnetically move our (init) rooks or opponents' ones ?
155 const firstRank = c == "w" ? 7 : 0;
156 const oppFirstRank = 7 - firstRank;
157 const oppCol = V.GetOppCol(c);
158 move.vanish.forEach(psq => {
159 if (psq.x == firstRank && this.INIT_COL_ROOK[c].includes(psq.y))
160 this.castleFlags[c][psq.y == this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
161 else if (
162 psq.x == oppFirstRank &&
163 this.INIT_COL_ROOK[oppCol].includes(psq.y)
164 )
165 this.castleFlags[oppCol][
166 psq.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1
167 ] = false;
168 });
169 }
170
171 unupdateVariables(move) {
172 super.unupdateVariables(move);
173 const c = move.vanish[0].c;
174 const oppCol = V.GetOppCol(c);
175 if (this.kingPos[oppCol][0] < 0) {
176 // Last move took opponent's king
177 for (let psq of move.vanish) {
178 if (psq.p == "k") {
179 this.kingPos[oppCol] = [psq.x, psq.y];
180 break;
181 }
182 }
183 }
184 }
185
186 getCurrentScore() {
187 const color = this.turn;
188 const kp = this.kingPos[color];
189 if (kp[0] < 0)
190 //king disappeared
191 return color == "w" ? "0-1" : "1-0";
192 if (this.atLeastOneMove())
193 // game not over
194 return "*";
195 return "1/2"; //no moves but kings still there
196 }
197
198 static get THRESHOLD_MATE() {
199 return 500; //checkmates evals may be slightly below 1000
200 }
201 };