Commit | Line | Data |
---|---|---|
bb688df5 BA |
1 | import { ChessRules } from "@/base_rules"; |
2 | import { ArrayFun } from "@/utils/array"; | |
3 | import { randInt, sample } from "@/utils/alea"; | |
4 | ||
5 | export class CoregalRules extends ChessRules { | |
6 | static IsGoodPosition(position) { | |
7 | if (!super.IsGoodPosition(position)) return false; | |
8 | // Check that at least one queen of each color is there: | |
9 | let queens = {}; | |
10 | for (let row of rows) { | |
11 | for (let i = 0; i < row.length; i++) | |
12 | if (['Q','q'].includes(row[i])) queens[row[i]] = true; | |
13 | } | |
14 | if (Object.keys(queens).length != 2) return false; | |
15 | return true; | |
16 | } | |
17 | ||
18 | static IsGoodFlags(flags) { | |
19 | return !!flags.match(/^[a-z]{8,8}$/); | |
20 | } | |
21 | ||
22 | getCheckSquares(color) { | |
23 | let squares = []; | |
24 | const oppCol = V.GetOppCol(color); | |
25 | if (this.isAttacked(this.kingPos[color], oppCol)) | |
26 | squares.push(this.kingPos[color]); | |
27 | for (let i=0; i<V.size.x; i++) { | |
28 | for (let j=0; j<V.size.y; j++) { | |
29 | if ( | |
30 | this.getColor(i, j) == color && | |
31 | this.getPiece(i, j) == V.QUEEN && | |
32 | this.isAttacked([i, j], oppCol) | |
33 | ) { | |
34 | squares.push([i, j]); | |
35 | } | |
36 | } | |
37 | } | |
38 | return squares; | |
39 | } | |
40 | ||
41 | static GenRandInitFen(randomness) { | |
42 | if (randomness == 0) | |
43 | // Castle flags here indicate pieces positions (if can castle) | |
44 | return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 adehadeh -"; | |
45 | ||
46 | let pieces = { w: new Array(8), b: new Array(8) }; | |
47 | let flags = ""; | |
48 | for (let c of ["w", "b"]) { | |
49 | if (c == 'b' && randomness == 1) { | |
50 | pieces['b'] = pieces['w']; | |
51 | flags += flags; | |
52 | break; | |
53 | } | |
54 | ||
55 | // Get random squares for king and queen between b and g files | |
56 | let randIndex = randInt(6); | |
57 | let kingPos = randIndex + 1; | |
58 | randIndex = randInt(5); | |
59 | if (randIndex >= kingPos) randIndex++; | |
60 | let queenPos = randIndex + 1; | |
61 | ||
62 | // Get random squares for rooks to the left and right of the queen | |
63 | // and king: not all squares of the same colors (for bishops). | |
64 | const minQR = Math.min(kingPos, queenPos); | |
65 | const maxQR = Math.max(kingPos, queenPos); | |
66 | let rook1Pos = randInt(minQR); | |
67 | let rook2Pos = 7 - randInt(7 - maxQR); | |
68 | ||
69 | // Now, if we are unlucky all these 4 pieces may be on the same color. | |
70 | const rem2 = [kingPos, queenPos, rook1Pos, rook2Pos].map(pos => pos % 2); | |
71 | if (rem2.every(r => r == 0) || rem2.every(r => r == 1)) { | |
72 | // Shift a random of these pieces to the left or right | |
73 | switch (randInt(4)) { | |
74 | case 0: | |
75 | if (rook1Pos == 0) rook1Pos++; | |
76 | else rook1Pos--; | |
77 | break; | |
78 | case 1: | |
79 | if (Math.random() < 0.5) kingPos++; | |
80 | else kingPos--; | |
81 | break; | |
82 | case 2: | |
83 | if (Math.random() < 0.5) queenPos++; | |
84 | else queenPos--; | |
85 | break; | |
86 | case 3: | |
87 | if (rook2Pos == 7) rook2Pos--; | |
88 | else rook2Pos++; | |
89 | break; | |
90 | } | |
91 | } | |
92 | let bishop1Options = { 0: true, 2: true, 4: true, 6: true }; | |
93 | let bishop2Options = { 1: true, 3: true, 5: true, 7: true }; | |
94 | [kingPos, queenPos, rook1Pos, rook2Pos].forEach(pos => { | |
95 | if (!!bishop1Options[pos]) delete bishop1Options[pos]; | |
96 | else if (!!bishop2Options[pos]) delete bishop2Options[pos]; | |
97 | }); | |
98 | const bishop1Pos = parseInt(sample(Object.keys(bishop1Options), 1)[0]); | |
99 | const bishop2Pos = parseInt(sample(Object.keys(bishop2Options), 1)[0]); | |
100 | ||
101 | // Knights' positions are now determined | |
102 | const forbidden = [ | |
103 | kingPos, queenPos, rook1Pos, rook2Pos, bishop1Pos, bishop2Pos | |
104 | ]; | |
105 | const [knight1Pos, knight2Pos] = | |
106 | ArrayFun.range(8).filter(pos => !forbidden.includes(pos)); | |
107 | ||
108 | pieces[c][rook1Pos] = "r"; | |
109 | pieces[c][knight1Pos] = "n"; | |
110 | pieces[c][bishop1Pos] = "b"; | |
111 | pieces[c][queenPos] = "q"; | |
112 | pieces[c][kingPos] = "k"; | |
113 | pieces[c][bishop2Pos] = "b"; | |
114 | pieces[c][knight2Pos] = "n"; | |
115 | pieces[c][rook2Pos] = "r"; | |
116 | flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(queenPos) + | |
117 | V.CoordToColumn(kingPos) + V.CoordToColumn(rook2Pos); | |
118 | } | |
119 | // Add turn + flags + enpassant | |
120 | return ( | |
121 | pieces["b"].join("") + | |
122 | "/pppppppp/8/8/8/8/PPPPPPPP/" + | |
123 | pieces["w"].join("").toUpperCase() + | |
124 | " w 0 " + flags + " -" | |
125 | ); | |
126 | } | |
127 | ||
128 | setFlags(fenflags) { | |
129 | // white a-castle, h-castle, black a-castle, h-castle | |
130 | this.castleFlags = { w: [...Array(4)], b: [...Array(4)] }; | |
131 | for (let i = 0; i < 8; i++) { | |
132 | this.castleFlags[i < 4 ? "w" : "b"][i % 4] = | |
133 | V.ColumnToCoord(fenflags.charAt(i)); | |
134 | } | |
135 | } | |
136 | ||
137 | getPotentialQueenMoves(sq) { | |
138 | return super.getPotentialQueenMoves(sq).concat(this.getCastleMoves(sq)); | |
139 | } | |
140 | ||
141 | getCastleMoves([x, y], castleInCheck) { | |
142 | return []; | |
143 | // const c = this.getColor(x, y); | |
144 | // if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c]) | |
145 | // return []; //x isn't first rank, or king has moved (shortcut) | |
146 | // | |
147 | // // Castling ? | |
148 | // const oppCol = V.GetOppCol(c); | |
149 | // let moves = []; | |
150 | // let i = 0; | |
151 | // // King, then rook: | |
152 | // const finalSquares = [ | |
153 | // [2, 3], | |
154 | // [V.size.y - 2, V.size.y - 3] | |
155 | // ]; | |
156 | // castlingCheck: for ( | |
157 | // let castleSide = 0; | |
158 | // castleSide < 2; | |
159 | // castleSide++ //large, then small | |
160 | // ) { | |
161 | // if (this.castleFlags[c][castleSide] >= V.size.y) continue; | |
162 | // // If this code is reached, rooks and king are on initial position | |
163 | // | |
164 | // // NOTE: in some variants this is not a rook, but let's keep variable name | |
165 | // const rookPos = this.castleFlags[c][castleSide]; | |
166 | // const castlingPiece = this.getPiece(x, rookPos); | |
167 | // if (this.getColor(x, rookPos) != c) | |
168 | // // Rook is here but changed color (see Benedict) | |
169 | // continue; | |
170 | // | |
171 | // // Nothing on the path of the king ? (and no checks) | |
172 | // const finDist = finalSquares[castleSide][0] - y; | |
173 | // let step = finDist / Math.max(1, Math.abs(finDist)); | |
174 | // i = y; | |
175 | // do { | |
176 | // if ( | |
177 | // (!castleInCheck && this.isAttacked([x, i], oppCol)) || | |
178 | // (this.board[x][i] != V.EMPTY && | |
179 | // // NOTE: next check is enough, because of chessboard constraints | |
180 | // (this.getColor(x, i) != c || | |
181 | // ![V.KING, castlingPiece].includes(this.getPiece(x, i)))) | |
182 | // ) { | |
183 | // continue castlingCheck; | |
184 | // } | |
185 | // i += step; | |
186 | // } while (i != finalSquares[castleSide][0]); | |
187 | // | |
188 | // // Nothing on the path to the rook? | |
189 | // step = castleSide == 0 ? -1 : 1; | |
190 | // for (i = y + step; i != rookPos; i += step) { | |
191 | // if (this.board[x][i] != V.EMPTY) continue castlingCheck; | |
192 | // } | |
193 | // | |
194 | // // Nothing on final squares, except maybe king and castling rook? | |
195 | // for (i = 0; i < 2; i++) { | |
196 | // if ( | |
197 | // this.board[x][finalSquares[castleSide][i]] != V.EMPTY && | |
198 | // this.getPiece(x, finalSquares[castleSide][i]) != V.KING && | |
199 | // finalSquares[castleSide][i] != rookPos | |
200 | // ) { | |
201 | // continue castlingCheck; | |
202 | // } | |
203 | // } | |
204 | // | |
205 | // // If this code is reached, castle is valid | |
206 | // moves.push( | |
207 | // new Move({ | |
208 | // appear: [ | |
209 | // new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }), | |
210 | // new PiPo({ x: x, y: finalSquares[castleSide][1], p: castlingPiece, c: c }) | |
211 | // ], | |
212 | // vanish: [ | |
213 | // new PiPo({ x: x, y: y, p: V.KING, c: c }), | |
214 | // new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c }) | |
215 | // ], | |
216 | // end: | |
217 | // Math.abs(y - rookPos) <= 2 | |
218 | // ? { x: x, y: rookPos } | |
219 | // : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) } | |
220 | // }) | |
221 | // ); | |
222 | // } | |
223 | // | |
224 | // return moves; | |
225 | } | |
226 | ||
227 | underCheck(color) { | |
228 | const oppCol = V.GetOppCol(color); | |
229 | if (this.isAttacked(this.kingPos[color], oppCol)) return true; | |
230 | for (let i=0; i<V.size.x; i++) { | |
231 | for (let j=0; j<V.size.y; j++) { | |
232 | if ( | |
233 | this.getColor(i, j) == color && | |
234 | this.getPiece(i, j) == V.QUEEN && | |
235 | this.isAttacked([i, j], oppCol) | |
236 | ) { | |
237 | return true; | |
238 | } | |
239 | } | |
240 | } | |
241 | return false; | |
242 | } | |
243 | ||
244 | updateCastleFlags(move, piece) { | |
245 | // const c = V.GetOppCol(this.turn); | |
246 | // const firstRank = (c == "w" ? V.size.x - 1 : 0); | |
247 | // // Update castling flags if rooks are moved | |
248 | // const oppCol = V.GetOppCol(c); | |
249 | // const oppFirstRank = V.size.x - 1 - firstRank; | |
250 | // if (piece == V.KING && move.appear.length > 0) | |
251 | // this.castleFlags[c] = [V.size.y, V.size.y]; | |
252 | // else if ( | |
253 | // move.start.x == firstRank && //our rook moves? | |
254 | // this.castleFlags[c].includes(move.start.y) | |
255 | // ) { | |
256 | // const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); | |
257 | // this.castleFlags[c][flagIdx] = V.size.y; | |
258 | // } else if ( | |
259 | // move.end.x == oppFirstRank && //we took opponent rook? | |
260 | // this.castleFlags[oppCol].includes(move.end.y) | |
261 | // ) { | |
262 | // const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1); | |
263 | // this.castleFlags[oppCol][flagIdx] = V.size.y; | |
264 | // } | |
265 | } | |
266 | ||
267 | // NOTE: do not set queen value to 1000 or so, because there may be several. | |
268 | }; |