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