Update TODO, improve style for Crazyhouse variant, fix an introduced bug in example...
[vchess.git] / client / src / variants / Shatranj.js
CommitLineData
c3a86f01
BA
1import { ChessRules } from "@/base_rules";
2
a4eca0dc 3export const VariantRules = class ShatranjRules extends ChessRules {
c3a86f01
BA
4 static get HasFlags() {
5 return false;
6 }
7
8 static get HasEnpassant() {
9 return false;
10 }
11
12 static get ElephantSteps() {
13 return [
14 [-2, -2],
15 [-2, 2],
16 [2, -2],
17 [2, 2]
18 ];
19 }
20
7ba4a5bc
BA
21 static GenRandInitFen(randomness) {
22 return ChessRules.GenRandInitFen(randomness).replace("w 1111 -", "w");
c3a86f01
BA
23 }
24
25 getPotentialPawnMoves([x, y]) {
26 const color = this.turn;
27 let moves = [];
28 const [sizeX, sizeY] = [V.size.x, V.size.y];
29 const shiftX = color == "w" ? -1 : 1;
30 const startRank = color == "w" ? sizeX - 2 : 1;
31 const lastRank = color == "w" ? 0 : sizeX - 1;
32 // Promotion in minister (queen) only:
33 const finalPiece = x + shiftX == lastRank ? V.QUEEN : V.PAWN;
34
35 if (this.board[x + shiftX][y] == V.EMPTY) {
36 // One square forward
37 moves.push(
38 this.getBasicMove([x, y], [x + shiftX, y], {
39 c: color,
40 p: finalPiece
41 })
42 );
43 }
44 // Captures
45 for (let shiftY of [-1, 1]) {
46 if (
47 y + shiftY >= 0 &&
48 y + shiftY < sizeY &&
49 this.board[x + shiftX][y + shiftY] != V.EMPTY &&
50 this.canTake([x, y], [x + shiftX, y + shiftY])
51 ) {
52 moves.push(
53 this.getBasicMove([x, y], [x + shiftX, y + shiftY], {
54 c: color,
55 p: finalPiece
56 })
57 );
58 }
59 }
60
61 return moves;
62 }
63
64 getPotentialBishopMoves(sq) {
65 let moves = this.getSlideNJumpMoves(sq, V.ElephantSteps, "oneStep");
66 // Complete with "repositioning moves": like a queen, without capture
67 let repositioningMoves = this.getSlideNJumpMoves(
68 sq,
69 V.steps[V.BISHOP],
70 "oneStep"
71 ).filter(m => m.vanish.length == 1);
72 return moves.concat(repositioningMoves);
73 }
74
75 getPotentialQueenMoves(sq) {
8055eabd
BA
76 // Diagonal capturing moves
77 let captures = this.getSlideNJumpMoves(
c3a86f01
BA
78 sq,
79 V.steps[V.BISHOP],
80 "oneStep"
8055eabd
BA
81 ).filter(m => m.vanish.length == 2);
82 return captures.concat(
83 // Orthogonal non-capturing moves
84 this.getSlideNJumpMoves(
85 sq,
86 V.steps[V.ROOK],
87 "oneStep"
88 ).filter(m => m.vanish.length == 1)
c3a86f01
BA
89 );
90 }
91
92 getPotentialKingMoves(sq) {
93 return this.getSlideNJumpMoves(
94 sq,
95 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
96 "oneStep"
97 );
98 }
99
100 isAttackedByBishop(sq, colors) {
101 return this.isAttackedBySlideNJump(
102 sq,
103 colors,
104 V.BISHOP,
105 V.ElephantSteps,
106 "oneStep"
107 );
108 }
109
110 isAttackedByQueen(sq, colors) {
111 return this.isAttackedBySlideNJump(
112 sq,
113 colors,
114 V.QUEEN,
115 V.steps[V.BISHOP],
116 "oneStep"
117 );
118 }
119
8055eabd
BA
120 getCurrentScore() {
121 const color = this.turn;
122 const getScoreLost = () => {
123 // Result if I lose:
124 return color == "w" ? "0-1" : "1-0";
125 };
126 if (!this.atLeastOneMove())
127 // No valid move: I lose (this includes checkmate)
128 return getScoreLost();
129 // Win if the opponent has no pieces left (except king),
130 // and cannot bare king on the next move.
131 let piecesLeft = {
132 // No need to remember all pieces' squares:
133 // variable only used if just one remaining piece.
134 "w": {count: 0, square: null},
135 "b": {count: 0, square: null}
136 };
137 outerLoop: for (let i=0; i<V.size.x; i++) {
138 for (let j=0; j<V.size.y; j++) {
139 if (this.board[i][j] != V.EMPTY && this.getPiece(i,j) != V.KING) {
140 const sqCol = this.getColor(i,j);
141 piecesLeft[sqCol].count++;
142 piecesLeft[sqCol].square = [i,j];
143 }
144 }
145 }
146 if (Object.values(piecesLeft).every(v => v.count > 0))
147 return "*";
148 // No pieces left for some side: if both kings are bare, it's a draw
149 if (Object.values(piecesLeft).every(v => v.count == 0))
150 return "1/2";
151 if (piecesLeft[color].count > 0)
152 // He could have drawn, but didn't take my last piece...
153 return color == "w" ? "1-0" : "0-1";
154 const oppCol = V.GetOppCol(color);
155 if (piecesLeft[oppCol].count >= 2)
156 // 2 enemy units or more: I lose
157 return getScoreLost();
158 // I don't have any piece, my opponent have one: can I take it?
159 if (this.isAttacked(piecesLeft[oppCol].square, [color]))
160 // Yes! But I still need to take it
161 return "*";
162 // No :(
163 return getScoreLost();
164 }
165
c3a86f01
BA
166 static get VALUES() {
167 return {
168 p: 1,
169 r: 5,
170 n: 3,
8055eabd
BA
171 b: 3,
172 q: 3,
c3a86f01
BA
173 k: 1000
174 };
175 }
176};