Several small improvements + integrate options + first working draft of Cwda
[vchess.git] / client / src / variants / Circular.js
CommitLineData
e3e2cc44
BA
1import { ChessRules, PiPo, Move } from "@/base_rules";
2import { ArrayFun } from "@/utils/array";
32f6285e 3import { shuffle } from "@/utils/alea";
e3e2cc44 4
32f6285e 5export class CircularRules extends ChessRules {
7e8a7ea1 6
3a2a7b5f
BA
7 static get HasCastle() {
8 return false;
9 }
10
71ef1664 11 static get HasEnpassant() {
e3e2cc44
BA
12 return false;
13 }
14
71ef1664 15 static get CanFlip() {
e3e2cc44
BA
16 return false;
17 }
18
71ef1664
BA
19 setFlags(fenflags) {
20 this.pawnFlags = {
21 w: [...Array(8).fill(true)], //pawns can move 2 squares?
22 b: [...Array(8).fill(true)]
23 };
24 for (let c of ["w", "b"]) {
25 for (let i = 0; i < 8; i++)
26 this.pawnFlags[c][i] = fenflags.charAt((c == "w" ? 0 : 8) + i) == "1";
27 }
28 }
29
30 aggregateFlags() {
31 return this.pawnFlags;
32 }
33
34 disaggregateFlags(flags) {
35 this.pawnFlags = flags;
36 }
e3e2cc44 37
4313762d
BA
38 static GenRandInitFen(options) {
39 if (options.randomness == 0) {
2c5d7b20
BA
40 return "8/8/pppppppp/rnbqkbnr/8/8/PPPPPPPP/RNBQKBNR " +
41 "w 0 1111111111111111";
42 }
7ba4a5bc 43
e3e2cc44 44 let pieces = { w: new Array(8), b: new Array(8) };
32f6285e 45 // Shuffle pieces on first and last rank
e3e2cc44 46 for (let c of ["w", "b"]) {
4313762d 47 if (c == 'b' && options.randomness == 1) {
7ba4a5bc
BA
48 pieces['b'] = pieces['w'];
49 break;
50 }
51
32f6285e
BA
52 // Get random squares for every piece, totally freely
53 let positions = shuffle(ArrayFun.range(8));
54 const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
55 const rem2 = positions[0] % 2;
56 if (rem2 == positions[1] % 2) {
57 // Fix bishops (on different colors)
58 for (let i=2; i<8; i++) {
5d74fcea 59 if (positions[i] % 2 != rem2) {
32f6285e 60 [positions[1], positions[i]] = [positions[i], positions[1]];
5d74fcea
BA
61 break;
62 }
32f6285e
BA
63 }
64 }
65 for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i];
e3e2cc44
BA
66 }
67 return (
71ef1664 68 "8/8/pppppppp/" +
e3e2cc44 69 pieces["b"].join("") +
71ef1664 70 "/8/8/PPPPPPPP/" +
e3e2cc44 71 pieces["w"].join("").toUpperCase() +
71ef1664
BA
72 // 16 flags: can pawns advance 2 squares?
73 " w 0 1111111111111111"
e3e2cc44
BA
74 );
75 }
76
71ef1664
BA
77 // Output basically x % 8 (circular board)
78 static ComputeX(x) {
79 let res = x % V.size.x;
80 if (res < 0)
81 res += V.size.x;
82 return res;
83 }
84
e3e2cc44
BA
85 getSlideNJumpMoves([x, y], steps, oneStep) {
86 let moves = [];
a8f0bbcb
BA
87 // Don't add move twice when running on an infinite file:
88 let infiniteSteps = {};
e3e2cc44 89 outerLoop: for (let step of steps) {
a8f0bbcb 90 if (!!infiniteSteps[(-step[0]) + "." + (-step[1])]) continue;
71ef1664 91 let i = V.ComputeX(x + step[0]);
e3e2cc44
BA
92 let j = y + step[1];
93 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
94 moves.push(this.getBasicMove([x, y], [i, j]));
4313762d 95 if (oneStep) continue outerLoop;
71ef1664 96 i = V.ComputeX(i + step[0]);
e3e2cc44
BA
97 j += step[1];
98 }
a8f0bbcb
BA
99 if (V.OnBoard(i, j)) {
100 if (i == x && j == y)
101 // Looped back onto initial square
102 infiniteSteps[step[0] + "." + step[1]] = true;
103 else if (this.canTake([x, y], [i, j]))
104 moves.push(this.getBasicMove([x, y], [i, j]));
105 }
e3e2cc44
BA
106 }
107 return moves;
108 }
109
e3e2cc44
BA
110 getPotentialPawnMoves([x, y]) {
111 const color = this.turn;
112 let moves = [];
113 const [sizeX, sizeY] = [V.size.x, V.size.y];
71ef1664
BA
114 // All pawns go in the same direction!
115 const shiftX = -1;
116 const startRank = color == "w" ? sizeX - 2 : 2;
117
118 // One square forward
119 const nextRow = V.ComputeX(x + shiftX);
120 if (this.board[nextRow][y] == V.EMPTY) {
121 moves.push(this.getBasicMove([x, y], [nextRow, y]));
122 if (
123 x == startRank &&
124 this.pawnFlags[color][y] &&
125 this.board[x + 2 * shiftX][y] == V.EMPTY
126 ) {
127 // Two squares jump
128 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
e3e2cc44 129 }
71ef1664
BA
130 }
131 // Captures
132 for (let shiftY of [-1, 1]) {
133 if (
134 y + shiftY >= 0 &&
135 y + shiftY < sizeY &&
136 this.board[nextRow][y + shiftY] != V.EMPTY &&
137 this.canTake([x, y], [nextRow, y + shiftY])
138 ) {
139 moves.push(this.getBasicMove([x, y], [nextRow, y + shiftY]));
e3e2cc44
BA
140 }
141 }
142
143 return moves;
144 }
145
71ef1664
BA
146 filterValid(moves) {
147 const filteredMoves = super.filterValid(moves);
148 // If at least one full move made, everything is allowed:
9842aca2 149 if (this.movesCount >= 2) return filteredMoves;
71ef1664
BA
150 // Else, forbid check:
151 const oppCol = V.GetOppCol(this.turn);
152 return filteredMoves.filter(m => {
153 this.play(m);
154 const res = !this.underCheck(oppCol);
155 this.undo(m);
156 return res;
157 });
158 }
159
68e19a44
BA
160 isAttackedByPawn([x, y], color) {
161 // pawn shift is always 1 (all pawns go the same way)
162 const attackerRow = V.ComputeX(x + 1);
163 for (let i of [-1, 1]) {
164 if (
165 y + i >= 0 &&
166 y + i < V.size.y &&
167 this.getPiece(attackerRow, y + i) == V.PAWN &&
168 this.getColor(attackerRow, y + i) == color
169 ) {
170 return true;
e3e2cc44
BA
171 }
172 }
173 return false;
174 }
175
68e19a44 176 isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) {
e3e2cc44 177 for (let step of steps) {
71ef1664 178 let rx = V.ComputeX(x + step[0]),
e3e2cc44
BA
179 ry = y + step[1];
180 while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) {
71ef1664 181 rx = V.ComputeX(rx + step[0]);
e3e2cc44
BA
182 ry += step[1];
183 }
184 if (
185 V.OnBoard(rx, ry) &&
68e19a44
BA
186 this.getPiece(rx, ry) == piece &&
187 this.getColor(rx, ry) == color
e3e2cc44
BA
188 ) {
189 return true;
190 }
191 }
192 return false;
193 }
71ef1664
BA
194
195 getFlagsFen() {
196 // Return pawns flags
197 let flags = "";
198 for (let c of ["w", "b"]) {
199 for (let i = 0; i < 8; i++) flags += this.pawnFlags[c][i] ? "1" : "0";
200 }
201 return flags;
202 }
203
3a2a7b5f
BA
204 postPlay(move) {
205 super.postPlay(move);
71ef1664 206 const c = move.vanish[0].c;
3a2a7b5f
BA
207 const secondRank = { "w": 6, "b": 2 };
208 if (move.vanish[0].p == V.PAWN && secondRank[c] == move.start.x)
71ef1664
BA
209 // This move turns off a 2-squares pawn flag
210 this.pawnFlags[c][move.start.y] = false;
211 }
212
213 static get VALUES() {
214 return {
215 p: 1,
216 r: 5,
217 n: 3,
218 b: 4,
219 q: 10,
220 k: 1000
221 };
222 }
b83a675a
BA
223
224 static get SEARCH_DEPTH() {
225 return 2;
226 }
7e8a7ea1 227
e3e2cc44 228};