Some fixes. Screen variant computer play is still broken, seemingly
[vchess.git] / client / src / variants / Screen.js
1 import { ChessRules, Move, PiPo } from "@/base_rules";
2 import { randInt } from "@/utils/alea";
3 import { ArrayFun } from "@/utils/array";
4
5 export class ScreenRules extends ChessRules {
6
7 static get HasFlags() {
8 return false;
9 }
10
11 static get HasEnpassant() {
12 return false;
13 }
14
15 get canAnalyze() {
16 return this.movesCount >= 2;
17 }
18
19 get someHiddenMoves() {
20 return this.movesCount <= 1;
21 }
22
23 static GenRandInitFen() {
24 // Empty board
25 return "8/8/8/8/8/8/8/8 w 0";
26 }
27
28 re_setReserve(subTurn) {
29 const mc = this.movesCount;
30 const wc = (mc == 0 ? 1 : 0);
31 const bc = (mc <= 1 ? 1 : 0);
32 this.reserve = {
33 w: {
34 [V.PAWN]: wc * 8,
35 [V.ROOK]: wc * 2,
36 [V.KNIGHT]: wc * 2,
37 [V.BISHOP]: wc * 2,
38 [V.QUEEN]: wc,
39 [V.KING]: wc
40 },
41 b: {
42 [V.PAWN]: bc * 8,
43 [V.ROOK]: bc * 2,
44 [V.KNIGHT]: bc * 2,
45 [V.BISHOP]: bc * 2,
46 [V.QUEEN]: bc,
47 [V.KING]: bc
48 }
49 }
50 this.subTurn = subTurn || 1;
51 }
52
53 re_setEnlightened(onOff) {
54 if (!onOff) delete this["enlightened"];
55 else {
56 // Turn on:
57 this.enlightened = {
58 'w': ArrayFun.init(8, 8, false),
59 'b': ArrayFun.init(8, 8, false)
60 };
61 for (let i=0; i<4; i++) {
62 for (let j=0; j<8; j++) this.enlightened['b'][i][j] = true;
63 }
64 for (let i=4; i<8; i++) {
65 for (let j=0; j<8; j++) this.enlightened['w'][i][j] = true;
66 }
67 }
68 }
69
70 setOtherVariables(fen) {
71 super.setOtherVariables(fen);
72 if (this.movesCount <= 1) {
73 this.re_setReserve();
74 this.re_setEnlightened(true);
75 }
76 }
77
78 getColor(i, j) {
79 if (i >= V.size.x) return i == V.size.x ? "w" : "b";
80 return this.board[i][j].charAt(0);
81 }
82
83 getPiece(i, j) {
84 if (i >= V.size.x) return V.RESERVE_PIECES[j];
85 return this.board[i][j].charAt(1);
86 }
87
88 getReservePpath(index, color) {
89 return color + V.RESERVE_PIECES[index];
90 }
91
92 static get RESERVE_PIECES() {
93 return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING];
94 }
95
96 getPotentialMovesFrom([x, y]) {
97 if (this.movesCount >= 2) return super.getPotentialMovesFrom([x, y]);
98 // Only reserve moves are allowed for now:
99 if (V.OnBoard(x, y)) return [];
100 const color = this.turn;
101 const p = V.RESERVE_PIECES[y];
102 if (this.reserve[color][p] == 0) return [];
103 const shift = (p == V.PAWN ? 1 : 0);
104 let iBound = (color == 'w' ? [4, 7 - shift] : [shift, 3]);
105 let moves = [];
106
107 // Pawns cannot stack on files, one bishop per color
108 let forbiddenFiles = [];
109 if (p == V.PAWN) {
110 const colorShift = (color == 'w' ? 4 : 1);
111 forbiddenFiles =
112 ArrayFun.range(8).filter(jj => {
113 return ArrayFun.range(3).some(ii => {
114 return (
115 this.board[colorShift + ii][jj] != V.EMPTY &&
116 this.getPiece(colorShift + ii, jj) == V.PAWN
117 );
118 })
119 });
120 }
121 let forbiddenColor = -1;
122 if (p == V.BISHOP) {
123 const colorShift = (color == 'w' ? 4 : 0);
124 outerLoop: for (let ii = colorShift; ii < colorShift + 4; ii++) {
125 for (let jj = 0; jj < 8; jj++) {
126 if (
127 this.board[ii][jj] != V.EMPTY &&
128 this.getPiece(ii, jj) == V.BISHOP
129 ) {
130 forbiddenColor = (ii + jj) % 2;
131 break outerLoop;
132 }
133 }
134 }
135 }
136
137 for (let i = iBound[0]; i <= iBound[1]; i++) {
138 for (let j = 0; j < 8; j++) {
139 if (
140 this.board[i][j] == V.EMPTY &&
141 (p != V.PAWN || !forbiddenFiles.includes(j)) &&
142 (p != V.BISHOP || (i + j) % 2 != forbiddenColor)
143 ) {
144 // Ok, move is valid:
145 let mv = new Move({
146 appear: [
147 new PiPo({
148 x: i,
149 y: j,
150 c: color,
151 p: p
152 })
153 ],
154 vanish: [],
155 start: { x: x, y: y },
156 end: { x: i, y: j }
157 });
158 moves.push(mv);
159 }
160 }
161 }
162 moves.forEach(m => { m.end.noHighlight = true; });
163 return moves;
164 }
165
166 underCheck(color) {
167 if (this.movesCount <= 1) return false;
168 return super.underCheck(color);
169 }
170
171 getAllValidMoves() {
172 if (this.movesCount >= 2) return super.getAllValidMoves();
173 const color = this.turn;
174 let moves = [];
175 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
176 moves = moves.concat(
177 this.getPotentialMovesFrom([V.size.x + (color == "w" ? 0 : 1), i])
178 );
179 }
180 return this.filterValid(moves);
181 }
182
183 play(move) {
184 const color = move.appear[0].c;
185 if (this.movesCount <= 1) {
186 V.PlayOnBoard(this.board, move);
187 const piece = move.appear[0].p;
188 this.reserve[color][piece]--;
189 if (piece == V.KING) this.kingPos[color] = [move.end.x, move.end.y];
190 if (this.subTurn == 16) {
191 // All placement moves are done
192 this.movesCount++;
193 this.turn = V.GetOppCol(color);
194 if (this.movesCount == 1) this.subTurn = 1;
195 else {
196 // Initial placement is over
197 delete this["reserve"];
198 delete this["subTurn"];
199 }
200 }
201 else this.subTurn++;
202 }
203 else {
204 if (this.movesCount == 2) this.re_setEnlightened(false);
205 super.play(move);
206 }
207 }
208
209 undo(move) {
210 const color = move.appear[0].c;
211 if (this.movesCount <= 2) {
212 V.UndoOnBoard(this.board, move);
213 const piece = move.appear[0].p;
214 if (piece == V.KING) this.kingPos[color] = [-1, -1];
215 if (!this.subTurn || this.subTurn == 1) {
216 // All placement moves are undone (if any)
217 if (!this.subTurn) this.re_setReserve(16);
218 else this.subTurn = 16;
219 this.movesCount--;
220 if (this.movesCount == 1) this.re_setEnlightened(true);
221 this.turn = color;
222 }
223 else this.subTurn--;
224 this.reserve[color][piece]++;
225 }
226 else super.undo(move);
227 }
228
229 getCheckSquares() {
230 if (this.movesCount <= 1) return [];
231 return super.getCheckSquares();
232 }
233
234 getCurrentScore() {
235 if (this.movesCount <= 1) return "*";
236 return super.getCurrentScore();
237 }
238
239 getComputerMove() {
240 if (this.movesCount >= 2) return super.getComputerMove();
241 // Play a random "initialization move"
242 let res = [];
243 for (let i=0; i<16; i++) {
244 const moves = this.getAllValidMoves();
245 const moveIdx = randInt(moves.length);
246 this.play(moves[moveIdx]);
247 res.push(moves[moveIdx]);
248 }
249 for (let i=15; i>=0; i--) this.undo(res[i]);
250 return res;
251 }
252
253 getNotation(move) {
254 // Do not note placement moves (complete move would be too long)
255 if (move.vanish.length == 0) return "";
256 return super.getNotation(move);
257 }
258
259 };