Add Alice Chess, fix a few things in base_rules.js
[xogo.git] / variants / Alice / class.js
1 import ChessRules from "/base_rules.js";
2 import { ArrayFun } from "/utils/array.js";
3
4 export default class AliceRules extends ChessRules {
5
6 static get Options() {
7 return {
8 select: C.Options.select,
9 input: C.Options.input,
10 styles: [
11 "balance",
12 "capture",
13 "cylinder",
14 "dark",
15 "doublemove",
16 "progressive",
17 "zen"
18 ]
19 };
20 }
21
22 // To the other side of the mirror and back...
23 static get ALICE_PIECES() {
24 return {
25 s: "p",
26 u: "r",
27 o: "n",
28 c: "b",
29 t: "q",
30 l: "k"
31 };
32 }
33 static get ALICE_CODES() {
34 return {
35 p: "s",
36 r: "u",
37 n: "o",
38 b: "c",
39 q: "t",
40 k: "l"
41 };
42 }
43
44 getPieceType(x, y, p) {
45 if (!p)
46 p = super.getPiece(x, y);
47 return V.ALICE_PIECES[p] || p;
48 }
49
50 pieces(color, x, y) {
51 let alices = {
52 's': {"class": "alice-pawn", "moveas": "p"},
53 'u': {"class": "alice-rook", "moveas": "r"},
54 'o': {"class": "alice-knight", "moveas": "n"},
55 'c': {"class": "alice-bishop", "moveas": "b"},
56 't': {"class": "alice-queen", "moveas": "q"},
57 'l': {"class": "alice-king", "moveas": "k"}
58 };
59 return Object.assign(alices, super.pieces(color, x, y));
60 }
61
62 fromSameWorld(p1, p2) {
63 return (
64 (V.ALICE_PIECES[p1] && V.ALICE_PIECES[p2]) ||
65 (V.ALICE_CODES[p1] && V.ALICE_CODES[p2])
66 );
67 }
68
69 // Step of p over i,j ?
70 canStepOver(i, j, p) {
71 return (
72 this.board[i][j] == "" || !this.fromSameWorld(this.getPiece(i, j), p));
73 }
74
75 // NOTE: castle & enPassant
76 // https://www.chessvariants.com/other.dir/alice.html
77 getPotentialMovesFrom([x, y]) {
78 return super.getPotentialMovesFrom([x, y]).filter(m => {
79 // Remove moves landing on occupied square on other board
80 return (
81 this.board[m.end.x][m.end.y] == "" ||
82 this.fromSameWorld(m.vanish[0].p, m.vanish[1].p)
83 );
84 }).map(m => {
85 // Apply Alice rule: go to the other side of the mirror
86 if (Object.keys(V.ALICE_CODES).includes(m.vanish[0].p))
87 // Board 1
88 m.appear.forEach(a => a.p = V.ALICE_CODES[a.p])
89 else
90 // Board 2
91 m.appear.forEach(a => a.p = V.ALICE_PIECES[a.p])
92 return m;
93 });
94 }
95
96 isKing(symbol) {
97 return ['k', 'l'].includes(symbol);
98 }
99
100 getCurrentScore() {
101 const color = this.turn;
102 const inCheck = this.underCheck(this.searchKingPos(color));
103 let someLegalMove = false;
104 // Search for legal moves: if any is found and
105 // does not change king world (if under check), then game not over.
106 for (let i=0; i<this.size.x; i++) {
107 for (let j=0; j<this.size.y; j++) {
108 if (this.getColor(i, j) == color) {
109 const moves = this.filterValid(this.getPotentialMovesFrom([i, j]));
110 if (
111 moves.length >= 1 &&
112 (
113 !inCheck ||
114 moves.some(m => m.vanish.every(v => !this.isKing(v.p)))
115 )
116 ) {
117 return "*";
118 }
119 }
120 }
121 }
122 // Couldn't find any legal move
123 return (inCheck ? "1/2" : (color == 'w' ? "0-1" : "1-0"));
124 }
125
126 };