Pandemonium 1 & 2, Stealthbomb 1 & 2
[vchess.git] / client / src / variants / Stealthbomb1.js
1 import { ChessRules, Move, PiPo } from "@/base_rules";
2
3 export class Stealthbomb1Rules extends ChessRules {
4
5 static get CanAnalyze() {
6 return false;
7 }
8
9 static get SomeHiddenMoves() {
10 return true;
11 }
12
13 static get BOMB_DECODE() {
14 return {
15 s: "p",
16 t: "q",
17 u: "r",
18 c: "b",
19 o: "n",
20 l: "k"
21 };
22 }
23 static get BOMB_CODE() {
24 return {
25 p: "s",
26 q: "t",
27 r: "u",
28 b: "c",
29 n: "o",
30 k: "l"
31 };
32 }
33
34 static get PIECES() {
35 return ChessRules.PIECES.concat(Object.keys(V.BOMB_DECODE));
36 }
37
38 getPiece(i, j) {
39 const piece = this.board[i][j].charAt(1);
40 if (
41 ChessRules.PIECES.includes(piece) ||
42 // 'side' is used to determine what I see: normal or "loaded" piece?
43 this.getColor(i, j) == this.side
44 ) {
45 return piece;
46 }
47 // Loaded piece, but no right to view it
48 return V.BOMB_DECODE[piece];
49 }
50
51 getPpath(b, color, score) {
52 if (Object.keys(V.BOMB_DECODE).includes(b[1])) {
53 // Supposed to be hidden.
54 if (score == "*" && (!color || color != b[0]))
55 return b[0] + V.BOMB_DECODE[b[1]];
56 return "Stealthbomb/" + b;
57 }
58 return b;
59 }
60
61 hoverHighlight([x, y]) {
62 const c = this.turn;
63 return (
64 this.movesCount <= 1 &&
65 (
66 (c == 'w' && x >= 6) ||
67 (c == 'b' && x <= 1)
68 )
69 );
70 }
71
72 onlyClick([x, y]) {
73 return (
74 this.movesCount <= 1 ||
75 // TODO: next line theoretically shouldn't be required...
76 (this.movesCount == 2 && this.getColor(x, y) != this.turn)
77 );
78 }
79
80 // Initiate the game by choosing a square for the bomb:
81 doClick(square) {
82 const c = this.turn;
83 if (
84 this.movesCount >= 2 ||
85 (
86 (c == 'w' && square[0] < 6) ||
87 (c == 'b' && square[0] > 1)
88 )
89 ) {
90 return null;
91 }
92 const [x, y] = square;
93 const piece = super.getPiece(x, y);
94 return new Move({
95 appear: [ new PiPo({ x: x, y: y, c: c, p: V.BOMB_CODE[piece] }) ],
96 vanish: [ new PiPo({ x: x, y: y, c: c, p: piece }) ],
97 start: { x: -1, y: -1 },
98 end: { x: x, y: y, noHighlight: true }
99 });
100 }
101
102 getPotentialMovesFrom([x, y]) {
103 if (this.movesCount <= 1) {
104 const setup = this.doClick([x, y]);
105 return (!setup ? [] : [setup]);
106 }
107 let moves = super.getPotentialMovesFrom([x, y]);
108 const c = this.turn;
109 // Add bomb explosion
110 if (Object.keys(V.BOMB_DECODE).includes(this.board[x][y][1])) {
111 let mv = new Move({
112 appear: [ ],
113 vanish: [ new PiPo({ x: x, y: y, c: c, p: this.board[x][y][1] }) ],
114 end: { x: this.kingPos[c][0], y: this.kingPos[c][1] }
115 });
116 for (let s of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) {
117 let [i, j] = [x + s[0], y + s[1]];
118 if (V.OnBoard(i, j) && this.board[i][j] != V.EMPTY) {
119 mv.vanish.push(
120 new PiPo({
121 x: i,
122 y: j,
123 c: this.getColor(i, j),
124 p: this.board[i][j][1]
125 })
126 );
127 }
128 }
129 moves.push(mv);
130 }
131 return moves;
132 }
133
134 // NOTE: a lot of copy-paste from Atomic from here.
135 postPlay(move) {
136 if (this.movesCount >= 3) {
137 super.postPlay(move);
138 if (move.appear.length == 0) {
139 // Explosion
140 const firstRank = { w: 7, b: 0 };
141 for (let c of ["w", "b"]) {
142 // Did we explode king of color c ?
143 if (
144 Math.abs(this.kingPos[c][0] - move.start.x) <= 1 &&
145 Math.abs(this.kingPos[c][1] - move.start.y) <= 1
146 ) {
147 this.kingPos[c] = [-1, -1];
148 this.castleFlags[c] = [8, 8];
149 }
150 else {
151 // Now check if init rook(s) exploded
152 if (Math.abs(move.start.x - firstRank[c]) <= 1) {
153 if (Math.abs(move.start.y - this.castleFlags[c][0]) <= 1)
154 this.castleFlags[c][0] = 8;
155 if (Math.abs(move.start.y - this.castleFlags[c][1]) <= 1)
156 this.castleFlags[c][1] = 8;
157 }
158 }
159 }
160 }
161 }
162 }
163
164 postUndo(move) {
165 if (this.movesCount >= 2) {
166 super.postUndo(move);
167 const c = this.turn;
168 const oppCol = V.GetOppCol(c);
169 if ([this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => e < 0)) {
170 // Last move exploded some king..
171 for (let psq of move.vanish) {
172 if (psq.p == "k")
173 this.kingPos[psq.c == c ? c : oppCol] = [psq.x, psq.y];
174 }
175 }
176 }
177 }
178
179 underCheck(color) {
180 const oppCol = V.GetOppCol(color);
181 let res = undefined;
182 // If our king disappeared, move is not valid
183 if (this.kingPos[color][0] < 0) res = true;
184 // If opponent king disappeared, move is valid
185 else if (this.kingPos[oppCol][0] < 0) res = false;
186 // Otherwise, if we remain under check, move is not valid
187 else res = this.isAttacked(this.kingPos[color], oppCol);
188 return res;
189 }
190
191 getCheckSquares() {
192 const color = this.turn;
193 let res = [];
194 if (
195 this.kingPos[color][0] >= 0 && //king might have exploded
196 this.isAttacked(this.kingPos[color], V.GetOppCol(color))
197 ) {
198 res = [JSON.parse(JSON.stringify(this.kingPos[color]))];
199 }
200 return res;
201 }
202
203 getCurrentScore() {
204 const color = this.turn;
205 const kp = this.kingPos[color];
206 if (kp[0] < 0)
207 // King disappeared
208 return color == "w" ? "0-1" : "1-0";
209 if (this.atLeastOneMove()) return "*";
210 if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2";
211 return color == "w" ? "0-1" : "1-0"; //checkmate
212 }
213
214 getNotation(move) {
215 if (this.movesCount <= 1) return "Bomb?";
216 const c = this.turn;
217 if (move.end.x == this.kingPos[c][0] && move.end.y == this.kingPos[c][1])
218 return V.CoordsToSquare(move.start) + "~X";
219 if (Object.keys(V.BOMB_DECODE).includes(move.vanish[0].p)) {
220 let cpMove = JSON.parse(JSON.stringify(move));
221 cpMove.vanish[0].p = V.BOMB_DECODE[move.vanish[0].p];
222 if (Object.keys(V.BOMB_DECODE).includes(move.appear[0].p))
223 cpMove.appear[0].p = V.BOMB_DECODE[move.appear[0].p];
224 return super.getNotation(cpMove);
225 }
226 return super.getNotation(move);
227 }
228
229 };