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