Commit | Line | Data |
---|---|---|
08909cf4 BA |
1 | import { ChessRules, Move, PiPo } from "@/base_rules"; |
2 | import { randInt } from "@/utils/alea"; | |
d2af3400 BA |
3 | |
4 | export class FanoronaRules extends ChessRules { | |
5 | ||
08909cf4 BA |
6 | static get HasFlags() { |
7 | return false; | |
8 | } | |
9 | ||
10 | static get HasEnpassant() { | |
11 | return false; | |
12 | } | |
13 | ||
14 | static get Monochrome() { | |
15 | return true; | |
16 | } | |
17 | ||
18 | static get Lines() { | |
19 | let lines = []; | |
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]]); | |
25 | const columnDiags = [ | |
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]] | |
30 | ]; | |
31 | for (let j of [0, 2, 4, 6]) { | |
32 | lines = lines.concat( | |
33 | columnDiags.map(L => [[L[0][0], L[0][1] + j], [L[1][0], L[1][1] + j]]) | |
34 | ); | |
35 | } | |
36 | return lines; | |
37 | } | |
38 | ||
39 | static get Notoodark() { | |
40 | return true; | |
41 | } | |
42 | ||
43 | static GenRandInitFen() { | |
44 | return "ppppppppp/ppppppppp/pPpP1pPpP/PPPPPPPPP/PPPPPPPPP w 0"; | |
45 | } | |
46 | ||
47 | setOtherVariables(fen) { | |
48 | // Local stack of captures during a turn (squares + directions) | |
49 | this.captures = []; | |
50 | } | |
51 | ||
52 | static get size() { | |
53 | return { x: 5, y: 9 }; | |
54 | } | |
55 | ||
56 | static get PIECES() { | |
57 | return [V.PAWN]; | |
58 | } | |
59 | ||
60 | getPiece() { | |
61 | return V.PAWN; | |
62 | } | |
63 | ||
64 | getPpath(b) { | |
65 | return "Fanorona/" + b; | |
66 | } | |
67 | ||
68 | //TODO | |
69 | //getPPpath() {} | |
70 | ||
71 | getPotentialMovesFrom([x, y]) { | |
72 | // NOTE: (x + y) % 2 == 0 ==> has diagonals | |
73 | // TODO | |
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... | |
80 | return []; | |
81 | } | |
82 | ||
83 | filterValid(moves) { | |
84 | return moves; | |
85 | } | |
86 | ||
87 | getCheckSquares() { | |
88 | return []; | |
89 | } | |
90 | ||
91 | //TODO: function aux to detect if continuation captures | |
92 | //(not trivial, but not difficult) | |
93 | ||
94 | play(move) { | |
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 | |
100 | ); | |
101 | this.captures.push(captureNotEnding); //TODO: something more structured | |
102 | //with square + direction of capture | |
103 | if (captureNotEnding) move.notTheEnd = true; | |
104 | else { | |
105 | this.turn = oppCol; | |
106 | this.movesCount++; | |
107 | } | |
108 | this.postPlay(move); | |
109 | } | |
110 | ||
111 | undo(move) { | |
112 | V.UndoOnBoard(this.board, move); | |
113 | this.captures.pop(); | |
114 | if (move.turn != this.turn) { | |
115 | this.turn = move.turn; | |
116 | this.movesCount--; | |
117 | } | |
118 | this.postUndo(move); | |
119 | } | |
120 | ||
121 | getCurrentScore() { | |
122 | const color = this.turn; | |
123 | // If no stones on board, I lose | |
124 | if ( | |
125 | this.board.every(b => { | |
126 | return b.every(cell => { | |
127 | return (cell == "" || cell[0] != color); | |
128 | }); | |
129 | }) | |
130 | ) { | |
131 | return (color == 'w' ? "0-1" : "1-0"); | |
132 | } | |
133 | return "*"; | |
134 | } | |
135 | ||
136 | getComputerMove() { | |
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); | |
142 | let mvArray = []; | |
143 | while (captures.length >= 1) { | |
144 | // Then just pick random captures (trying to maximize) | |
145 | let candidates = captures.filter(c => !!c.notTheEnd); | |
146 | let mv = null; | |
147 | if (candidates.length >= 1) mv = candidates[randInt(candidates.length)]; | |
148 | else mv = captures[randInt(captures.length)]; | |
149 | this.play(mv); | |
150 | captures = (this.turn == color ? super.getAllValidMoves() : []); | |
151 | } | |
152 | if (mvArray.length >= 1) { | |
153 | for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); | |
154 | return mvArray; | |
155 | } | |
156 | // Just play a random move, which if possible do not let a capture | |
157 | let candidates = []; | |
158 | for (let m of moves) { | |
159 | this.play(m); | |
160 | const moves2 = super.getAllValidMoves(); | |
161 | if (moves2.every(m2 => m2.vanish.length <= 1)) | |
162 | candidates.push(m); | |
163 | this.undo(m); | |
164 | } | |
165 | if (candidates.length >= 1) return candidates[randInt(candidates.length)]; | |
166 | return moves[randInt(moves.length)]; | |
167 | } | |
168 | ||
169 | getNotation(move) { | |
170 | return ( | |
171 | V.CoordsToSquare(move.start) + | |
172 | (move.vanish.length >= 2 ? "x" : "") + | |
173 | V.CoordsToSquare(move.end) | |
174 | ); | |
175 | } | |
d2af3400 BA |
176 | |
177 | }; |