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