Fix notation and pieces setup for random asymmetric in Eightpieces
[vchess.git] / client / src / variants / Suicide.js
1 import { ChessRules } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { shuffle } from "@/utils/alea";
4
5 export class SuicideRules extends ChessRules {
6 static get HasFlags() {
7 return false;
8 }
9
10 static get HasCastle() {
11 return false;
12 }
13
14 static get PawnSpecs() {
15 return Object.assign(
16 {},
17 ChessRules.PawnSpecs,
18 { promotions: ChessRules.PawnSpecs.promotions.concat([V.KING]) }
19 );
20 }
21
22 static IsGoodPosition(position) {
23 if (position.length == 0) return false;
24 const rows = position.split("/");
25 if (rows.length != V.size.x) return false;
26 // Just check that at least one piece of each color is there:
27 let pieces = { "w": 0, "b": 0 };
28 for (let row of rows) {
29 let sumElts = 0;
30 for (let i = 0; i < row.length; i++) {
31 const lowerRi = row[i].toLowerCase();
32 if (V.PIECES.includes(lowerRi)) {
33 pieces[row[i] == lowerRi ? "b" : "w"]++;
34 sumElts++;
35 } else {
36 const num = parseInt(row[i]);
37 if (isNaN(num)) return false;
38 sumElts += num;
39 }
40 }
41 if (sumElts != V.size.y) return false;
42 }
43 if (Object.values(pieces).some(v => v == 0)) return false;
44 return true;
45 }
46
47 scanKings() {}
48
49 // Trim all non-capturing moves (not the most efficient, but easy)
50 static KeepCaptures(moves) {
51 return moves.filter(m => m.vanish.length == 2);
52 }
53
54 // Stop at the first capture found (if any)
55 atLeastOneCapture() {
56 const color = this.turn;
57 const oppCol = V.GetOppCol(color);
58 for (let i = 0; i < V.size.x; i++) {
59 for (let j = 0; j < V.size.y; j++) {
60 if (
61 this.board[i][j] != V.EMPTY &&
62 this.getColor(i, j) != oppCol &&
63 this.getPotentialMovesFrom([i, j]).some(m => m.vanish.length == 2)
64 ) {
65 return true;
66 }
67 }
68 }
69 return false;
70 }
71
72 getPossibleMovesFrom(sq) {
73 let moves = this.getPotentialMovesFrom(sq);
74 const captureMoves = V.KeepCaptures(moves);
75 if (captureMoves.length > 0) return captureMoves;
76 if (this.atLeastOneCapture()) return [];
77 return moves;
78 }
79
80 filterValid(moves) {
81 return moves;
82 }
83
84 getAllValidMoves() {
85 const moves = super.getAllValidMoves();
86 if (moves.some(m => m.vanish.length == 2)) return V.KeepCaptures(moves);
87 return moves;
88 }
89
90 atLeastOneMove() {
91 const color = this.turn;
92 for (let i = 0; i < V.size.x; i++) {
93 for (let j = 0; j < V.size.y; j++) {
94 if (
95 this.getColor(i, j) == color &&
96 this.getPotentialMovesFrom([i, j]).length > 0
97 ) {
98 return true;
99 }
100 }
101 }
102 return false;
103 }
104
105 getCheckSquares() {
106 return [];
107 }
108
109 // No variables update because no royal king + no castling
110 prePlay() {}
111 postPlay() {}
112 preUndo() {}
113 postUndo() {}
114
115 getCurrentScore() {
116 if (this.atLeastOneMove()) return "*";
117 // No valid move: the side who cannot move wins
118 return this.turn == "w" ? "1-0" : "0-1";
119 }
120
121 static get VALUES() {
122 return {
123 p: 1,
124 r: 7,
125 n: 3,
126 b: 3,
127 q: 5,
128 k: 5
129 };
130 }
131
132 static get SEARCH_DEPTH() {
133 return 4;
134 }
135
136 evalPosition() {
137 // Less material is better:
138 return -super.evalPosition();
139 }
140
141 static GenRandInitFen(randomness) {
142 if (randomness == 0)
143 return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 -";
144
145 let pieces = { w: new Array(8), b: new Array(8) };
146 for (let c of ["w", "b"]) {
147 if (c == 'b' && randomness == 1) {
148 pieces['b'] = pieces['w'];
149 break;
150 }
151
152 // Get random squares for every piece, totally freely
153 let positions = shuffle(ArrayFun.range(8));
154 const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q'];
155 const rem2 = positions[0] % 2;
156 if (rem2 == positions[1] % 2) {
157 // Fix bishops (on different colors)
158 for (let i=2; i<8; i++) {
159 if (positions[i] % 2 != rem2)
160 [positions[1], positions[i]] = [positions[i], positions[1]];
161 }
162 }
163 for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i];
164 }
165 return (
166 pieces["b"].join("") +
167 "/pppppppp/8/8/8/8/PPPPPPPP/" +
168 pieces["w"].join("").toUpperCase() +
169 // En-passant allowed, but no flags
170 " w 0 -"
171 );
172 }
173 };