First draft of Magnetic chess
[vchess.git] / public / javascripts / variants / Magnetic.js
1 class MagneticRules
2 {
3 getEpSquare(move)
4 {
5 return undefined; //no en-passant
6 }
7
8 // Complete a move with magnetic actions
9 applyMagneticLaws([x,y], move)
10 {
11 // TODO
12 }
13
14 getBasicMove([sx,sy], [ex,ey], tr)
15 {
16 var mv = new Move({
17 appear: [
18 new PiPo({
19 x: ex,
20 y: ey,
21 c: !!tr ? tr.c : this.getColor(sx,sy),
22 p: !!tr ? tr.p : this.getPiece(sx,sy)
23 })
24 ],
25 vanish: [
26 new PiPo({
27 x: sx,
28 y: sy,
29 c: this.getColor(sx,sy),
30 p: this.getPiece(sx,sy)
31 })
32 ]
33 });
34
35 if (this.board[ex][ey] != VariantRules.EMPTY)
36 {
37 mv.vanish.push(
38 new PiPo({
39 x: ex,
40 y: ey,
41 c: this.getColor(ex,ey),
42 p: this.getPiece(ex,ey)
43 })
44 );
45 }
46 this.applyMagneticLaws([ex,ey], mv);
47 return mv;
48 }
49
50 getCastleMoves([x,y])
51 {
52 const c = this.getColor(x,y);
53 if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c])
54 return []; //x isn't first rank, or king has moved (shortcut)
55
56 const V = VariantRules;
57
58 // Castling ?
59 const oppCol = this.getOppCol(c);
60 let moves = [];
61 let i = 0;
62 const finalSquares = [ [2,3], [6,5] ]; //king, then rook
63 castlingCheck:
64 for (let castleSide=0; castleSide < 2; castleSide++) //large, then small
65 {
66 if (!this.flags[c][castleSide])
67 continue;
68 // If this code is reached, rooks and king are on initial position
69
70 // Nothing on the path of the king (and no checks; OK also if y==finalSquare)?
71 let step = finalSquares[castleSide][0] < y ? -1 : 1;
72 for (i=y; i!=finalSquares[castleSide][0]; i+=step)
73 {
74 if (this.isAttacked([x,i], oppCol) || (this.board[x][i] != V.EMPTY &&
75 // NOTE: next check is enough, because of chessboard constraints
76 (this.getColor(x,i) != c || ![V.KING,V.ROOK].includes(this.getPiece(x,i)))))
77 {
78 continue castlingCheck;
79 }
80 }
81
82 // Nothing on the path to the rook?
83 step = castleSide == 0 ? -1 : 1;
84 for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step)
85 {
86 if (this.board[x][i] != V.EMPTY)
87 continue castlingCheck;
88 }
89 const rookPos = this.INIT_COL_ROOK[c][castleSide];
90
91 // Nothing on final squares, except maybe king and castling rook?
92 for (i=0; i<2; i++)
93 {
94 if (this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
95 this.getPiece(x,finalSquares[castleSide][i]) != V.KING &&
96 finalSquares[castleSide][i] != rookPos)
97 {
98 continue castlingCheck;
99 }
100 }
101
102 // If this code is reached, castle is valid
103 let cmove = new Move({
104 appear: [
105 new PiPo({x:x,y:finalSquares[castleSide][0],p:V.KING,c:c}),
106 new PiPo({x:x,y:finalSquares[castleSide][1],p:V.ROOK,c:c})],
107 vanish: [
108 new PiPo({x:x,y:y,p:V.KING,c:c}),
109 new PiPo({x:x,y:rookPos,p:V.ROOK,c:c})],
110 end: Math.abs(y - rookPos) <= 2
111 ? {x:x, y:rookPos}
112 : {x:x, y:y + 2 * (castleSide==0 ? -1 : 1)}
113 });
114 this.applyMagneticLaws([x,finalSquares[castleSide][1]], cmove);
115 moves.push(cmove);
116 }
117
118 return moves;
119 }
120
121 // TODO: verify this assertion
122 // atLeastOneMove()
123 // {
124 // return true; //always at least one possible move
125 // }
126
127 underCheck(move)
128 {
129 return false; //there is no check
130 }
131
132 getCheckSquares(move)
133 {
134 const c = this.getOppCol(this.turn); //opponent
135 const saveKingPos = this.kingPos[c]; //king might be taken
136 this.play(move);
137 // The only way to be "under check" is to have lost the king (thus game over)
138 let res = this.kingPos[c][0] < 0;
139 ? [ JSON.parse(JSON.stringify(saveKingPos)) ]
140 : [ ];
141 this.undo(move);
142 return res;
143 }
144
145 updateVariables(move)
146 {
147 super.updateVariables(move);
148 const c = this.getColor(move.start.x,move.start.y);
149 if (c != this.getColor(move.end.x,move.end.y)
150 && this.board[move.end.x][move.end.y] != VariantRules.EMPTY
151 && this.getPiece(move.end.x,move.end.y) == VariantRules.KING)
152 {
153 // We took opponent king !
154 const oppCol = this.getOppCol(c);
155 this.kingPos[oppCol] = [-1,-1];
156 this.flags[oppCol] = [false,false];
157 }
158 }
159
160 unupdateVariables(move)
161 {
162 super.unupdateVariables(move);
163 const c = this.getColor(move.start.x,move.start.y);
164 const oppCol = this.getOppCol(c);
165 if (this.kingPos[oppCol][0] < 0)
166 {
167 // Last move took opponent's king
168 for (let psq of move.vanish)
169 {
170 if (psq.p == 'k')
171 {
172 this.kingPos[oppCol] = [psq.x, psq.y];
173 break;
174 }
175 }
176 }
177 }
178
179 checkGameOver()
180 {
181 if (this.checkRepetition())
182 return "1/2";
183
184 const color = this.turn;
185 // TODO: do we need "atLeastOneMove()"?
186 if (this.atLeastOneMove() && this.kingPos[color][0] >= 0)
187 return "*";
188
189 return this.checkGameEnd();
190 }
191
192 checkGameEnd()
193 {
194 // No valid move: our king disappeared
195 return this.turn == "w" ? "0-1" : "1-0";
196 }
197 }