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