Fix SuctionChess, draft HiddenRules (unfinished)
[vchess.git] / client / src / variants / Hidden.js
1 import { ChessRules } from "@/base_rules";
2 import { ArrayFun } from "@/utils/array";
3 import { randInt } from "@/utils/alea";
4
5 export const VariantRules = class HiddenRules extends ChessRules {
6 static get HasFlags() {
7 return false;
8 }
9
10 static get HasEnpassant() {
11 return false;
12 }
13
14 // Analyse in Hidden mode makes no sense
15 static get CanAnalyze() {
16 return false;
17 }
18
19 // Moves are revealed only when game ends
20 static get ShowMoves() {
21 return "none";
22 }
23
24 static get HIDDEN_DECODE() {
25 return {
26 s: "p",
27 t: "q",
28 u: "r",
29 c: "b",
30 o: "n",
31 l: "k"
32 };
33 }
34 static get HIDDEN_CODE() {
35 return {
36 p: "s",
37 q: "t",
38 r: "u",
39 b: "c",
40 n: "o",
41 k: "l"
42 };
43 }
44
45 static get PIECES() {
46 return ChessRules.PIECES.concat(Object.values(V.HIDDEN_CODE));
47 }
48
49 // Scan board for kings positions (no castling)
50 scanKingsRooks(fen) {
51 this.kingPos = { w: [-1, -1], b: [-1, -1] };
52 const fenRows = V.ParseFen(fen).position.split("/");
53 for (let i = 0; i < fenRows.length; i++) {
54 let k = 0; //column index on board
55 for (let j = 0; j < fenRows[i].length; j++) {
56 switch (fenRows[i].charAt(j)) {
57 case "k":
58 case "l":
59 this.kingPos["b"] = [i, k];
60 break;
61 case "K":
62 case "L":
63 this.kingPos["w"] = [i, k];
64 break;
65 default: {
66 const num = parseInt(fenRows[i].charAt(j));
67 if (!isNaN(num)) k += num - 1;
68 }
69 }
70 k++;
71 }
72 }
73 }
74
75 getPpath(b, color, score) {
76 if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) {
77 // Supposed to be hidden.
78 if (score == "*" && (!color || color != b[0]))
79 return "Hidden/" + b[0] + "p";
80 // Else: condition OK to show the piece
81 return b[0] + V.HIDDEN_DECODE[b[1]];
82 }
83 // The piece is already not supposed to be hidden:
84 return b;
85 }
86
87 //getPotentialMovesFrom: TODO: write
88
89 // TODO: bishops on different colors, a1 --> h1, h2 --> a2 ?
90 static GenRandInitFen() {
91 let pieces = { w: new Array(8), b: new Array(8) };
92 // Shuffle pieces + pawns on two first ranks
93 for (let c of ["w", "b"]) {
94 let positions = ArrayFun.range(16);
95
96 // Get random squares for bishops
97 let randIndex = 2 * randInt(8);
98 const bishop1Pos = positions[randIndex];
99 // The second bishop must be on a square of different color
100 let randIndex_tmp = 2 * randInt(8) + 1;
101 const bishop2Pos = positions[randIndex_tmp];
102 // Remove chosen squares
103 positions.splice(Math.max(randIndex, randIndex_tmp), 1);
104 positions.splice(Math.min(randIndex, randIndex_tmp), 1);
105
106 // Get random squares for knights
107 randIndex = randInt(14);
108 const knight1Pos = positions[randIndex];
109 positions.splice(randIndex, 1);
110 randIndex = randInt(13);
111 const knight2Pos = positions[randIndex];
112 positions.splice(randIndex, 1);
113
114 // Get random square for queen
115 randIndex = randInt(12);
116 const queenPos = positions[randIndex];
117 positions.splice(randIndex, 1);
118
119 // Get random squares for pawns
120 // TODO...
121
122 // Rooks and king positions are now fixed,
123 // because of the ordering rook-king-rook
124 const rook1Pos = positions[0];
125 const kingPos = positions[1];
126 const rook2Pos = positions[2];
127
128 // Finally put the shuffled pieces in the board array
129 pieces[c][rook1Pos] = "r";
130 pieces[c][knight1Pos] = "n";
131 pieces[c][bishop1Pos] = "b";
132 pieces[c][queenPos] = "q";
133 pieces[c][kingPos] = "k";
134 pieces[c][bishop2Pos] = "b";
135 pieces[c][knight2Pos] = "n";
136 pieces[c][rook2Pos] = "r";
137 }
138 return (
139 pieces["b"].join("") +
140 "/pppppppp/8/8/8/8/PPPPPPPP/" +
141 pieces["w"].join("").toUpperCase() +
142 " w 0"
143 );
144 }
145
146 getCheckSquares() {
147 return [];
148 }
149
150 updateVariables(move) {
151 super.updateVariables(move);
152 if (
153 move.vanish.length >= 2 &&
154 [V.KING,V.HIDDEN_CODE[V.KING]].includes(move.vanish[1].p)
155 ) {
156 // We took opponent king
157 this.kingPos[this.turn] = [-1, -1];
158 }
159 }
160
161 unupdateVariables(move) {
162 super.unupdateVariables(move);
163 const c = move.vanish[0].c;
164 const oppCol = V.GetOppCol(c);
165 if (this.kingPos[oppCol][0] < 0)
166 // Last move took opponent's king:
167 this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y];
168 }
169
170 getCurrentScore() {
171 const color = this.turn;
172 const kp = this.kingPos[color];
173 if (kp[0] < 0)
174 // King disappeared
175 return color == "w" ? "0-1" : "1-0";
176 // Assume that stalemate is impossible:
177 return "*";
178 }
179
180 getComputerMove() {
181 // Just return a random move. TODO: something smarter...
182 const moves = this.getAllValidMoves();
183 return moves[randInt(moves.length)];
184 }
185 };