Commit | Line | Data |
---|---|---|
32cfcea4 BA |
1 | class UltimaRules extends ChessRules |
2 | { | |
2eef6db6 BA |
3 | static getPpath(b) |
4 | { | |
5 | if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1) | |
6 | return "Ultima/" + b; | |
7 | return b; //usual piece | |
8 | } | |
32cfcea4 | 9 | |
2eef6db6 BA |
10 | initVariables(fen) |
11 | { | |
12 | this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; | |
13 | const fenParts = fen.split(" "); | |
14 | const position = fenParts[0].split("/"); | |
15 | for (let i=0; i<position.length; i++) | |
16 | { | |
17 | let k = 0; | |
18 | for (let j=0; j<position[i].length; j++) | |
19 | { | |
20 | switch (position[i].charAt(j)) | |
21 | { | |
22 | case 'k': | |
23 | this.kingPos['b'] = [i,k]; | |
24 | break; | |
25 | case 'K': | |
26 | this.kingPos['w'] = [i,k]; | |
27 | break; | |
28 | default: | |
29 | let num = parseInt(position[i].charAt(j)); | |
30 | if (!isNaN(num)) | |
31 | k += (num-1); | |
32 | } | |
33 | k++; | |
34 | } | |
35 | } | |
36 | this.epSquares = []; //no en-passant here | |
37 | } | |
38 | ||
39 | setFlags(fen) | |
40 | { | |
41 | // TODO: for compatibility? | |
42 | this.castleFlags = {"w":[false,false], "b":[false,false]}; | |
43 | } | |
44 | ||
45 | static get IMMOBILIZER() { return 'm'; } | |
46 | // Although other pieces keep their names here for coding simplicity, | |
47 | // keep in mind that: | |
48 | // - a "rook" is a coordinator, capturing by coordinating with the king | |
49 | // - a "knight" is a long-leaper, capturing as in draughts | |
50 | // - a "bishop" is a chameleon, capturing as its prey | |
51 | // - a "queen" is a withdrawer, capturing by moving away from pieces | |
52 | ||
53 | getPotentialMovesFrom([x,y]) | |
54 | { | |
55 | switch (this.getPiece(x,y)) | |
56 | { | |
57 | case VariantRules.IMMOBILIZER: | |
58 | return this.getPotentialImmobilizerMoves([x,y]); | |
59 | default: | |
60 | return super.getPotentialMovesFrom([x,y]); | |
61 | } | |
62 | // TODO: add potential suicides as a move "taking the immobilizer" | |
63 | // TODO: add long-leaper captures | |
64 | // TODO: mark matching coordinator/withdrawer/chameleon moves as captures | |
65 | // (will be a bit tedious for chameleons) | |
66 | // --> filter directly in functions below | |
67 | } | |
68 | ||
69 | getSlideNJumpMoves([x,y], steps, oneStep) | |
70 | { | |
71 | const color = this.getColor(x,y); | |
72 | const piece = this.getPiece(x,y); | |
73 | let moves = []; | |
74 | const [sizeX,sizeY] = VariantRules.size; | |
75 | outerLoop: | |
76 | for (let step of steps) | |
77 | { | |
78 | let i = x + step[0]; | |
79 | let j = y + step[1]; | |
80 | while (i>=0 && i<sizeX && j>=0 && j<sizeY | |
81 | && this.board[i][j] == VariantRules.EMPTY) | |
82 | { | |
83 | moves.push(this.getBasicMove([x,y], [i,j])); | |
84 | if (oneStep !== undefined) | |
85 | continue outerLoop; | |
86 | i += step[0]; | |
87 | j += step[1]; | |
88 | } | |
89 | // Only king can take on occupied square: | |
90 | if (piece==VariantRules.KING && i>=0 && i<sizeX && j>=0 | |
91 | && j<sizeY && this.canTake([x,y], [i,j])) | |
92 | { | |
93 | moves.push(this.getBasicMove([x,y], [i,j])); | |
94 | } | |
95 | } | |
96 | return moves; | |
97 | } | |
98 | ||
99 | getPotentialPawnMoves([x,y]) | |
100 | { | |
101 | return super.getPotentialRookMoves([x,y]); | |
102 | } | |
103 | ||
104 | getPotentialRookMoves(sq) | |
105 | { | |
106 | return super.getPotentialQueenMoves(sq); | |
107 | } | |
108 | ||
109 | getPotentialKnightMoves(sq) | |
110 | { | |
111 | return super.getPotentialQueenMoves(sq); | |
112 | } | |
113 | ||
114 | getPotentialBishopMoves(sq) | |
115 | { | |
116 | return super.getPotentialQueenMoves(sq); | |
117 | } | |
118 | ||
119 | getPotentialQueenMoves(sq) | |
120 | { | |
121 | return super.getPotentialQueenMoves(sq); | |
122 | } | |
123 | ||
124 | getPotentialKingMoves(sq) | |
125 | { | |
126 | const V = VariantRules; | |
127 | return this.getSlideNJumpMoves(sq, | |
128 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep"); | |
129 | } | |
130 | ||
131 | // isAttacked() is OK because the immobilizer doesn't take | |
132 | ||
133 | isAttackedByPawn([x,y], colors) | |
134 | { | |
135 | // Square (x,y) must be surrounded by two enemy pieces, | |
136 | // and one of them at least should be a pawn | |
137 | return false; | |
138 | } | |
139 | ||
140 | isAttackedByRook(sq, colors) | |
141 | { | |
142 | // Enemy king must be on same file and a rook on same row (or reverse) | |
143 | } | |
144 | ||
145 | isAttackedByKnight(sq, colors) | |
146 | { | |
147 | // Square (x,y) must be on same line as a knight, | |
148 | // and there must be empty square(s) behind. | |
149 | } | |
150 | ||
151 | isAttackedByBishop(sq, colors) | |
152 | { | |
153 | // switch on piece nature on square sq: a chameleon attack as this piece | |
154 | // ==> call the appropriate isAttackedBy... (exception of immobilizers) | |
155 | // Other exception: a chameleon cannot attack a chameleon (seemingly...) | |
156 | } | |
157 | ||
158 | isAttackedByQueen(sq, colors) | |
159 | { | |
160 | // Square (x,y) must be adjacent to a queen, and the queen must have | |
161 | // some free space in the opposite direction from (x,y) | |
162 | } | |
163 | ||
164 | updateVariables(move) | |
165 | { | |
166 | // Just update king position | |
167 | const piece = this.getPiece(move.start.x,move.start.y); | |
168 | const c = this.getColor(move.start.x,move.start.y); | |
169 | if (piece == VariantRules.KING && move.appear.length > 0) | |
170 | { | |
171 | this.kingPos[c][0] = move.appear[0].x; | |
172 | this.kingPos[c][1] = move.appear[0].y; | |
173 | } | |
174 | } | |
175 | ||
176 | static get VALUES() { //TODO: totally experimental! | |
177 | return { | |
178 | 'p': 1, | |
179 | 'r': 2, | |
180 | 'n': 5, | |
181 | 'b': 3, | |
182 | 'q': 3, | |
183 | 'm': 5, | |
184 | 'k': 1000 | |
185 | }; | |
186 | } | |
187 | ||
188 | static get SEARCH_DEPTH() { return 2; } //TODO? | |
189 | ||
190 | static GenRandInitFen() | |
191 | { | |
192 | let pieces = { "w": new Array(8), "b": new Array(8) }; | |
193 | // Shuffle pieces on first and last rank | |
194 | for (let c of ["w","b"]) | |
195 | { | |
196 | let positions = _.range(8); | |
197 | // Get random squares for every piece, totally freely | |
198 | ||
199 | let randIndex = _.random(7); | |
200 | const bishop1Pos = positions[randIndex]; | |
201 | positions.splice(randIndex, 1); | |
202 | ||
203 | randIndex = _.random(6); | |
204 | const bishop2Pos = positions[randIndex]; | |
205 | positions.splice(randIndex, 1); | |
206 | ||
207 | randIndex = _.random(5); | |
208 | const knight1Pos = positions[randIndex]; | |
209 | positions.splice(randIndex, 1); | |
210 | ||
211 | randIndex = _.random(4); | |
212 | const knight2Pos = positions[randIndex]; | |
213 | positions.splice(randIndex, 1); | |
214 | ||
215 | randIndex = _.random(3); | |
216 | const queenPos = positions[randIndex]; | |
217 | positions.splice(randIndex, 1); | |
218 | ||
219 | randIndex = _.random(2); | |
220 | const kingPos = positions[randIndex]; | |
221 | positions.splice(randIndex, 1); | |
222 | ||
223 | randIndex = _.random(1); | |
224 | const rookPos = positions[randIndex]; | |
225 | positions.splice(randIndex, 1); | |
226 | const immobilizerPos = positions[2]; | |
227 | ||
228 | pieces[c][bishop1Pos] = 'b'; | |
229 | pieces[c][bishop2Pos] = 'b'; | |
230 | pieces[c][knight1Pos] = 'n'; | |
231 | pieces[c][knight2Pos] = 'n'; | |
232 | pieces[c][queenPos] = 'q'; | |
233 | pieces[c][kingPos] = 'k'; | |
234 | pieces[c][rookPos] = 'r'; | |
235 | pieces[c][immobilizerPos] = 'm'; | |
236 | } | |
237 | return pieces["b"].join("") + | |
238 | "/pppppppp/8/8/8/8/PPPPPPPP/" + | |
239 | pieces["w"].join("").toUpperCase() + | |
240 | " 0000"; //TODO: flags?! | |
241 | } | |
242 | ||
243 | getFlagsFen() | |
244 | { | |
245 | return "0000"; //TODO: or "-" ? | |
246 | } | |
32cfcea4 | 247 | } |