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