80a3cce3e13472707d5db80ce2d9e744c8763ddb
[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 // 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;
136 if (this.subTurn == 1) {
137 // Remove defining moves with un-movable def piece
138 moves = moves.filter(m => {
139 if (m.vanish.length >= 2 || m.vanish[0].p != 'u')
140 return true;
141 this.playOnBoard(m);
142 const canMove = super.filterValid(
143 super.getPotentialMovesFrom([m.end.x, m.end.y])).length >= 1;
144 this.undoOnBoard(m);
145 return canMove;
146 });
147 }
148 return super.filterValid(moves);
149 }
150
151 atLeastOneMove(color) {
152 if (this.subTurn != 1)
153 return true;
154 return super.atLeastOneMove(color);
155 }
156
157 // TODO: this method fails to detect undefined checks
158 underCheck(square_s, oppCol) {
159 if (super.underCheck(square_s, oppCol))
160 return true;
161 // Check potential specializations of undefined using reserve:
162 const allAttacks = Array.prototype.concat.apply(
163 ['r', 'n', 'b', 'q'].map(p => this.pieces()[p].moves[0]));
164 const [x, y] = [square_s[0], square_s[1]];
165 for (let i=0; i<this.size.x; i++) {
166 for (let j=0; j<this.size.y; j++) {
167 if (
168 this.board[i][j] != "" &&
169 this.getColor(i, j) == oppCol &&
170 this.getPiece(i, j) == 'u'
171 ) {
172 for (let stepDef of allAttacks) {
173 for (let s of stepDef.steps) {
174 if (!super.compatibleStep([i, j], [x, y], s, stepDef.range))
175 continue;
176 if (
177 super.findDestSquares(
178 [i, j],
179 {
180 captureTarget: [x, y],
181 captureSteps: [{steps: [s], range: stepDef.range}],
182 segments: false,
183 attackOnly: true,
184 one: false
185 }
186 )
187 ) {
188 return true;
189 }
190 }
191 }
192 }
193 }
194 }
195 return false;
196 }
197
198 // TODO: missing "undefined reset" check (is everything defined? If yes, reset if enough variety)
199 postPlay(move) {
200 const color = this.turn;
201 const toNextPlayer = () => {
202 this.turn = C.GetOppCol(color);
203 this.movesCount++;
204 };
205 if (this.movesCount <= 1) {
206 toNextPlayer();
207 return;
208 }
209 const captureUndef = (
210 move.vanish.length == 2 &&
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") {
218 this.reserve[color][move.appear[0].p]--;
219 if (move.vanish.length == 1 && move.vanish[0].p == 'u')
220 this.definition = move.end;
221 this.subTurn++;
222 }
223 else {
224 this.subTurn = 0;
225 this.captureUndef = move.end;
226 toNextPlayer();
227 }
228 }
229
230 isLastMove() {
231 return true; //called only on normal moves (not Teleport)
232 }
233
234 getCurrentScore(move_s) {
235 return (this.movesCount <= 2 ? "*" : super.getCurrentScore(move_s));
236 }
237
238 };