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