0b9678613623990eeef03d1e0f17b4f1e8b06c0b
1 import { ChessRules
, Move
, PiPo
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class YoteRules
extends ChessRules
{
6 static get HasFlags() {
10 static get HasEnpassant() {
14 static get Monochrome() {
18 static get Notoodark() {
22 static get ReverseColors() {
26 static IsGoodFen(fen
) {
27 if (!ChessRules
.IsGoodFen(fen
)) return false;
28 const fenParsed
= V
.ParseFen(fen
);
32 !fenParsed
.reserve
.match(/^([0-9]{1,2},?){2,2}$/)
37 if (!fenParsed
.lastMove
) return false;
38 const lmParts
= fenParsed
.lastMove
.split(",");
40 if (lp
!= "-" && !lp
.match(/^([a-f][1-5]){2,2}$/)) return false;
45 static ParseFen(fen
) {
46 const fenParts
= fen
.split(" ");
48 ChessRules
.ParseFen(fen
),
56 static GenRandInitFen(randomness
) {
57 return "6/6/6/6/6 w 0 12,12 -,-";
62 super.getFen() + " " +
63 this.getReserveFen() + " " +
69 return super.getFenForRepeat() + "_" + this.getReserveFen();
74 (!this.reserve
["w"] ? 0 : this.reserve
["w"][V
.PAWN
]) + "," +
75 (!this.reserve
["b"] ? 0 : this.reserve
["b"][V
.PAWN
])
80 const L
= this.lastMove
.length
;
81 const lm
= this.lastMove
[L
-1];
86 : V
.CoordsToSquare(lm
['w'].start
) + V
.CoordsToSquare(lm
['w'].end
)
92 : V
.CoordsToSquare(lm
['b'].start
) + V
.CoordsToSquare(lm
['b'].end
)
97 setOtherVariables(fen
) {
98 const fenParsed
= V
.ParseFen(fen
);
99 const reserve
= fenParsed
.reserve
.split(",").map(x
=> parseInt(x
, 10));
101 w: { [V
.PAWN
]: reserve
[0] },
102 b: { [V
.PAWN
]: reserve
[1] }
104 // And last moves (to avoid undoing your last move)
105 const lmParts
= fenParsed
.lastMove
.split(",");
106 this.lastMove
= [{ w: null, b: null }];
107 ['w', 'b'].forEach((c
, i
) => {
108 if (lmParts
[i
] != '-') {
109 this.lastMove
[0][c
] = {
110 start: V
.SquareToCoords(lmParts
[i
].substr(0, 2)),
111 end: V
.SquareToCoords(lmParts
[i
].substr(2))
115 // Local stack to know if (current) last move captured something
116 this.captures
= [false];
120 return { x: 5, y: 6 };
123 static get PIECES() {
128 if (i
>= V
.size
.x
) return i
== V
.size
.x
? "w" : "b";
129 return this.board
[i
][j
].charAt(0);
140 getReservePpath(index
, color
) {
141 return "Yote/" + color
+ V
.PAWN
;
144 static get RESERVE_PIECES() {
148 canIplay(side
, [x
, y
]) {
149 if (this.turn
!= side
) return false;
150 const L
= this.captures
.length
;
151 if (!this.captures
[L
-1]) return this.getColor(x
, y
) == side
;
152 return (x
< V
.size
.x
&& this.getColor(x
, y
) != side
);
155 // TODO: hoverHighlight() would well take an arg "side"...
156 hoverHighlight(x
, y
) {
157 const L
= this.captures
.length
;
158 if (!this.captures
[L
-1]) return false;
159 const oppCol
= V
.GetOppCol(this.turn
);
160 return (this.board
[x
][y
] != V
.EMPTY
&& this.getColor(x
, y
) == oppCol
);
163 // TODO: onlyClick() doesn't fulfill exactly its role.
164 // Seems that there is some lag... TOFIX
166 const L
= this.captures
.length
;
167 return (this.captures
[L
-1] && this.getColor(x
, y
) != this.turn
);
170 // PATCH related to above TO-DO:
171 getPossibleMovesFrom([x
, y
]) {
172 if (x
< V
.size
.x
&& this.board
[x
][y
] == V
.EMPTY
) return [];
173 return super.getPossibleMovesFrom([x
, y
]);
177 const L
= this.captures
.length
;
178 if (!this.captures
[L
-1]) return null;
179 const oppCol
= V
.GetOppCol(this.turn
);
180 if (this.board
[x
][y
] == V
.EMPTY
|| this.getColor(x
, y
) != oppCol
)
184 vanish: [ new PiPo({ x: x
, y: y
, c: oppCol
, p: V
.PAWN
}) ],
190 const color
= this.turn
;
191 const L
= this.captures
.length
;
193 this.captures
[L
-1] ||
194 !this.reserve
[color
] ||
195 this.reserve
[color
][V
.PAWN
] == 0
200 for (let i
= 0; i
< V
.size
.x
; i
++) {
201 for (let j
= 0; j
< V
.size
.y
; j
++) {
202 if (this.board
[i
][j
] == V
.EMPTY
) {
213 start: { x: x
, y: 0 }, //a bit artificial...
223 getPotentialMovesFrom([x
, y
]) {
224 const L
= this.captures
.length
;
225 if (this.captures
[L
-1]) {
226 if (x
>= V
.size
.x
) return [];
227 const mv
= this.doClick([x
, y
]);
228 return (!!mv
? [mv
] : []);
230 if (x
>= V
.size
.x
) return this.getReserveMoves([x
, y
]);
231 return this.getPotentialPawnMoves([x
, y
]);
234 getPotentialPawnMoves([x
, y
]) {
236 const color
= this.turn
;
237 const L
= this.lastMove
.length
;
238 const lm
= this.lastMove
[L
-1];
239 let forbiddenStep
= null;
240 if (!!lm
[color
] && x
== lm
[color
].end
.x
&& y
== lm
[color
].end
.y
) {
242 lm
[color
].start
.x
- lm
[color
].end
.x
,
243 lm
[color
].start
.y
- lm
[color
].end
.y
246 const oppCol
= V
.GetOppCol(color
);
247 for (let s
of V
.steps
[V
.ROOK
]) {
248 const [i1
, j1
] = [x
+ s
[0], y
+ s
[1]];
249 if (V
.OnBoard(i1
, j1
)) {
250 if (this.board
[i1
][j1
] == V
.EMPTY
) {
253 s
[0] != forbiddenStep
[0] ||
254 s
[1] != forbiddenStep
[1]
256 moves
.push(super.getBasicMove([x
, y
], [i1
, j1
]));
259 else if (this.getColor(i1
, j1
) == oppCol
) {
260 const [i2
, j2
] = [i1
+ s
[0], j1
+ s
[1]];
261 if (V
.OnBoard(i2
, j2
) && this.board
[i2
][j2
] == V
.EMPTY
) {
264 new PiPo({ x: i2
, y: j2
, c: color
, p: V
.PAWN
})
267 new PiPo({ x: x
, y: y
, c: color
, p: V
.PAWN
}),
268 new PiPo({ x: i1
, y: j1
, c: oppCol
, p: V
.PAWN
})
279 getAllPotentialMoves() {
280 const L
= this.captures
.length
;
281 const color
= (this.captures
[L
-1] ? V
.GetOppCol(this.turn
) : this.turn
);
282 let potentialMoves
= [];
283 for (let i
= 0; i
< V
.size
.x
; i
++) {
284 for (let j
= 0; j
< V
.size
.y
; j
++) {
285 if (this.board
[i
][j
] != V
.EMPTY
&& this.getColor(i
, j
) == color
) {
286 Array
.prototype.push
.apply(
288 this.getPotentialMovesFrom([i
, j
])
293 potentialMoves
= potentialMoves
.concat(
294 this.getReserveMoves(V
.size
.x
+ (color
== "w" ? 0 : 1)));
295 return potentialMoves
;
307 if (!super.atLeastOneMove()) {
308 // Search one reserve move
310 this.getReserveMoves(V
.size
.x
+ (this.turn
== "w" ? 0 : 1));
311 if (moves
.length
> 0) return true;
318 const color
= this.turn
;
319 move.turn
= color
; //for undo
320 const L
= this.lastMove
.length
;
322 this.lastMove
.push({ w: null, b: this.lastMove
[L
-1]['b'] });
323 if (move.appear
.length
== move.vanish
.length
) { //== 1
324 // Normal move (non-capturing, non-dropping, non-removal)
325 let lm
= this.lastMove
[L
- (color
== 'w' ? 0 : 1)];
326 if (!lm
[color
]) lm
[color
] = {};
327 lm
[color
].start
= move.start
;
328 lm
[color
].end
= move.end
;
330 const oppCol
= V
.GetOppCol(color
);
331 V
.PlayOnBoard(this.board
, move);
332 const captureNotEnding
= (
333 move.vanish
.length
== 2 &&
334 this.board
.some(b
=> b
.some(cell
=> cell
!= "" && cell
[0] == oppCol
))
336 this.captures
.push(captureNotEnding
);
337 // Change turn unless I just captured something,
338 // and an opponent stone can be removed from board.
339 if (!captureNotEnding
) {
347 V
.UndoOnBoard(this.board
, move);
348 if (this.turn
== 'b') this.lastMove
.pop();
349 else this.lastMove
['b'] = null;
351 if (move.turn
!= this.turn
) {
352 this.turn
= move.turn
;
359 if (move.vanish
.length
== 0) {
360 const color
= move.appear
[0].c
;
361 this.reserve
[color
][V
.PAWN
]--;
362 if (this.reserve
[color
][V
.PAWN
] == 0) delete this.reserve
[color
];
367 if (move.vanish
.length
== 0) {
368 const color
= move.appear
[0].c
;
369 if (!this.reserve
[color
]) this.reserve
[color
] = { [V
.PAWN
]: 0 };
370 this.reserve
[color
][V
.PAWN
]++;
375 if (this.movesCount
<= 2) return "*";
376 const color
= this.turn
;
377 // If no stones on board, or no move available, I lose
379 this.board
.every(b
=> {
380 return b
.every(cell
=> {
381 return (cell
== "" || cell
[0] != color
);
385 !this.atLeastOneMove()
387 return (color
== 'w' ? "0-1" : "1-0");
393 const moves
= super.getAllValidMoves();
394 if (moves
.length
== 0) return null;
395 const color
= this.turn
;
396 const oppCol
= V
.GetOppCol(color
);
397 // Capture available? If yes, play it
398 const captures
= moves
.filter(m
=> m
.vanish
.length
== 2);
399 if (captures
.length
>= 1) {
400 const m1
= captures
[randInt(captures
.length
)];
402 const moves2
= super.getAllValidMoves();
403 // Remove a stone which was about to capture one of ours, if possible
405 for (let m2
of moves2
) {
406 const [x
, y
] = [m2
.start
.x
, m2
.start
.y
];
407 for (let s
of V
.steps
[V
.ROOK
]) {
408 const [i
, j
] = [x
+ 2*s
[0], y
+ 2*s
[1]];
411 this.board
[i
][j
] == V
.EMPTY
&&
412 this.board
[i
- s
[0], j
- s
[1]] != V
.EMPTY
&&
413 this.getColor(i
- s
[0], j
- s
[1]) == color
421 if (candidates
.length
>= 1)
422 return [m1
, candidates
[randInt(candidates
.length
)]];
423 return [m1
, moves2
[randInt(moves2
.length
)]];
425 // Just play a random move, which if possible do not let a capture
427 for (let m
of moves
) {
429 const moves2
= super.getAllValidMoves();
430 if (moves2
.every(m2
=> m2
.vanish
.length
<= 1))
434 if (candidates
.length
>= 1) return candidates
[randInt(candidates
.length
)];
435 return moves
[randInt(moves
.length
)];
439 let evaluation
= super.evalPosition();
441 evaluation
+= this.reserve
["w"][V
.PAWN
];
442 evaluation
-= this.reserve
["b"][V
.PAWN
];
447 if (move.vanish
.length
== 0)
449 return "@" + V
.CoordsToSquare(move.end
);
450 if (move.appear
.length
== 0)
451 // Removal after capture:
452 return V
.CoordsToSquare(move.start
) + "X";
454 V
.CoordsToSquare(move.start
) +
455 (move.vanish
.length
== 2 ? "x" : "") +
456 V
.CoordsToSquare(move.end
)