Get rid of livereload dependency. Draft Baroque (still some issues)
[xogo.git] / variants / Bario / class.js
1 import ChessRules from "/base_rules.js";
2 import PiPo from "/utils/PiPo.js";
3 import Move from "/utils/Move.js";
4
5 export default class BarioRules extends ChessRules {
6
7 static get Options() {
8 return {
9 select: C.Options.select,
10 input: C.Options.input,
11 styles: [
12 "atomic", "cannibal", "capture", "cylinder",
13 "dark", "madrasi", "rifle", "teleport"
14 ]
15 };
16 }
17
18 // Does not really seem necessary (although the author mention it)
19 // Instead, first move = pick a square for the king.
20 get hasFlags() {
21 return false;
22 }
23 get hasReserve() {
24 return true;
25 }
26
27 pieces(color, x, y) {
28 return Object.assign(
29 {
30 'u': {
31 "class": "undefined",
32 moves: []
33 }
34 },
35 super.pieces(color, x, y)
36 );
37 }
38
39 get onlyClick() {
40 return this.movesCount <= 1;
41 }
42
43 // Initiate the game by choosing a square for the king:
44 doClick(coords) {
45 const color = this.turn;
46 if (
47 this.movesCount <= 1 &&
48 (
49 (color == 'w' && coords.x == this.size.x - 1) ||
50 (color == 'b' && coords.x == 0)
51 )
52 ) {
53 return new Move({
54 appear: [ new PiPo({x: coords.x, y: coords.y, c: color, p: 'k' }) ],
55 vanish: [ new PiPo({x: coords.x, y: coords.y, c: color, p: 'u' }) ]
56 });
57 }
58 return null;
59 }
60
61 genRandInitBaseFen() {
62 return {
63 fen: "uuuuuuuu/pppppppp/8/8/8/8/PPPPPPPP/UUUUUUUU",
64 o: {}
65 }
66 }
67
68 getPartFen(o) {
69 return Object.assign(
70 {
71 captureUndef: (o.init || !this.captureUndef)
72 ? "-"
73 : C.CoordsToSquare(this.captureUndef)
74 },
75 super.getPartFen(o)
76 );
77 }
78
79 getReserveFen(o) {
80 if (o.init)
81 return "22212221";
82 return (
83 ["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("")
84 );
85 }
86
87 initReserves(reserveStr) {
88 super.initReserves(reserveStr, ['r', 'n', 'b', 'q']);
89 }
90
91 setOtherVariables(fenParsed) {
92 super.setOtherVariables(fenParsed);
93 this.captureUndef = fenParsed.captureUndef == '-'
94 ? null :
95 C.SquareToCoords(fenParsed.captureUndef);
96 this.definition = null;
97 }
98
99 canDrop([c, p], [i, j]) {
100 switch (this.subTurn) {
101 case 0:
102 return i == this.captureUndef.x && j == this.captureUndef.y;
103 case 1:
104 return this.getPiece(i, j) == 'u' && c == this.getColor(i, j);
105 }
106 return false; //never reached
107 }
108
109 getPotentialMovesFrom([x, y]) {
110 if (this.movesCount <= 1)
111 return [];
112 let moves = [];
113 switch (this.subTurn) {
114 case 0:
115 if (typeof x == "string")
116 moves = this.getDropMovesFrom([x, y]);
117 // Empty move: just start + end
118 moves.forEach(m => {m.vanish.pop(); m.appear.pop();});
119 break;
120 case 1:
121 // Both normal move (from defined piece) and definition allowed
122 if (typeof x == "string")
123 moves = this.getDropMovesFrom([x, y]);
124 else if (this.getPiece(x, y) != 'u')
125 moves = super.getPotentialMovesFrom([x, y]);
126 break;
127 case 2:
128 // We can only move the just-defined piece
129 if (x == this.definition.x && y == this.definition.y)
130 moves = super.getPotentialMovesFrom([x, y]);
131 break;
132 }
133 return moves;
134 }
135
136 filterValid(moves) {
137 if (this.movesCount <= 1 || this.subTurn == 0)
138 return moves;
139 let filterLater = [];
140 if (this.subTurn == 1) {
141 // Remove defining moves with un-movable def piece,
142 // and separate compatible definitions.
143 moves = moves.filter(m => {
144 if (m.vanish.length >= 2 || m.vanish[0].p != 'u')
145 return true;
146 this.playOnBoard(m);
147 const canMove = super.filterValid(
148 super.getPotentialMovesFrom([m.end.x, m.end.y])).length >= 1;
149 this.undoOnBoard(m);
150 if (canMove)
151 filterLater.push(m);
152 return false;
153 });
154 }
155 return super.filterValid(moves).concat(filterLater);
156 }
157
158 atLeastOneMove(color) {
159 if (this.subTurn != 1)
160 return true;
161 return super.atLeastOneMove(color);
162 }
163
164 underCheck(square_s, oppCol) {
165 if (super.underCheck(square_s, oppCol))
166 return true;
167 // Check potential specializations of undefined using reserve:
168 const allAttacks = Array.prototype.concat.apply(
169 ['r', 'n', 'b', 'q'].map(p => this.pieces()[p].moves[0]));
170 const [x, y] = square_s[0];
171 for (let i=0; i<this.size.x; i++) {
172 for (let j=0; j<this.size.y; j++) {
173 if (
174 this.board[i][j] != "" &&
175 this.getColor(i, j) == oppCol &&
176 this.getPiece(i, j) == 'u'
177 ) {
178 for (let stepDef of allAttacks) {
179 for (let s of stepDef.steps) {
180 if (!super.compatibleStep([i, j], [x, y], s, stepDef.range))
181 continue;
182 if (
183 super.findDestSquares(
184 [i, j],
185 {
186 captureTarget: [x, y],
187 captureSteps: [{steps: [s], range: stepDef.range}],
188 segments: false,
189 attackOnly: true,
190 one: false
191 }
192 )
193 ) {
194 return true;
195 }
196 }
197 }
198 }
199 }
200 }
201 return false;
202 }
203
204 postPlay(move) {
205 const color = this.turn;
206 if (this.movesCount <= 1 || move.reset || move.next) {
207 if (!move.next)
208 this.tryChangeTurn();
209 return;
210 }
211 if (this.subTurn == 0)
212 this.captureUndef = null; //already used
213 const captureUndef = (
214 move.vanish.length == 2 && //exclude subTurn == 0
215 move.vanish[1].c != color &&
216 move.vanish[1].p == 'u'
217 );
218 if (typeof move.start.x == "number" && !captureUndef)
219 // Normal move (including Teleport)
220 super.postPlay(move);
221 else if (typeof move.start.x == "string") {
222 super.updateReserve(
223 color, move.start.y, this.reserve[color][move.start.y] - 1);
224 if (move.vanish.length == 1 && move.vanish[0].p == 'u')
225 this.definition = move.end;
226 this.subTurn++;
227 }
228 else {
229 this.subTurn = 0;
230 this.captureUndef = move.end;
231 this.tryChangeTurn(null, captureUndef);
232 }
233 }
234
235 // NOTE: not "trying", the turn always change here (TODO?)
236 tryChangeTurn(move, captureUndef) {
237 this.definition = null;
238 this.subTurn = captureUndef ? 0 : 1;
239 this.turn = C.GetOppCol(this.turn);
240 this.movesCount++;
241 }
242
243 computeNextMove(move) {
244 if (
245 !this.definition || this.playerColor != this.turn ||
246 this.board.some(row => row.some(cell =>
247 cell.charAt(0) == this.turn && cell.charAt(1) == 'u'))
248 ) {
249 return;
250 }
251 const variety = (c) => {
252 return (
253 [...new Set(
254 Array.prototype.concat.apply([],
255 this.board.map(row =>
256 row.filter(cell =>
257 cell.charAt(0) == c && !['p', 'k'].includes(cell.charAt(1))
258 ).map(cell => cell.charAt(1))
259 )
260 )
261 )].length >= 2
262 );
263 };
264 let next = {start: move.end, end: move.end, vanish: [], appear: []};
265 this.playOnBoard(move);
266 const twoOrMorePieces = {w: variety('w'), b: variety('b')};
267 const resetCols =
268 Object.keys(twoOrMorePieces).filter(k => twoOrMorePieces[k]);
269 if (resetCols.length >= 1) {
270 for (let i=0; i<this.size.x; i++) {
271 for (let j=0; j<this.size.y; j++) {
272 const colIJ = this.getColor(i, j);
273 const pieceIJ = this.getPiece(i, j);
274 if (
275 resetCols.includes(colIJ) &&
276 this.board[i][j] != "" &&
277 !['p', 'k', 'u'].includes(pieceIJ)
278 ) {
279 // NOTE: could also use a "flip" strategy similar to Benedict
280 next.vanish.push(new PiPo({c: colIJ, p: pieceIJ, x: i, y: j}));
281 next.appear.push(new PiPo({c: colIJ, p: 'u', x: i, y: j}));
282 this.reserve[colIJ][pieceIJ]++;
283 }
284 }
285 }
286 super.re_drawReserve(resetCols);
287 }
288 this.undoOnBoard(move);
289 if (next.vanish.length >= 1) {
290 next.reset = true;
291 move.next = next;
292 }
293 }
294
295 isLastMove() {
296 return true; //called only on normal moves (not Teleport)
297 }
298
299 getCurrentScore(move_s) {
300 return (this.movesCount <= 2 ? "*" : super.getCurrentScore(move_s));
301 }
302
303 };