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