4c286e1dea5b2fe1270600ebe607040bdf027ad9
[vchess.git] / public / javascripts / variants / Magnetic.js
1 class MagneticRules extends ChessRules
2 {
3 getEpSquare(move)
4 {
5 return undefined; //no en-passant
6 }
7
8 getPotentialMovesFrom([x,y])
9 {
10 let standardMoves = super.getPotentialMovesFrom([x,y]);
11 let moves = [];
12 standardMoves.forEach(m => {
13 let newMove_s = this.applyMagneticLaws(m);
14 if (newMove_s.length == 1)
15 moves.push(newMove_s[0]);
16 else //promotion
17 moves = moves.concat(moves, newMove_s);
18 });
19 return moves;
20 }
21
22 // Complete a move with magnetic actions
23 // TODO: job is done multiple times for (normal) promotions.
24 applyMagneticLaws(move)
25 {
26 const V = VariantRules;
27 if (move.appear[0].p == V.KING && move.appear.length==1)
28 return [move]; //kings are not charged
29 const aIdx = (move.appear[0].p != V.KING ? 0 : 1); //if castling, rook is charged
30 const [x,y] = [move.appear[aIdx].x, move.appear[aIdx].y];
31 const color = this.turn;
32 const lastRank = (color=="w" ? 0 : 7);
33 const standardMove = JSON.parse(JSON.stringify(move));
34 this.play(standardMove);
35 const [sizeX,sizeY] = V.size;
36 for (let step of [[-1,0],[1,0],[0,-1],[0,1]])
37 {
38 let [i,j] = [x+step[0],y+step[1]];
39 while (i>=0 && i<sizeX && j>=0 && j<sizeY)
40 {
41 if (this.board[i][j] != V.EMPTY)
42 {
43 // Found something. Same color or not?
44 if (this.getColor(i,j) != color)
45 {
46 // Attraction
47 if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) && this.getPiece(i,j) != V.KING)
48 {
49 move.vanish.push(
50 new PiPo({
51 p:this.getPiece(i,j),
52 c:this.getColor(i,j),
53 x:i,
54 y:j
55 })
56 );
57 move.appear.push(
58 new PiPo({
59 p:this.getPiece(i,j),
60 c:this.getColor(i,j),
61 x:x+step[0],
62 y:y+step[1]
63 })
64 );
65 }
66 }
67 else
68 {
69 // Repulsion
70 if (this.getPiece(i,j) != V.KING)
71 {
72 // Push it until we meet an obstacle or edge of the board
73 let [ii,jj] = [i+step[0],j+step[1]];
74 while (ii>=0 && ii<sizeX && jj>=0 && jj<sizeY)
75 {
76 if (this.board[ii][jj] != V.EMPTY)
77 break;
78 ii += step[0];
79 jj += step[1];
80 }
81 ii -= step[0];
82 jj -= step[1];
83 if (Math.abs(ii-i)>=1 || Math.abs(jj-j)>=1)
84 {
85 move.vanish.push(
86 new PiPo({
87 p:this.getPiece(i,j),
88 c:this.getColor(i,j),
89 x:i,
90 y:j
91 })
92 );
93 move.appear.push(
94 new PiPo({
95 p:this.getPiece(i,j),
96 c:this.getColor(i,j),
97 x:ii,
98 y:jj
99 })
100 );
101 }
102 }
103 }
104 break;
105 }
106 i += step[0];
107 j += step[1];
108 }
109 }
110 this.undo(standardMove);
111 let moves = [];
112 // Scan move for pawn (max 1) on 8th rank
113 for (let i=1; i<move.appear.length; i++)
114 {
115 if (move.appear[i].p==V.PAWN && move.appear[i].x==lastRank)
116 {
117 move.appear[i].p = V.ROOK;
118 moves.push(move);
119 for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
120 {
121 let cmove = JSON.parse(JSON.stringify(move));
122 cmove.appear[i].p = piece;
123 moves.push(cmove);
124 }
125 // Swap appear[i] and appear[0] for moves presentation (TODO: this is awkward)
126 moves.forEach(m => {
127 let tmp = m.appear[0];
128 m.appear[0] = m.appear[i];
129 m.appear[i] = tmp;
130 });
131 break;
132 }
133 }
134 if (moves.length == 0) //no pawn on 8th rank
135 moves.push(move);
136 return moves;
137 }
138
139 // TODO: verify this assertion
140 atLeastOneMove()
141 {
142 return true; //always at least one possible move
143 }
144
145 underCheck(move)
146 {
147 return false; //there is no check
148 }
149
150 getCheckSquares(move)
151 {
152 const c = this.getOppCol(this.turn); //opponent
153 const saveKingPos = this.kingPos[c]; //king might be taken
154 this.play(move);
155 // The only way to be "under check" is to have lost the king (thus game over)
156 let res = this.kingPos[c][0] < 0
157 ? [ JSON.parse(JSON.stringify(saveKingPos)) ]
158 : [ ];
159 this.undo(move);
160 return res;
161 }
162
163 updateVariables(move)
164 {
165 super.updateVariables(move);
166 const c = this.getColor(move.start.x,move.start.y);
167 if (c != this.getColor(move.end.x,move.end.y)
168 && this.board[move.end.x][move.end.y] != VariantRules.EMPTY
169 && this.getPiece(move.end.x,move.end.y) == VariantRules.KING)
170 {
171 // We took opponent king !
172 const oppCol = this.getOppCol(c);
173 this.kingPos[oppCol] = [-1,-1];
174 this.castleFlags[oppCol] = [false,false];
175 }
176 }
177
178 unupdateVariables(move)
179 {
180 super.unupdateVariables(move);
181 const c = this.getColor(move.start.x,move.start.y);
182 const oppCol = this.getOppCol(c);
183 if (this.kingPos[oppCol][0] < 0)
184 {
185 // Last move took opponent's king
186 for (let psq of move.vanish)
187 {
188 if (psq.p == 'k')
189 {
190 this.kingPos[oppCol] = [psq.x, psq.y];
191 break;
192 }
193 }
194 }
195 }
196
197 checkGameOver()
198 {
199 if (this.checkRepetition())
200 return "1/2";
201
202 const color = this.turn;
203 // TODO: do we need "atLeastOneMove()"?
204 if (this.atLeastOneMove() && this.kingPos[color][0] >= 0)
205 return "*";
206
207 return this.checkGameEnd();
208 }
209
210 checkGameEnd()
211 {
212 // No valid move: our king disappeared
213 return this.turn == "w" ? "0-1" : "1-0";
214 }
215 }