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