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