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