1 import { ChessRules
, Move
, PiPo
} from "@/base_rules";
3 export class YoteRules
extends ChessRules
{
5 static get HasFlags() {
9 static get HasEnpassant() {
13 static get Monochrome() {
17 static get Notoodark() {
21 static get ReverseColors() {
25 static IsGoodFen(fen
) {
26 if (!ChessRules
.IsGoodFen(fen
)) return false;
27 const fenParsed
= V
.ParseFen(fen
);
31 !fenParsed
.reserve
.match(/^([0-9]{1,2},){2,2}$/)
36 if (!fenParsed
.lastMove
) return false;
37 const lmParts
= fenParsed
.lastMove
.split(",");
39 if (lp
!= "-" && !lp
.match(/^([a-f][1-5]){2,2}$/)) return false;
44 static ParseFen(fen
) {
45 const fenParts
= fen
.split(" ");
47 ChessRules
.ParseFen(fen
),
55 static GenRandInitFen(randomness
) {
56 return "6/6/6/6/6 w 0 12,12 -,-";
61 super.getFen() + " " +
62 this.getReserveFen() + " " +
68 return super.getFenForRepeat() + "_" + this.getReserveFen();
73 (!this.reserve
["w"] ? 0 : this.reserve
["w"][V
.PAWN
]) + "," +
74 (!this.reserve
["b"] ? 0 : this.reserve
["b"][V
.PAWN
])
79 const L
= this.lastMove
.length
;
80 const lm
= this.lastMove
[L
-1];
85 : V
.CoordsToSquare(lm
['w'].start
) + V
.CoordsToSquare(lm
['w'].end
)
91 : V
.CoordsToSquare(lm
['b'].start
) + V
.CoordsToSquare(lm
['b'].end
)
96 setOtherVariables(fen
) {
97 const fenParsed
= V
.ParseFen(fen
);
98 const reserve
= fenParsed
.reserve
.split(",").map(x
=> parseInt(x
, 10));
100 w: { [V
.PAWN
]: reserve
[0] },
101 b: { [V
.PAWN
]: reserve
[1] }
103 // And last moves (to avoid undoing your last move)
104 const lmParts
= fenParsed
.lastMove
.split(",");
105 this.lastMove
= [{ w: null, b: null }];
106 ['w', 'b'].forEach((c
, i
) => {
107 if (lmParts
[i
] != '-') {
108 this.lastMove
[0][c
] = {
109 start: V
.SquareToCoords(lmParts
[i
].substr(0, 2)),
110 end: V
.SquareToCoords(lmParts
[i
].substr(2))
114 // Local stack to know if (current) last move captured something
115 this.captures
= [false];
119 return { x: 5, y: 6 };
122 static get PIECES() {
127 if (i
>= V
.size
.x
) return i
== V
.size
.x
? "w" : "b";
128 return this.board
[i
][j
].charAt(0);
139 getReservePpath(index
, color
) {
140 return "Yote/" + color
+ V
.PAWN
;
143 static get RESERVE_PIECES() {
147 canIplay(side
, [x
, y
]) {
148 if (this.turn
!= side
) return false;
149 const L
= this.captures
.length
;
150 if (!this.captures
[L
-1]) return this.getColor(x
, y
) == side
;
151 return (x
< V
.size
.x
&& this.getColor(x
, y
) != side
);
154 hoverHighlight(x
, y
) {
155 const L
= this.captures
.length
;
156 if (!this.captures
[L
-1]) return false;
157 const oppCol
= V
.GetOppCol(this.turn
);
158 return (this.board
[x
][y
] != V
.EMPTY
&& this.getColor(x
, y
) == oppCol
);
161 // TODO: onlyClick() doesn't fulfill exactly its role.
162 // Seems that there is some lag... TOFIX
164 const L
= this.captures
.length
;
165 return (this.captures
[L
-1] && this.getColor(x
, y
) != this.turn
);
168 // PATCH related to above TO-DO:
169 getPossibleMovesFrom([x
, y
]) {
170 if (x
< V
.size
.x
&& this.board
[x
][y
] == V
.EMPTY
) return [];
171 return super.getPossibleMovesFrom([x
, y
]);
175 const L
= this.captures
.length
;
176 if (!this.captures
[L
-1]) return null;
177 const oppCol
= V
.GetOppCol(this.turn
);
178 if (this.board
[x
][y
] == V
.EMPTY
|| this.getColor(x
, y
) != oppCol
)
182 vanish: [ new PiPo({ x: x
, y: y
, c: oppCol
, p: V
.PAWN
}) ],
188 const color
= this.turn
;
189 if (this.reserve
[color
][V
.PAWN
] == 0) return [];
191 for (let i
= 0; i
< V
.size
.x
; i
++) {
192 for (let j
= 0; j
< V
.size
.y
; j
++) {
193 if (this.board
[i
][j
] == V
.EMPTY
) {
204 start: { x: x
, y: 0 }, //a bit artificial...
214 getPotentialMovesFrom([x
, y
]) {
215 const L
= this.captures
.length
;
216 if (this.captures
[L
-1]) {
217 if (x
>= V
.size
.x
) return [];
218 const mv
= this.doClick([x
, y
]);
219 return (!!mv
? [mv
] : []);
222 return this.getReserveMoves([x
, y
]);
223 return this.getPotentialPawnMoves([x
, y
]);
226 getPotentialPawnMoves([x
, y
]) {
228 const color
= this.turn
;
229 const L
= this.lastMove
.length
;
230 const lm
= this.lastMove
[L
-1];
231 let forbiddenStep
= null;
234 lm
[color
].start
.x
- lm
[color
].end
.x
,
235 lm
[color
].start
.y
- lm
[color
].end
.y
238 const oppCol
= V
.GetOppCol(color
);
239 for (let s
of V
.steps
[V
.ROOK
]) {
242 s
[0] == forbiddenStep
[0] && s
[1] == forbiddenStep
[1]
246 const [i1
, j1
] = [x
+ s
[0], y
+ s
[1]];
247 if (V
.OnBoard(i1
, j1
)) {
248 if (this.board
[i1
][j1
] == V
.EMPTY
)
249 moves
.push(super.getBasicMove([x
, y
], [i1
, j1
]));
250 else if (this.getColor(i1
, j1
) == oppCol
) {
251 const [i2
, j2
] = [i1
+ s
[0], j1
+ s
[1]];
252 if (V
.OnBoard(i2
, j2
) && this.board
[i2
][j2
] == V
.EMPTY
) {
255 new PiPo({ x: i2
, y: j2
, c: color
, p: V
.PAWN
})
258 new PiPo({ x: x
, y: y
, c: color
, p: V
.PAWN
}),
259 new PiPo({ x: i1
, y: j1
, c: oppCol
, p: V
.PAWN
})
270 getAllPotentialMoves() {
271 let moves
= super.getAllPotentialMoves();
272 const color
= this.turn
;
273 moves
= moves
.concat(
274 this.getReserveMoves(V
.size
.x
+ (color
== "w" ? 0 : 1)));
287 if (!super.atLeastOneMove()) {
288 // Search one reserve move
290 this.getReserveMoves(V
.size
.x
+ (this.turn
== "w" ? 0 : 1));
291 if (moves
.length
> 0) return true;
298 const color
= this.turn
;
299 move.turn
= color
; //for undo
300 const L
= this.lastMove
.length
;
302 this.lastMove
.push({ w: null, b: this.lastMove
[L
-1]['b'] });
303 if (move.appear
.length
== move.vanish
.length
) { //== 1
304 // Normal move (non-capturing, non-dropping, non-removal)
305 let lm
= this.lastMove
[L
- (color
== 'w' ? 0 : 1)];
306 if (!lm
[color
]) lm
[color
] = {};
307 lm
[color
].start
= move.start
;
308 lm
[color
].end
= move.end
;
310 const oppCol
= V
.GetOppCol(color
);
311 V
.PlayOnBoard(this.board
, move);
312 const captureNotEnding
= (
313 move.vanish
.length
== 2 &&
314 this.board
.some(b
=> b
.some(cell
=> cell
!= "" && cell
[0] == oppCol
))
316 this.captures
.push(captureNotEnding
);
317 // Change turn unless I just captured something,
318 // and an opponent stone can be removed from board.
319 if (!captureNotEnding
) {
327 V
.UndoOnBoard(this.board
, move);
328 if (this.turn
== 'b') this.lastMove
.pop();
329 else this.lastMove
['b'] = null;
331 if (move.turn
!= this.turn
) {
332 this.turn
= move.turn
;
339 if (move.vanish
.length
== 0) {
340 const color
= move.appear
[0].c
;
341 this.reserve
[color
][V
.PAWN
]--;
342 if (this.reserve
[color
][V
.PAWN
] == 0) delete this.reserve
[color
];
347 if (move.vanish
.length
== 0) {
348 const color
= move.appear
[0].c
;
349 if (!this.reserve
[color
]) this.reserve
[color
] = { [V
.PAWN
]: 0 };
350 this.reserve
[color
][V
.PAWN
]++;
355 if (this.movesCount
<= 2) return "*";
356 const color
= this.turn
;
357 // If no stones on board, or no move available, I lose
359 this.board
.every(b
=> {
360 return b
.every(cell
=> {
361 return (cell
== "" || cell
[0] != color
);
365 !this.atLeastOneMove()
367 return (color
== 'w' ? "0-1" : "1-0");
372 static get SEARCH_DEPTH() {
377 let evaluation
= super.evalPosition();
379 evaluation
+= this.reserve
["w"][V
.PAWN
];
380 evaluation
-= this.reserve
["b"][V
.PAWN
];
385 if (move.vanish
.length
== 0)
387 return "@" + V
.CoordsToSquare(move.end
);
388 if (move.appear
.length
== 0)
389 // Removal after capture:
390 return V
.CoordsToSquare(move.start
) + "X";
392 V
.CoordsToSquare(move.start
) +
393 (move.vanish
.length
== 2 ? "x" : "") +
394 V
.CoordsToSquare(move.end
)