Commit | Line | Data |
---|---|---|
ecd7d190 BA |
1 | import { ChessRules, PiPo, Move } from "@/base_rules"; |
2 | import { ArrayFun } from "@/utils/array"; | |
3 | ||
4 | export default class ClorangeRules extends ChessRules { | |
5 | ||
6 | get hasReserve() { | |
7 | return true; | |
8 | } | |
9 | ||
10 | // TODO | |
11 | ||
12 | static GenRandInitFen(options) { | |
13 | // Capturing and non-capturing reserves: | |
14 | return ChessRules.GenRandInitFen(options) + " 00000000000000000000"; | |
15 | } | |
16 | ||
17 | getFen() { | |
18 | return super.getFen() + " " + this.getReserveFen(); | |
19 | } | |
20 | ||
21 | getFenForRepeat() { | |
22 | return super.getFenForRepeat() + "_" + this.getReserveFen(); | |
23 | } | |
24 | ||
25 | getReserveFen() { | |
26 | return ( | |
27 | Object.keys(this.reserve).map( | |
28 | c => Object.values(this.reserve[c]).join("")).join("") | |
29 | ); | |
30 | } | |
31 | ||
32 | getEpSquare(moveOrSquare) { | |
33 | if (!moveOrSquare) return undefined; | |
34 | if (typeof moveOrSquare === "string") { | |
35 | const square = moveOrSquare; | |
36 | if (square == "-") return undefined; | |
37 | return V.SquareToCoords(square); | |
38 | } | |
39 | const move = moveOrSquare; | |
40 | const s = move.start, | |
41 | e = move.end; | |
42 | if ( | |
43 | s.y == e.y && | |
44 | Math.abs(s.x - e.x) == 2 && | |
45 | move.vanish.length > 0 && ['p', 's'].includes(move.vanish[0].p) | |
46 | ) { | |
47 | return { | |
48 | x: (s.x + e.x) / 2, | |
49 | y: s.y | |
50 | }; | |
51 | } | |
52 | return undefined; | |
53 | } | |
54 | ||
55 | setOtherVariables(fen) { | |
56 | super.setOtherVariables(fen); | |
57 | // Also init reserves (used by the interface to show landable pieces) | |
58 | const reserve = | |
59 | V.ParseFen(fen).reserve.split("").map(x => parseInt(x, 10)); | |
60 | this.reserve = { | |
61 | w: { | |
62 | 'p': reserve[0], | |
63 | 'r': reserve[1], | |
64 | 'n': reserve[2], | |
65 | 'b': reserve[3], | |
66 | 'q': reserve[4], | |
67 | 's': reserve[5], | |
68 | 'u': reserve[6], | |
69 | 'o': reserve[7], | |
70 | 'c': reserve[8], | |
71 | 't': reserve[9] | |
72 | }, | |
73 | b: { | |
74 | 'p': reserve[10], | |
75 | 'r': reserve[11], | |
76 | 'n': reserve[12], | |
77 | 'b': reserve[13], | |
78 | 'q': reserve[14], | |
79 | 's': reserve[15], | |
80 | 'u': reserve[16], | |
81 | 'o': reserve[17], | |
82 | 'c': reserve[18], | |
83 | 't': reserve[19] | |
84 | } | |
85 | }; | |
86 | } | |
87 | ||
88 | getColor(i, j) { | |
89 | if (i >= V.size.x) return i == V.size.x ? "w" : "b"; | |
90 | return this.board[i][j].charAt(0); | |
91 | } | |
92 | ||
93 | getPiece(i, j) { | |
94 | if (i >= V.size.x) return V.RESERVE_PIECES[j]; | |
95 | return this.board[i][j].charAt(1); | |
96 | } | |
97 | ||
98 | getPpath(b) { | |
99 | return (V.NON_VIOLENT.includes(b[1]) ? "Clorange/" : "") + b; | |
100 | } | |
101 | ||
102 | getReservePpath(index, color) { | |
103 | const prefix = | |
104 | (V.NON_VIOLENT.includes(V.RESERVE_PIECES[index]) ? "Clorange/" : ""); | |
105 | return prefix + color + V.RESERVE_PIECES[index]; | |
106 | } | |
107 | ||
108 | static get NON_VIOLENT() { | |
109 | return ['s', 'u', 'o', 'c', 't']; | |
110 | } | |
111 | ||
112 | static get PIECES() { | |
113 | return ChessRules.PIECES.concat(V.NON_VIOLENT); | |
114 | } | |
115 | ||
116 | // Ordering on reserve pieces | |
117 | static get RESERVE_PIECES() { | |
118 | return V.PIECES.filter(p => p != 'k'); | |
119 | } | |
120 | ||
121 | getReserveMoves([x, y]) { | |
122 | const color = this.turn; | |
123 | const p = V.RESERVE_PIECES[y]; | |
124 | if (this.reserve[color][p] == 0) return []; | |
125 | let moves = []; | |
126 | let rank1 = 0; | |
127 | let rank2 = V.size.x - 1; | |
128 | if (['p', 's'].includes(p)) { | |
129 | if (color == 'w') rank1++; | |
130 | else rank2--; | |
131 | } | |
132 | for (let i = rank1; i <= rank2; i++) { | |
133 | for (let j = 0; j < V.size.y; j++) { | |
134 | if (this.board[i][j] == V.EMPTY) { | |
135 | let mv = new Move({ | |
136 | appear: [ | |
137 | new PiPo({ | |
138 | x: i, | |
139 | y: j, | |
140 | c: color, | |
141 | p: p | |
142 | }) | |
143 | ], | |
144 | vanish: [], | |
145 | start: { x: x, y: y }, //a bit artificial... | |
146 | end: { x: i, y: j } | |
147 | }); | |
148 | moves.push(mv); | |
149 | } | |
150 | } | |
151 | } | |
152 | return moves; | |
153 | } | |
154 | ||
155 | getPotentialMovesFrom([x, y]) { | |
156 | if (x >= V.size.x) | |
157 | // Reserves, outside of board: x == sizeX(+1) | |
158 | return this.getReserveMoves([x, y]); | |
159 | // Standard moves | |
160 | switch (this.getPiece(x, y)) { | |
161 | case 's': return this.getPotentialPawnMoves([x, y]); | |
162 | case 'u': return super.getPotentialRookMoves([x, y]); | |
163 | case 'o': return super.getPotentialKnightMoves([x, y]); | |
164 | case 'c': return super.getPotentialBishopMoves([x, y]); | |
165 | case 't': return super.getPotentialQueenMoves([x, y]); | |
166 | default: return super.getPotentialMovesFrom([x, y]); | |
167 | } | |
168 | return []; //never reached | |
169 | } | |
170 | ||
171 | getPotentialPawnMoves(sq) { | |
172 | let moves = super.getPotentialPawnMoves(sq); | |
173 | if (moves.length > 0 && moves[0].vanish[0].p == 's') { | |
174 | // Remove captures for non-violent pawns: | |
175 | moves = moves.filter(m => m.vanish.length == 1); | |
176 | moves.forEach(m => { | |
177 | if (m.appear[0].p != 's') { | |
178 | // Promotion pieces should be non-violent as well: | |
179 | const pIdx = ChessRules.PIECES.findIndex(p => p == m.appear[0].p) | |
180 | m.appear[0].p = V.NON_VIOLENT[pIdx]; | |
181 | } | |
182 | }); | |
183 | } | |
184 | return moves; | |
185 | } | |
186 | ||
187 | canTake([x1, y1], [x2, y2]) { | |
188 | return ( | |
189 | this.getColor(x1, y1) !== this.getColor(x2, y2) && | |
190 | ChessRules.PIECES.includes(this.getPiece(x1, y1)) | |
191 | ); | |
192 | } | |
193 | ||
194 | getAllValidMoves() { | |
195 | let moves = super.getAllPotentialMoves(); | |
196 | const color = this.turn; | |
197 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
198 | moves = moves.concat( | |
199 | this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i]) | |
200 | ); | |
201 | } | |
202 | return this.filterValid(moves); | |
203 | } | |
204 | ||
205 | atLeastOneMove() { | |
206 | if (!super.atLeastOneMove()) { | |
207 | // Search one reserve move | |
208 | for (let i = 0; i < V.RESERVE_PIECES.length; i++) { | |
209 | let moves = this.filterValid( | |
210 | this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i]) | |
211 | ); | |
212 | if (moves.length > 0) return true; | |
213 | } | |
214 | return false; | |
215 | } | |
216 | return true; | |
217 | } | |
218 | ||
219 | prePlay(move) { | |
220 | super.prePlay(move); | |
221 | // Skip castle: | |
222 | if (move.vanish.length == 2 && move.appear.length == 2) return; | |
223 | const color = this.turn; | |
224 | if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]--; | |
225 | else if (move.vanish.length == 2) { | |
226 | // Capture | |
227 | const normal = ChessRules.PIECES.includes(move.vanish[1].p); | |
228 | const pIdx = | |
229 | normal | |
230 | ? ChessRules.PIECES.findIndex(p => p == move.vanish[1].p) | |
231 | : V.NON_VIOLENT.findIndex(p => p == move.vanish[1].p); | |
232 | const rPiece = (normal ? V.NON_VIOLENT : ChessRules.PIECES)[pIdx]; | |
233 | this.reserve[move.vanish[1].c][rPiece]++; | |
234 | } | |
235 | } | |
236 | ||
237 | }; |