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