Commit | Line | Data |
---|---|---|
a4eca0dc BA |
1 | // TODO: bishop OK, but queen should move vertical/horizontal and capture diagonally. |
2 | // ==> then the pawn promotion is a real promotion (enhancement). | |
3 | ||
c3a86f01 BA |
4 | import { ChessRules } from "@/base_rules"; |
5 | ||
a4eca0dc | 6 | export const VariantRules = class ShatranjRules extends ChessRules { |
c3a86f01 BA |
7 | static get HasFlags() { |
8 | return false; | |
9 | } | |
10 | ||
11 | static get HasEnpassant() { | |
12 | return false; | |
13 | } | |
14 | ||
15 | static get ElephantSteps() { | |
16 | return [ | |
17 | [-2, -2], | |
18 | [-2, 2], | |
19 | [2, -2], | |
20 | [2, 2] | |
21 | ]; | |
22 | } | |
23 | ||
7ba4a5bc BA |
24 | static GenRandInitFen(randomness) { |
25 | return ChessRules.GenRandInitFen(randomness).replace("w 1111 -", "w"); | |
c3a86f01 BA |
26 | } |
27 | ||
28 | getPotentialPawnMoves([x, y]) { | |
29 | const color = this.turn; | |
30 | let moves = []; | |
31 | const [sizeX, sizeY] = [V.size.x, V.size.y]; | |
32 | const shiftX = color == "w" ? -1 : 1; | |
33 | const startRank = color == "w" ? sizeX - 2 : 1; | |
34 | const lastRank = color == "w" ? 0 : sizeX - 1; | |
35 | // Promotion in minister (queen) only: | |
36 | const finalPiece = x + shiftX == lastRank ? V.QUEEN : V.PAWN; | |
37 | ||
38 | if (this.board[x + shiftX][y] == V.EMPTY) { | |
39 | // One square forward | |
40 | moves.push( | |
41 | this.getBasicMove([x, y], [x + shiftX, y], { | |
42 | c: color, | |
43 | p: finalPiece | |
44 | }) | |
45 | ); | |
46 | } | |
47 | // Captures | |
48 | for (let shiftY of [-1, 1]) { | |
49 | if ( | |
50 | y + shiftY >= 0 && | |
51 | y + shiftY < sizeY && | |
52 | this.board[x + shiftX][y + shiftY] != V.EMPTY && | |
53 | this.canTake([x, y], [x + shiftX, y + shiftY]) | |
54 | ) { | |
55 | moves.push( | |
56 | this.getBasicMove([x, y], [x + shiftX, y + shiftY], { | |
57 | c: color, | |
58 | p: finalPiece | |
59 | }) | |
60 | ); | |
61 | } | |
62 | } | |
63 | ||
64 | return moves; | |
65 | } | |
66 | ||
67 | getPotentialBishopMoves(sq) { | |
68 | let moves = this.getSlideNJumpMoves(sq, V.ElephantSteps, "oneStep"); | |
69 | // Complete with "repositioning moves": like a queen, without capture | |
70 | let repositioningMoves = this.getSlideNJumpMoves( | |
71 | sq, | |
72 | V.steps[V.BISHOP], | |
73 | "oneStep" | |
74 | ).filter(m => m.vanish.length == 1); | |
75 | return moves.concat(repositioningMoves); | |
76 | } | |
77 | ||
78 | getPotentialQueenMoves(sq) { | |
8055eabd BA |
79 | // Diagonal capturing moves |
80 | let captures = this.getSlideNJumpMoves( | |
c3a86f01 BA |
81 | sq, |
82 | V.steps[V.BISHOP], | |
83 | "oneStep" | |
8055eabd BA |
84 | ).filter(m => m.vanish.length == 2); |
85 | return captures.concat( | |
86 | // Orthogonal non-capturing moves | |
87 | this.getSlideNJumpMoves( | |
88 | sq, | |
89 | V.steps[V.ROOK], | |
90 | "oneStep" | |
91 | ).filter(m => m.vanish.length == 1) | |
c3a86f01 BA |
92 | ); |
93 | } | |
94 | ||
95 | getPotentialKingMoves(sq) { | |
96 | return this.getSlideNJumpMoves( | |
97 | sq, | |
98 | V.steps[V.ROOK].concat(V.steps[V.BISHOP]), | |
99 | "oneStep" | |
100 | ); | |
101 | } | |
102 | ||
103 | isAttackedByBishop(sq, colors) { | |
104 | return this.isAttackedBySlideNJump( | |
105 | sq, | |
106 | colors, | |
107 | V.BISHOP, | |
108 | V.ElephantSteps, | |
109 | "oneStep" | |
110 | ); | |
111 | } | |
112 | ||
113 | isAttackedByQueen(sq, colors) { | |
114 | return this.isAttackedBySlideNJump( | |
115 | sq, | |
116 | colors, | |
117 | V.QUEEN, | |
118 | V.steps[V.BISHOP], | |
119 | "oneStep" | |
120 | ); | |
121 | } | |
122 | ||
8055eabd BA |
123 | getCurrentScore() { |
124 | const color = this.turn; | |
125 | const getScoreLost = () => { | |
126 | // Result if I lose: | |
127 | return color == "w" ? "0-1" : "1-0"; | |
128 | }; | |
129 | if (!this.atLeastOneMove()) | |
130 | // No valid move: I lose (this includes checkmate) | |
131 | return getScoreLost(); | |
132 | // Win if the opponent has no pieces left (except king), | |
133 | // and cannot bare king on the next move. | |
134 | let piecesLeft = { | |
135 | // No need to remember all pieces' squares: | |
136 | // variable only used if just one remaining piece. | |
137 | "w": {count: 0, square: null}, | |
138 | "b": {count: 0, square: null} | |
139 | }; | |
140 | outerLoop: for (let i=0; i<V.size.x; i++) { | |
141 | for (let j=0; j<V.size.y; j++) { | |
142 | if (this.board[i][j] != V.EMPTY && this.getPiece(i,j) != V.KING) { | |
143 | const sqCol = this.getColor(i,j); | |
144 | piecesLeft[sqCol].count++; | |
145 | piecesLeft[sqCol].square = [i,j]; | |
146 | } | |
147 | } | |
148 | } | |
149 | if (Object.values(piecesLeft).every(v => v.count > 0)) | |
150 | return "*"; | |
151 | // No pieces left for some side: if both kings are bare, it's a draw | |
152 | if (Object.values(piecesLeft).every(v => v.count == 0)) | |
153 | return "1/2"; | |
154 | if (piecesLeft[color].count > 0) | |
155 | // He could have drawn, but didn't take my last piece... | |
156 | return color == "w" ? "1-0" : "0-1"; | |
157 | const oppCol = V.GetOppCol(color); | |
158 | if (piecesLeft[oppCol].count >= 2) | |
159 | // 2 enemy units or more: I lose | |
160 | return getScoreLost(); | |
161 | // I don't have any piece, my opponent have one: can I take it? | |
162 | if (this.isAttacked(piecesLeft[oppCol].square, [color])) | |
163 | // Yes! But I still need to take it | |
164 | return "*"; | |
165 | // No :( | |
166 | return getScoreLost(); | |
167 | } | |
168 | ||
c3a86f01 BA |
169 | static get VALUES() { |
170 | return { | |
171 | p: 1, | |
172 | r: 5, | |
173 | n: 3, | |
8055eabd BA |
174 | b: 3, |
175 | q: 3, | |
c3a86f01 BA |
176 | k: 1000 |
177 | }; | |
178 | } | |
179 | }; |