37d0521f1b8657dd2f016c47ba9b675044bf4ceb
[vchess.git] / client / src / variants / Pacifist1.js
1 import { ChessRules } from "@/base_rules";
2
3 export class Pacifist1Rules extends ChessRules {
4
5 static get PawnSpecs() {
6 return Object.assign(
7 {},
8 ChessRules.PawnSpecs,
9 { canCapture: false }
10 );
11 }
12
13 static get HasEnpassant() {
14 return false;
15 }
16
17 static IsGoodPosition(position) {
18 if (position.length == 0) return false;
19 const rows = position.split("/");
20 if (rows.length != V.size.x) return false;
21 let kingsCount = 0;
22 for (let row of rows) {
23 let sumElts = 0;
24 for (let i = 0; i < row.length; i++) {
25 if (['K','k'].includes(row[i])) kingsCount++;
26 if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
27 else {
28 const num = parseInt(row[i], 10);
29 if (isNaN(num)) return false;
30 sumElts += num;
31 }
32 }
33 if (sumElts != V.size.y) return false;
34 }
35 // Both kings should be on board. May be of the same color.
36 if (kingsCount != 2) return false;
37 return true;
38 }
39
40 // Kings may be swapped, so they are not tracked (no kingPos)
41 scanKings(fen) { }
42
43 // Sum white pieces attacking a square, and remove black pieces count.
44 sumAttacks([x, y]) {
45 const getSign = (color) => {
46 return (color == 'w' ? 1 : -1);
47 };
48 let res = 0;
49 // Knights:
50 V.steps[V.KNIGHT].forEach(s => {
51 const [i, j] = [x + s[0], y + s[1]];
52 if (V.OnBoard(i, j) && this.getPiece(i, j) == V.KNIGHT)
53 res += getSign(this.getColor(i, j));
54 });
55 // Kings:
56 V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(s => {
57 const [i, j] = [x + s[0], y + s[1]];
58 if (V.OnBoard(i, j) && this.getPiece(i, j) == V.KING)
59 res += getSign(this.getColor(i, j));
60 });
61 // Pawns:
62 for (let c of ['w', 'b']) {
63 for (let shift of [-1, 1]) {
64 const sign = getSign(c);
65 const [i, j] = [x + sign, y + shift];
66 if (
67 V.OnBoard(i, j) &&
68 this.getPiece(i, j) == V.PAWN &&
69 this.getColor(i, j) == c
70 ) {
71 res += sign;
72 }
73 }
74 }
75 // Other pieces (sliders):
76 V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(s => {
77 let [i, j] = [x + s[0], y + s[1]];
78 let compatible = [V.QUEEN];
79 compatible.push(s[0] == 0 || s[1] == 0 ? V.ROOK : V.BISHOP);
80 let firstCol = undefined;
81 while (V.OnBoard(i, j)) {
82 if (this.board[i][j] != V.EMPTY) {
83 if (!(compatible.includes(this.getPiece(i, j)))) break;
84 const colIJ = this.getColor(i, j);
85 if (!firstCol) firstCol = colIJ;
86 if (colIJ == firstCol) res += getSign(colIJ);
87 else break;
88 }
89 i += s[0];
90 j += s[1];
91 }
92 });
93 return res;
94 }
95
96 getPotentialMovesFrom([x, y]) {
97 let moves = super.getPotentialMovesFrom([x, y]);
98 const color = this.turn;
99 const oppCol = V.GetOppCol(color);
100 if (this.getPiece(x, y) == V.PAWN) {
101 // Pawns cannot move 2 squares if the intermediate is overly persuaded
102 moves = moves.filter(m => {
103 if (Math.abs(m.end.x - m.start.x) == 2) {
104 const [i, j] = [(m.start.x + m.end.x) / 2, y];
105 const persuasion = this.sumAttacks([i, j]);
106 return (
107 color == 'w' && persuasion >= 0 ||
108 color == 'b' && persuasion <= 0
109 );
110 }
111 return true;
112 });
113 }
114 // Potentially flipped (opp) pieces
115 let targets = [];
116 for (let i=0; i<8; i++) {
117 for (let j=0; j<8; j++) {
118 if (this.board[i][j] != V.EMPTY && this.getColor(i, j) == oppCol)
119 targets.push([i, j]);
120 }
121 }
122 moves.forEach(m => {
123 // Start persuading other pieces: loop until nothing changes
124 V.PlayOnBoard(this.board, m);
125 while (true) {
126 let persuaded = [];
127 targets.forEach(t => {
128 if (this.getColor(t[0], t[1]) == oppCol) {
129 const sqAttacks = this.sumAttacks([t[0], t[1]]);
130 if (
131 (oppCol == 'w' && sqAttacks < 0) ||
132 (oppCol == 'b' && sqAttacks > 0)
133 ) {
134 persuaded.push(t);
135 }
136 }
137 });
138 if (persuaded.length == 0) break;
139 persuaded.forEach(p => {
140 this.board[p[0]][p[1]] = color + this.getPiece(p[0], p[1]);
141 });
142 }
143 V.UndoOnBoard(this.board, m);
144 // Reset pieces colors + adjust move (flipped pieces)
145 targets.forEach(t => {
146 if (this.getColor(t[0], t[1]) == color) {
147 const piece = this.getPiece(t[0], t[1]);
148 m.appear.push({ x: t[0], y: t[1], c: color, p: piece });
149 m.vanish.push({ x: t[0], y: t[1], c: oppCol, p: piece });
150 this.board[t[0]][t[1]] = oppCol + piece;
151 }
152 });
153 });
154 return moves;
155 }
156
157 getSlideNJumpMoves([x, y], steps, oneStep) {
158 let moves = [];
159 outerLoop: for (let loop = 0; loop < steps.length; loop++) {
160 const step = steps[loop];
161 let i = x + step[0];
162 let j = y + step[1];
163 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
164 moves.push(this.getBasicMove([x, y], [i, j]));
165 if (oneStep) continue outerLoop;
166 i += step[0];
167 j += step[1];
168 }
169 // No captures
170 }
171 return moves;
172 }
173
174 underCheck(color) {
175 // Find the king(s) and determine if it (both) is persuaded.
176 // If yes, "under check"
177 let kingPos = [];
178 for (let i=0; i<8; i++) {
179 for (let j=0; j<8; j++) {
180 if (this.getPiece(i, j) == V.KING && this.getColor(i, j) == color)
181 kingPos.push([i, j]);
182 }
183 }
184 return kingPos.every(kp => {
185 const persuasion = this.sumAttacks(kp);
186 return (
187 (color == 'w' && persuasion < 0) ||
188 (color == 'b' && persuasion > 0)
189 );
190 });
191 }
192
193 filterValid(moves) {
194 const fmoves = super.filterValid(moves);
195 const color = this.turn;
196 // If the king isn't here, only moves persuading a king are valid
197 const kingHere = this.board.some(b =>
198 b.some(cell => cell[0] == color && cell[1] == V.KING)
199 );
200 if (!kingHere) {
201 return (
202 fmoves.filter(m => m.appear.some(a => a.c == color && a.p == V.KING))
203 );
204 }
205 return fmoves;
206 }
207
208 getCheckSquares() {
209 // There are not really "checks": just color change
210 return [];
211 }
212
213 getCurrentScore() {
214 const color = this.turn;
215 // TODO: if no king of turn color, and no move to get one, then it's lost
216 // otherwise 1/2 if no moves, or "*"
217 const kingHere = this.board.some(b =>
218 b.some(cell => cell[0] == color && cell[1] == V.KING)
219 );
220 if (kingHere) {
221 if (this.atLeastOneMove()) return "*";
222 return "1/2";
223 }
224 // No king was found: try to convert one
225 const moves = this.getAllValidMoves();
226 return (
227 moves.some(m => m.appear.some(a => a.c == color && a.p == V.KING))
228 ? "*"
229 : (color == 'w' ? "0-1" : "1-0")
230 );
231 }
232
233 postPlay(move) {
234 this.updateCastleFlags(move, move.vanish[0].p);
235 }
236
237 postUndo() {}
238
239 static get SEARCH_DEPTH() {
240 return 1;
241 }
242
243 };