1 // NOTE: alternative implementation, probably cleaner = use only 1 board
2 class AliceRules
extends ChessRules
4 static get ALICE_PIECES()
15 static get ALICE_CODES()
29 return (Object
.keys(this.ALICE_PIECES
).includes(b
[1]) ? "Alice/" : "") + b
;
34 super.initVariables(fen
);
35 const fenParts
= fen
.split(" ");
36 const position
= fenParts
[0].split("/");
37 if (this.kingPos
["w"][0] < 0 || this.kingPos
["b"][0] < 0)
39 // INIT_COL_XXX won't be used, so no need to set them for Alice kings
40 for (let i
=0; i
<position
.length
; i
++)
42 let k
= 0; //column index on board
43 for (let j
=0; j
<position
[i
].length
; j
++)
45 switch (position
[i
].charAt(j
))
48 this.kingPos
['b'] = [i
,k
];
51 this.kingPos
['w'] = [i
,k
];
54 let num
= parseInt(position
[i
].charAt(j
));
64 // Return the (standard) color+piece notation at a square for a board
65 getSquareOccupation(i
, j
, mirrorSide
)
67 const piece
= this.getPiece(i
,j
);
68 const V
= VariantRules
;
69 if (mirrorSide
==1 && Object
.keys(V
.ALICE_CODES
).includes(piece
))
70 return this.board
[i
][j
];
71 else if (mirrorSide
==2 && Object
.keys(V
.ALICE_PIECES
).includes(piece
))
72 return this.getColor(i
,j
) + V
.ALICE_PIECES
[piece
];
76 // Build board of the given (mirror)side
77 getSideBoard(mirrorSide
)
79 // Build corresponding board from complete board
80 const [sizeX
,sizeY
] = VariantRules
.size
;
81 let sideBoard
= doubleArray(sizeX
, sizeY
, "");
82 for (let i
=0; i
<sizeX
; i
++)
84 for (let j
=0; j
<sizeY
; j
++)
85 sideBoard
[i
][j
] = this.getSquareOccupation(i
, j
, mirrorSide
);
90 // NOTE: castle & enPassant https://www.chessvariants.com/other.dir/alice.html
91 getPotentialMovesFrom([x
,y
], sideBoard
)
93 const pieces
= Object
.keys(VariantRules
.ALICE_CODES
);
94 const codes
= Object
.keys(VariantRules
.ALICE_PIECES
);
95 const mirrorSide
= (pieces
.includes(this.getPiece(x
,y
)) ? 1 : 2);
97 // Search valid moves on sideBoard
98 let saveBoard
= this.board
;
99 this.board
= sideBoard
|| this.getSideBoard(mirrorSide
);
100 let moves
= super.getPotentialMovesFrom([x
,y
]);
101 this.board
= saveBoard
;
103 // Finally filter impossible moves
104 let res
= moves
.filter(m
=> {
105 if (m
.appear
.length
== 2) //castle
107 // appear[i] must be an empty square on the other board
108 for (let psq
of m
.appear
)
110 if (this.getSquareOccupation(psq
.x
,psq
.y
,3-mirrorSide
) != VariantRules
.EMPTY
)
114 else if (this.board
[m
.end
.x
][m
.end
.y
] != VariantRules
.EMPTY
)
116 // Attempt to capture
117 const piece
= this.getPiece(m
.end
.x
,m
.end
.y
);
118 if ((mirrorSide
==1 && codes
.includes(piece
))
119 || (mirrorSide
==2 && pieces
.includes(piece
)))
124 // If the move is computed on board1, m.appear change for Alice pieces.
127 m
.appear
.forEach(psq
=> { //forEach: castling taken into account
128 psq
.p
= VariantRules
.ALICE_CODES
[psq
.p
]; //goto board2
131 else //move on board2: mark vanishing pieces as Alice
133 m
.vanish
.forEach(psq
=> {
134 psq
.p
= VariantRules
.ALICE_CODES
[psq
.p
];
137 // Fix en-passant captures
138 if (m
.vanish
[0].p
== VariantRules
.PAWN
139 && m
.vanish
.length
== 2 && this.board
[m
.end
.x
][m
.end
.y
] == VariantRules
.EMPTY
)
141 m
.vanish
[1].c
= this.getOppCol(this.getColor(x
,y
));
142 // In the special case of en-passant, if
143 // - board1 takes board2 : vanish[1] --> Alice
144 // - board2 takes board1 : vanish[1] --> normal
145 let van
= m
.vanish
[1];
146 if (mirrorSide
==1 && codes
.includes(this.getPiece(van
.x
,van
.y
)))
147 van
.p
= VariantRules
.ALICE_CODES
[van
.p
];
148 else if (mirrorSide
==2 && pieces
.includes(this.getPiece(van
.x
,van
.y
)))
149 van
.p
= VariantRules
.ALICE_PIECES
[van
.p
];
158 if (moves
.length
== 0)
160 let sideBoard
= [this.getSideBoard(1), this.getSideBoard(2)];
161 return moves
.filter(m
=> { return !this.underCheck(m
, sideBoard
); });
166 const color
= this.turn
;
167 const oppCol
= this.getOppCol(color
);
168 var potentialMoves
= [];
169 let [sizeX
,sizeY
] = VariantRules
.size
;
170 let sideBoard
= [this.getSideBoard(1), this.getSideBoard(2)];
171 for (var i
=0; i
<sizeX
; i
++)
173 for (var j
=0; j
<sizeY
; j
++)
175 if (this.board
[i
][j
] != VariantRules
.EMPTY
&& this.getColor(i
,j
) == color
)
178 Object
.keys(VariantRules
.ALICE_CODES
).includes(this.getPiece(i
,j
))
181 Array
.prototype.push
.apply(potentialMoves
,
182 this.getPotentialMovesFrom([i
,j
], sideBoard
[mirrorSide
-1]));
186 return this.filterValid(potentialMoves
, sideBoard
);
189 // Play on sideboards [TODO: only one sideBoard required]
190 playSide(move, sideBoard
)
192 const pieces
= Object
.keys(VariantRules
.ALICE_CODES
);
193 move.vanish
.forEach(psq
=> {
194 const mirrorSide
= (pieces
.includes(psq
.p
) ? 1 : 2);
195 sideBoard
[mirrorSide
-1][psq
.x
][psq
.y
] = VariantRules
.EMPTY
;
197 move.appear
.forEach(psq
=> {
198 const mirrorSide
= (pieces
.includes(psq
.p
) ? 1 : 2);
199 const piece
= (mirrorSide
== 1 ? psq
.p : VariantRules
.ALICE_PIECES
[psq
.p
]);
200 sideBoard
[mirrorSide
-1][psq
.x
][psq
.y
] = psq
.c
+ piece
;
201 if (piece
== VariantRules
.KING
)
202 this.kingPos
[psq
.c
] = [psq
.x
,psq
.y
];
206 // Undo on sideboards
207 undoSide(move, sideBoard
)
209 const pieces
= Object
.keys(VariantRules
.ALICE_CODES
);
210 move.appear
.forEach(psq
=> {
211 const mirrorSide
= (pieces
.includes(psq
.p
) ? 1 : 2);
212 sideBoard
[mirrorSide
-1][psq
.x
][psq
.y
] = VariantRules
.EMPTY
;
214 move.vanish
.forEach(psq
=> {
215 const mirrorSide
= (pieces
.includes(psq
.p
) ? 1 : 2);
216 const piece
= (mirrorSide
== 1 ? psq
.p : VariantRules
.ALICE_PIECES
[psq
.p
]);
217 sideBoard
[mirrorSide
-1][psq
.x
][psq
.y
] = psq
.c
+ piece
;
218 if (piece
== VariantRules
.KING
)
219 this.kingPos
[psq
.c
] = [psq
.x
,psq
.y
];
223 underCheck(move, sideBoard
) //sideBoard arg always provided
225 const color
= this.turn
;
226 this.playSide(move, sideBoard
); //no need to track flags
227 const kp
= this.kingPos
[color
];
228 const mirrorSide
= sideBoard
[0][kp
[0]][kp
[1]] != VariantRules
.EMPTY
? 1 : 2;
229 let saveBoard
= this.board
;
230 this.board
= sideBoard
[mirrorSide
-1];
231 let res
= this.isAttacked(kp
, [this.getOppCol(color
)]);
232 this.board
= saveBoard
;
233 this.undoSide(move, sideBoard
);
237 getCheckSquares(move)
240 const color
= this.turn
; //opponent
241 const pieces
= Object
.keys(VariantRules
.ALICE_CODES
);
242 const kp
= this.kingPos
[color
];
243 const mirrorSide
= (pieces
.includes(this.getPiece(kp
[0],kp
[1])) ? 1 : 2);
244 let sideBoard
= this.getSideBoard(mirrorSide
);
245 let saveBoard
= this.board
;
246 this.board
= sideBoard
;
247 let res
= this.isAttacked(this.kingPos
[color
], [this.getOppCol(color
)])
248 ? [ JSON
.parse(JSON
.stringify(this.kingPos
[color
])) ]
250 this.board
= saveBoard
;
255 updateVariables(move)
257 super.updateVariables(move); //standard king
258 const piece
= this.getPiece(move.start
.x
,move.start
.y
);
259 const c
= this.getColor(move.start
.x
,move.start
.y
);
263 this.kingPos
[c
][0] = move.appear
[0].x
;
264 this.kingPos
[c
][1] = move.appear
[0].y
;
265 this.castleFlags
[c
] = [false,false];
269 unupdateVariables(move)
271 super.unupdateVariables(move);
272 const c
= this.getColor(move.start
.x
,move.start
.y
);
273 if (this.getPiece(move.start
.x
,move.start
.y
) == "l")
274 this.kingPos
[c
] = [move.start
.x
, move.start
.y
];
279 const pieces
= Object
.keys(VariantRules
.ALICE_CODES
);
280 const color
= this.turn
;
281 const kp
= this.kingPos
[color
];
282 const mirrorSide
= (pieces
.includes(this.getPiece(kp
[0],kp
[1])) ? 1 : 2);
283 let sideBoard
= this.getSideBoard(mirrorSide
);
284 let saveBoard
= this.board
;
285 this.board
= sideBoard
;
287 if (!this.isAttacked(this.kingPos
[color
], [this.getOppCol(color
)]))
290 res
= (color
== "w" ? "0-1" : "1-0");
291 this.board
= saveBoard
;
295 static get VALUES() {
296 return Object
.assign(
311 if (move.appear
.length
== 2 && move.appear
[0].p
== VariantRules
.KING
)
313 if (move.end
.y
< move.start
.y
)
320 String
.fromCharCode(97 + move.end
.y
) + (VariantRules
.size
[0]-move.end
.x
);
321 const piece
= this.getPiece(move.start
.x
, move.start
.y
);
323 const captureMark
= (move.vanish
.length
> move.appear
.length
? "x" : "");
325 if (["p","s"].includes(piece
) && captureMark
.length
== 1)
326 pawnMark
= String
.fromCharCode(97 + move.start
.y
); //start column
328 // Piece or pawn movement
329 let notation
= piece
.toUpperCase() + pawnMark
+ captureMark
+ finalSquare
;
330 if (['s','p'].includes(piece
) && !['s','p'].includes(move.appear
[0].p
))
333 notation
+= "=" + move.appear
[0].p
.toUpperCase();