1 import { ChessRules
, Move
, PiPo
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class FanoronaRules
extends ChessRules
{
6 static get HasFlags() {
10 static get HasEnpassant() {
14 static get Monochrome() {
20 // Draw all inter-squares lines, shifted:
21 for (let i
= 0; i
< V
.size
.x
; i
++)
22 lines
.push([[i
+0.5, 0.5], [i
+0.5, V
.size
.y
-0.5]]);
23 for (let j
= 0; j
< V
.size
.y
; j
++)
24 lines
.push([[0.5, j
+0.5], [V
.size
.x
-0.5, j
+0.5]]);
26 [[0.5, 0.5], [2.5, 2.5]],
27 [[0.5, 2.5], [2.5, 0.5]],
28 [[2.5, 0.5], [4.5, 2.5]],
29 [[4.5, 0.5], [2.5, 2.5]]
31 for (let j
of [0, 2, 4, 6]) {
33 columnDiags
.map(L
=> [[L
[0][0], L
[0][1] + j
], [L
[1][0], L
[1][1] + j
]])
39 static get Notoodark() {
43 static GenRandInitFen() {
44 return "ppppppppp/ppppppppp/pPpP1pPpP/PPPPPPPPP/PPPPPPPPP w 0";
47 setOtherVariables(fen
) {
48 // Local stack of captures during a turn (squares + directions)
53 return { x: 5, y: 9 };
65 return "Fanorona/" + b
;
71 getPotentialMovesFrom([x
, y
]) {
72 // NOTE: (x + y) % 2 == 0 ==> has diagonals
74 // Même stratégie que Yote, revenir sur ses pas si stop avant de tout capturer
75 // Mais première capture obligatoire (si this.captures.length == 0).
76 // After a capture: allow only capturing.
77 // Warning: case 3 on Wikipedia page, if both percussion & aspiration,
78 // two different moves, cannot take all ==> adjust getPPpath showing arrows.
79 // nice looking arrows, with something representing a capture at its end...
91 //TODO: function aux to detect if continuation captures
92 //(not trivial, but not difficult)
95 const color
= this.turn
;
96 move.turn
= color
; //for undo
97 const captureNotEnding
= (
98 move.vanish
.length
>= 2 &&
99 true //TODO: detect if there are continuation captures
101 this.captures
.push(captureNotEnding
); //TODO: something more structured
102 //with square + direction of capture
103 if (captureNotEnding
) move.notTheEnd
= true;
112 V
.UndoOnBoard(this.board
, move);
114 if (move.turn
!= this.turn
) {
115 this.turn
= move.turn
;
122 const color
= this.turn
;
123 // If no stones on board, I lose
125 this.board
.every(b
=> {
126 return b
.every(cell
=> {
127 return (cell
== "" || cell
[0] != color
);
131 return (color
== 'w' ? "0-1" : "1-0");
137 const moves
= super.getAllValidMoves();
138 if (moves
.length
== 0) return null;
139 const color
= this.turn
;
140 // Capture available? If yes, play it
141 let captures
= moves
.filter(m
=> m
.vanish
.length
>= 2);
143 while (captures
.length
>= 1) {
144 // Then just pick random captures (trying to maximize)
145 let candidates
= captures
.filter(c
=> !!c
.notTheEnd
);
147 if (candidates
.length
>= 1) mv
= candidates
[randInt(candidates
.length
)];
148 else mv
= captures
[randInt(captures
.length
)];
150 captures
= (this.turn
== color
? super.getAllValidMoves() : []);
152 if (mvArray
.length
>= 1) {
153 for (let i
= mvArray
.length
- 1; i
>= 0; i
--) this.undo(mvArray
[i
]);
156 // Just play a random move, which if possible do not let a capture
158 for (let m
of moves
) {
160 const moves2
= super.getAllValidMoves();
161 if (moves2
.every(m2
=> m2
.vanish
.length
<= 1))
165 if (candidates
.length
>= 1) return candidates
[randInt(candidates
.length
)];
166 return moves
[randInt(moves
.length
)];
171 V
.CoordsToSquare(move.start
) +
172 (move.vanish
.length
>= 2 ? "x" : "") +
173 V
.CoordsToSquare(move.end
)