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