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