Slight change in Ultima rules. TODO: merge with removed code 'isAttacked'
[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(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].c==color
116 && move.appear[i].x==lastRank)
117 {
118 move.appear[i].p = V.ROOK;
119 moves.push(move);
120 for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
121 {
122 let cmove = JSON.parse(JSON.stringify(move));
123 cmove.appear[i].p = piece;
124 moves.push(cmove);
125 }
126 // Swap appear[i] and appear[0] for moves presentation (TODO: this is awkward)
127 moves.forEach(m => {
128 let tmp = m.appear[0];
129 m.appear[0] = m.appear[i];
130 m.appear[i] = tmp;
131 });
132 break;
133 }
134 }
135 if (moves.length == 0) //no pawn on 8th rank
136 moves.push(move);
137 return moves;
138 }
139
140 atLeastOneMove()
141 {
142 if (this.kingPos[this.turn][0] < 0)
143 return false;
144 return true; //TODO: is it right?
145 }
146
147 underCheck(move)
148 {
149 return false; //there is no check
150 }
151
152 getCheckSquares(move)
153 {
154 const c = this.getOppCol(this.turn); //opponent
155 const saveKingPos = this.kingPos[c]; //king might be taken
156 this.play(move);
157 // The only way to be "under check" is to have lost the king (thus game over)
158 let res = this.kingPos[c][0] < 0
159 ? [ JSON.parse(JSON.stringify(saveKingPos)) ]
160 : [ ];
161 this.undo(move);
162 return res;
163 }
164
165 updateVariables(move)
166 {
167 super.updateVariables(move);
168 const c = this.getColor(move.start.x,move.start.y);
169 if (this.board[move.end.x][move.end.y] != VariantRules.EMPTY
170 && c != this.getColor(move.end.x,move.end.y)
171 && this.getPiece(move.end.x,move.end.y) == VariantRules.KING)
172 {
173 // We took opponent king !
174 const oppCol = this.getOppCol(c);
175 this.kingPos[oppCol] = [-1,-1];
176 this.castleFlags[oppCol] = [false,false];
177 }
178 // Did we magnetically move our (init) rooks or opponents' ones ?
179 const firstRank = (c == "w" ? 7 : 0);
180 const oppFirstRank = 7 - firstRank;
181 const oppCol = this.getOppCol(c);
182 move.vanish.forEach(psq => {
183 if (psq.x == firstRank && this.INIT_COL_ROOK[c].includes(psq.y))
184 this.castleFlags[c][psq.y==this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
185 else if (psq.x == oppFirstRank && this.INIT_COL_ROOK[oppCol].includes(psq.y))
186 this.castleFlags[oppCol][psq.y==this.INIT_COL_ROOK[oppCol][0] ? 0 : 1] = false;
187 });
188 }
189
190 unupdateVariables(move)
191 {
192 super.unupdateVariables(move);
193 const c = this.getColor(move.start.x,move.start.y);
194 const oppCol = this.getOppCol(c);
195 if (this.kingPos[oppCol][0] < 0)
196 {
197 // Last move took opponent's king
198 for (let psq of move.vanish)
199 {
200 if (psq.p == 'k')
201 {
202 this.kingPos[oppCol] = [psq.x, psq.y];
203 break;
204 }
205 }
206 }
207 }
208
209 checkGameEnd()
210 {
211 // No valid move: our king disappeared
212 return this.turn == "w" ? "0-1" : "1-0";
213 }
214
215 static get THRESHOLD_MATE() {
216 return 500; //checkmates evals may be slightly below 1000
217 }
218 }