Commit | Line | Data |
---|---|---|
b9139251 | 1 | import { ChessRules, PiPo, Move } from "@/base_rules"; |
241bf8f2 BA |
2 | import { ArrayFun } from "@/utils/array"; |
3 | import { randInt } from "@/utils/alea"; | |
4 | ||
5 | export const VariantRules = class HiddenRules extends ChessRules { | |
6 | static get HasFlags() { | |
7 | return false; | |
8 | } | |
9 | ||
10 | static get HasEnpassant() { | |
11 | return false; | |
12 | } | |
13 | ||
14 | // Analyse in Hidden mode makes no sense | |
15 | static get CanAnalyze() { | |
16 | return false; | |
17 | } | |
18 | ||
19 | // Moves are revealed only when game ends | |
20 | static get ShowMoves() { | |
21 | return "none"; | |
22 | } | |
23 | ||
24 | static get HIDDEN_DECODE() { | |
25 | return { | |
26 | s: "p", | |
27 | t: "q", | |
28 | u: "r", | |
29 | c: "b", | |
30 | o: "n", | |
31 | l: "k" | |
32 | }; | |
33 | } | |
34 | static get HIDDEN_CODE() { | |
35 | return { | |
36 | p: "s", | |
37 | q: "t", | |
38 | r: "u", | |
39 | b: "c", | |
40 | n: "o", | |
41 | k: "l" | |
42 | }; | |
43 | } | |
44 | ||
45 | static get PIECES() { | |
46 | return ChessRules.PIECES.concat(Object.values(V.HIDDEN_CODE)); | |
47 | } | |
48 | ||
b9139251 BA |
49 | // Pieces can be hidden :) |
50 | getPiece(i, j) { | |
51 | const piece = this.board[i][j].charAt(1); | |
52 | if (Object.keys(V.HIDDEN_DECODE).includes(piece)) | |
53 | return V.HIDDEN_DECODE[piece]; | |
54 | return piece; | |
55 | } | |
56 | ||
241bf8f2 BA |
57 | // Scan board for kings positions (no castling) |
58 | scanKingsRooks(fen) { | |
59 | this.kingPos = { w: [-1, -1], b: [-1, -1] }; | |
60 | const fenRows = V.ParseFen(fen).position.split("/"); | |
61 | for (let i = 0; i < fenRows.length; i++) { | |
62 | let k = 0; //column index on board | |
63 | for (let j = 0; j < fenRows[i].length; j++) { | |
64 | switch (fenRows[i].charAt(j)) { | |
65 | case "k": | |
66 | case "l": | |
67 | this.kingPos["b"] = [i, k]; | |
68 | break; | |
69 | case "K": | |
70 | case "L": | |
71 | this.kingPos["w"] = [i, k]; | |
72 | break; | |
73 | default: { | |
74 | const num = parseInt(fenRows[i].charAt(j)); | |
75 | if (!isNaN(num)) k += num - 1; | |
76 | } | |
77 | } | |
78 | k++; | |
79 | } | |
80 | } | |
81 | } | |
82 | ||
83 | getPpath(b, color, score) { | |
84 | if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) { | |
85 | // Supposed to be hidden. | |
86 | if (score == "*" && (!color || color != b[0])) | |
87 | return "Hidden/" + b[0] + "p"; | |
88 | // Else: condition OK to show the piece | |
89 | return b[0] + V.HIDDEN_DECODE[b[1]]; | |
90 | } | |
91 | // The piece is already not supposed to be hidden: | |
92 | return b; | |
93 | } | |
94 | ||
b9139251 BA |
95 | getBasicMove([sx, sy], [ex, ey], tr) { |
96 | let mv = new Move({ | |
97 | appear: [ | |
98 | new PiPo({ | |
99 | x: ex, | |
100 | y: ey, | |
101 | c: tr ? tr.c : this.getColor(sx, sy), | |
102 | p: tr ? tr.p : this.board[sx][sy].charAt(1) | |
103 | }) | |
104 | ], | |
105 | vanish: [ | |
106 | new PiPo({ | |
107 | x: sx, | |
108 | y: sy, | |
109 | c: this.getColor(sx, sy), | |
110 | p: this.board[sx][sy].charAt(1) | |
111 | }) | |
112 | ] | |
113 | }); | |
114 | ||
115 | // The opponent piece disappears if we take it | |
116 | if (this.board[ex][ey] != V.EMPTY) { | |
117 | mv.vanish.push( | |
118 | new PiPo({ | |
119 | x: ex, | |
120 | y: ey, | |
121 | c: this.getColor(ex, ey), | |
122 | p: this.board[ex][ey].charAt(1) | |
123 | }) | |
124 | ); | |
125 | // Pieces are revealed when they capture | |
126 | if (Object.keys(V.HIDDEN_DECODE).includes(mv.appear[0].p)) | |
127 | mv.appear[0].p = V.HIDDEN_DECODE[mv.appear[0].p]; | |
128 | } | |
129 | return mv; | |
130 | } | |
131 | ||
132 | // What are the king moves from square x,y ? | |
133 | getPotentialKingMoves(sq) { | |
134 | // No castling: | |
135 | return this.getSlideNJumpMoves( | |
136 | sq, | |
137 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), | |
138 | "oneStep" | |
139 | ); | |
140 | } | |
241bf8f2 | 141 | |
241bf8f2 BA |
142 | static GenRandInitFen() { |
143 | let pieces = { w: new Array(8), b: new Array(8) }; | |
144 | // Shuffle pieces + pawns on two first ranks | |
145 | for (let c of ["w", "b"]) { | |
146 | let positions = ArrayFun.range(16); | |
147 | ||
148 | // Get random squares for bishops | |
149 | let randIndex = 2 * randInt(8); | |
150 | const bishop1Pos = positions[randIndex]; | |
151 | // The second bishop must be on a square of different color | |
152 | let randIndex_tmp = 2 * randInt(8) + 1; | |
153 | const bishop2Pos = positions[randIndex_tmp]; | |
154 | // Remove chosen squares | |
155 | positions.splice(Math.max(randIndex, randIndex_tmp), 1); | |
156 | positions.splice(Math.min(randIndex, randIndex_tmp), 1); | |
157 | ||
158 | // Get random squares for knights | |
159 | randIndex = randInt(14); | |
160 | const knight1Pos = positions[randIndex]; | |
161 | positions.splice(randIndex, 1); | |
162 | randIndex = randInt(13); | |
163 | const knight2Pos = positions[randIndex]; | |
164 | positions.splice(randIndex, 1); | |
165 | ||
b9139251 | 166 | // Get random squares for rooks |
241bf8f2 | 167 | randIndex = randInt(12); |
b9139251 BA |
168 | const rook1Pos = positions[randIndex]; |
169 | positions.splice(randIndex, 1); | |
170 | randIndex = randInt(11); | |
171 | const rook2Pos = positions[randIndex]; | |
172 | positions.splice(randIndex, 1); | |
173 | ||
174 | // Get random square for queen | |
175 | randIndex = randInt(10); | |
241bf8f2 BA |
176 | const queenPos = positions[randIndex]; |
177 | positions.splice(randIndex, 1); | |
178 | ||
b9139251 BA |
179 | // Get random square for queen |
180 | randIndex = randInt(9); | |
181 | const kingPos = positions[randIndex]; | |
182 | positions.splice(randIndex, 1); | |
241bf8f2 | 183 | |
b9139251 BA |
184 | // Pawns position are all remaining slots: |
185 | for (let p of positions) | |
186 | pieces[c][p] = "s"; | |
241bf8f2 BA |
187 | |
188 | // Finally put the shuffled pieces in the board array | |
b9139251 BA |
189 | pieces[c][rook1Pos] = "u"; |
190 | pieces[c][knight1Pos] = "o"; | |
191 | pieces[c][bishop1Pos] = "c"; | |
192 | pieces[c][queenPos] = "t"; | |
193 | pieces[c][kingPos] = "l"; | |
194 | pieces[c][bishop2Pos] = "c"; | |
195 | pieces[c][knight2Pos] = "o"; | |
196 | pieces[c][rook2Pos] = "u"; | |
241bf8f2 | 197 | } |
b9139251 BA |
198 | let upFen = pieces["b"].join(""); |
199 | upFen = upFen.substr(0,8) + "/" + upFen.substr(8); | |
200 | let downFen = pieces["b"].join("").toUpperCase(); | |
201 | downFen = downFen.substr(0,8) + "/" + downFen.substr(8); | |
202 | return upFen + "/8/8/8/8/" + downFen + " w 0"; | |
241bf8f2 BA |
203 | } |
204 | ||
205 | getCheckSquares() { | |
206 | return []; | |
207 | } | |
208 | ||
209 | updateVariables(move) { | |
210 | super.updateVariables(move); | |
211 | if ( | |
212 | move.vanish.length >= 2 && | |
213 | [V.KING,V.HIDDEN_CODE[V.KING]].includes(move.vanish[1].p) | |
214 | ) { | |
215 | // We took opponent king | |
216 | this.kingPos[this.turn] = [-1, -1]; | |
217 | } | |
218 | } | |
219 | ||
220 | unupdateVariables(move) { | |
221 | super.unupdateVariables(move); | |
222 | const c = move.vanish[0].c; | |
223 | const oppCol = V.GetOppCol(c); | |
224 | if (this.kingPos[oppCol][0] < 0) | |
225 | // Last move took opponent's king: | |
226 | this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y]; | |
227 | } | |
228 | ||
229 | getCurrentScore() { | |
230 | const color = this.turn; | |
231 | const kp = this.kingPos[color]; | |
232 | if (kp[0] < 0) | |
233 | // King disappeared | |
234 | return color == "w" ? "0-1" : "1-0"; | |
235 | // Assume that stalemate is impossible: | |
236 | return "*"; | |
237 | } | |
238 | ||
239 | getComputerMove() { | |
240 | // Just return a random move. TODO: something smarter... | |
241 | const moves = this.getAllValidMoves(); | |
242 | return moves[randInt(moves.length)]; | |
243 | } | |
244 | }; |