Avoid score redundancy in PGN
[vchess.git] / public / javascripts / variants / Magnetic.js
CommitLineData
7f0711a8 1class MagneticRules extends ChessRules
1af36beb
BA
2{
3 getEpSquare(move)
4 {
5 return undefined; //no en-passant
6 }
7
2526c041
BA
8 getPotentialMovesFrom([x,y])
9 {
aea1443e 10 let standardMoves = super.getPotentialMovesFrom([x,y]);
2526c041
BA
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
f3c10e18 17 moves = moves.concat(newMove_s);
2526c041 18 });
aea1443e 19 return moves;
2526c041
BA
20 }
21
1af36beb 22 // Complete a move with magnetic actions
5bfb0956 23 // TODO: job is done multiple times for (normal) promotions.
aea1443e 24 applyMagneticLaws(move)
1af36beb 25 {
aea1443e
BA
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));
7f0711a8 34 this.play(standardMove);
aea1443e 35 const [sizeX,sizeY] = V.size;
7f0711a8
BA
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 {
aea1443e 41 if (this.board[i][j] != V.EMPTY)
7f0711a8
BA
42 {
43 // Found something. Same color or not?
44 if (this.getColor(i,j) != color)
45 {
46 // Attraction
5bfb0956 47 if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) && this.getPiece(i,j) != V.KING)
7f0711a8
BA
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
aea1443e 70 if (this.getPiece(i,j) != V.KING)
7f0711a8
BA
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 {
aea1443e 76 if (this.board[ii][jj] != V.EMPTY)
7f0711a8
BA
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);
1af36beb 111 let moves = [];
aea1443e
BA
112 // Scan move for pawn (max 1) on 8th rank
113 for (let i=1; i<move.appear.length; i++)
1af36beb 114 {
1221ac47
BA
115 if (move.appear[i].p==V.PAWN && move.appear[i].c==color
116 && move.appear[i].x==lastRank)
aea1443e
BA
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 }
1af36beb 134 }
aea1443e 135 if (moves.length == 0) //no pawn on 8th rank
2526c041 136 moves.push(move);
1af36beb
BA
137 return moves;
138 }
139
140 // TODO: verify this assertion
2526c041
BA
141 atLeastOneMove()
142 {
143 return true; //always at least one possible move
144 }
1af36beb
BA
145
146 underCheck(move)
147 {
148 return false; //there is no check
149 }
150
151 getCheckSquares(move)
152 {
153 const c = this.getOppCol(this.turn); //opponent
154 const saveKingPos = this.kingPos[c]; //king might be taken
155 this.play(move);
156 // The only way to be "under check" is to have lost the king (thus game over)
7f0711a8 157 let res = this.kingPos[c][0] < 0
1af36beb
BA
158 ? [ JSON.parse(JSON.stringify(saveKingPos)) ]
159 : [ ];
160 this.undo(move);
161 return res;
162 }
163
164 updateVariables(move)
165 {
166 super.updateVariables(move);
167 const c = this.getColor(move.start.x,move.start.y);
168 if (c != this.getColor(move.end.x,move.end.y)
169 && this.board[move.end.x][move.end.y] != VariantRules.EMPTY
170 && this.getPiece(move.end.x,move.end.y) == VariantRules.KING)
171 {
172 // We took opponent king !
173 const oppCol = this.getOppCol(c);
174 this.kingPos[oppCol] = [-1,-1];
2526c041 175 this.castleFlags[oppCol] = [false,false];
1af36beb
BA
176 }
177 }
178
179 unupdateVariables(move)
180 {
181 super.unupdateVariables(move);
182 const c = this.getColor(move.start.x,move.start.y);
183 const oppCol = this.getOppCol(c);
184 if (this.kingPos[oppCol][0] < 0)
185 {
186 // Last move took opponent's king
187 for (let psq of move.vanish)
188 {
189 if (psq.p == 'k')
190 {
191 this.kingPos[oppCol] = [psq.x, psq.y];
192 break;
193 }
194 }
195 }
196 }
197
198 checkGameOver()
199 {
200 if (this.checkRepetition())
201 return "1/2";
202
203 const color = this.turn;
204 // TODO: do we need "atLeastOneMove()"?
205 if (this.atLeastOneMove() && this.kingPos[color][0] >= 0)
206 return "*";
207
208 return this.checkGameEnd();
209 }
210
211 checkGameEnd()
212 {
213 // No valid move: our king disappeared
214 return this.turn == "w" ? "0-1" : "1-0";
215 }
9e42b4dd
BA
216
217 static get THRESHOLD_MATE() {
218 return 500; //checkmates evals may be slightly below 1000
219 }
1af36beb 220}