Animate castle moves + better Apocalypse draft
[xogo.git] / variants / Apocalypse / class.js
1 import ChessRules from "/base_rules.js";
2 import {ArrayFun} from "/utils/array.js";
3
4 export class ApocalypseRules extends ChessRules {
5
6 static get Options() {
7 return {};
8 }
9
10 get pawnPromotions() {
11 return ['n', 'p'];
12 }
13
14 get size() {
15 return {x: 5, y: 5};
16 }
17
18 setOtherVariables(fenParsed) {
19 super.setOtherVariables(fenParsed);
20 this.whiteMove = fenParsed.whiteMove != "-"
21 ? JSON.parse(fenParsed.whiteMove)
22 : null;
23 }
24
25 genRandInitBaseFen() {
26 return {
27 fen: "npppn/p3p/5/P3P/NPPPN w 0",
28 o: {"flags":"00"}
29 };
30 }
31
32 getPartFen(o) {
33 let parts = super.getPartFen(o);
34 parts["whiteMove"] = this.whiteMove || "-";
35 return parts;
36 }
37
38 getFlagsFen() {
39 return Object.values(this.penaltyFlags).join("");
40 }
41
42 setFlags(fenflags) {
43 this.penaltyFlags = ArrayFun.toObject(
44 ['w', 'b'], [0, 1].map(i => parseInt(fenflags.charAt(i), 10)));
45 }
46
47 getWhitemoveFen() {
48 return !this.whiteMove ? "-" : JSON.stringify(this.whiteMove);
49 }
50
51 // Allow pawns to move diagonally and capture vertically,
52 // because some of these moves might be valid a posteriori.
53 // They will be flagged as 'illegal' in a first time, however.
54 pieces(color, x, y) {
55 const pawnShift = (color == "w" ? -1 : 1);
56 return {
57 'p': {
58 "class": "pawn",
59 moves: [
60 {
61 steps: [[pawnShift, 0], [pawnShift, -1], [pawnShift, 1]],
62 range: 1
63 }
64 ],
65 },
66 'n': super.pieces(color, x, y)['n']
67 };
68 }
69
70 // Allow self-captures, because they might be valid
71 // if opponent takes on the same square (luck...)
72 canTake() {
73 return true;
74 }
75
76 getPotentialMovesFrom([x, y]) {
77 let moves = [];
78 if (this.subTurn == 2) {
79 const start = this.moveStack[0].end;
80 if (x == start.x && y == start.y) {
81 // Move the pawn to any empty square not on last rank (== x)
82 for (let i=0; i<this.size.x; i++) {
83 if (i == x)
84 continue;
85 for (let j=0; j<this.size.y; j++) {
86 if (this.board[i][j] == "")
87 moves.push(this.getBasicMove([x, y], [i, j]));
88 }
89 }
90 }
91 }
92 else {
93 moves = super.getPotentialMovesFrom([x, y])
94 // Flag a priori illegal moves
95 moves.forEach(m => {
96 if (
97 // Self-capture test:
98 (m.vanish.length == 2 && m.vanish[1].c == m.vanish[0].c) ||
99 // Pawn going diagonaly to empty square, or vertically to occupied
100 (
101 m.vanish[0].p == 'p' &&
102 (
103 (m.end.y == m.start.y && m.vanish.length == 2) ||
104 (m.end.y != m.start.y && m.vanish.length == 1)
105 )
106 )
107 ) {
108 m.illegal = true;
109 }
110 });
111 }
112 return moves;
113 }
114
115 filterValid(moves) {
116 // No checks:
117 return moves;
118 }
119
120
121 //TODO: from here
122
123 // White and black (partial) moves were played: merge
124 // + animate both at the same time !
125 resolveSynchroneMove(move) {
126 // TODO
127 }
128
129 play(move) {
130 if (this.subTurn...) //TODO: detect (mark?) if pawn move arriving on last rank (=> subTurn++)
131 this.turn = V.GetOppCol(this.turn);
132 this.movesCount++;
133 this.postPlay(move);
134 }
135
136 postPlay(move) {
137 if (pawn promotion into pawn) {
138 this.curMove move; //TODO: animate both move at same time + effects AFTER !
139 this.subTurn = 2
140 }
141 else if (this.turn == 'b')
142 // NOTE: whiteMove is used read-only, so no need to copy
143 this.whiteMove = move;
144 }
145 else {
146 // A full turn just ended:
147 const [wMove, bMove] = this.resolveSynchroneMove(move);
148 V.PlayOnBoard(this.board, smove); //----> ici : animate both !
149 this.whiteMove = null;
150 }
151 }
152
153 //until here
154
155
156 atLeastOneLegalMove(color) {
157 for (let i=0; i<this.size.x; i++) {
158 for (let j=0; j<this.size.y; j++) {
159 if (
160 this.board[i][j] != "" &&
161 this.getColor(i, j) == color &&
162 this.getPotentialMoves([i, j]).some(m => !m.illegal)
163 ) {
164 return true;
165 }
166 }
167 }
168 return false;
169 }
170
171 getCurrentScore() {
172 if (this.turn == 'b') {
173 // Turn (white + black) not over yet.
174 // Could be stalemate if black cannot move (legally):
175 if (!this.atLeastOneLegalMove('b'))
176 return "1/2";
177 return "*";
178 }
179 // Count footmen: if a side has none, it loses
180 let fmCount = { 'w': 0, 'b': 0 };
181 for (let i=0; i<5; i++) {
182 for (let j=0; j<5; j++) {
183 if (this.board[i][j] != V.EMPTY && this.getPiece(i, j) == V.PAWN)
184 fmCount[this.getColor(i, j)]++;
185 }
186 }
187 if (Object.values(fmCount).some(v => v == 0)) {
188 if (fmCount['w'] == 0 && fmCount['b'] == 0)
189 // Everyone died
190 return "1/2";
191 if (fmCount['w'] == 0) return "0-1";
192 return "1-0"; //fmCount['b'] == 0
193 }
194 // Check penaltyFlags: if a side has 2 or more, it loses
195 if (Object.values(this.penaltyFlags).every(v => v == 2)) return "1/2";
196 if (this.penaltyFlags['w'] == 2) return "0-1";
197 if (this.penaltyFlags['b'] == 2) return "1-0";
198 if (!this.atLeastOneLegalMove('w') || !this.atLeastOneLegalMove('b'))
199 // Stalemate (should be very rare)
200 return "1/2";
201 return "*";
202 }
203
204 };