Refactor models (merge Players in Games), add cursor to correspondance games. Finishe...
[vchess.git] / client / src / variants / Extinction.js
1 import { ChessRules } from "@/base_rules";
2
3 export const VariantRules = class ExtinctionRules extends ChessRules {
4 static IsGoodPosition(position) {
5 if (!ChessRules.IsGoodPosition(position))
6 return false;
7 // Also check that each piece type is present
8 const rows = position.split("/");
9 let pieces = {};
10 for (let row of rows) {
11 for (let i = 0; i < row.length; i++) {
12 if (isNaN(parseInt(row[i])) && !pieces[row[i]])
13 pieces[row[i]] = true;
14 }
15 }
16 if (Object.keys(pieces).length != 12)
17 return false;
18 return true;
19 }
20
21 setOtherVariables(fen) {
22 super.setOtherVariables(fen);
23 const pos = V.ParseFen(fen).position;
24 // NOTE: no need for safety "|| []", because each piece type must be present
25 // (otherwise game is already over!)
26 this.material = {
27 w: {
28 [V.KING]: pos.match(/K/g).length,
29 [V.QUEEN]: pos.match(/Q/g).length,
30 [V.ROOK]: pos.match(/R/g).length,
31 [V.KNIGHT]: pos.match(/N/g).length,
32 [V.BISHOP]: pos.match(/B/g).length,
33 [V.PAWN]: pos.match(/P/g).length
34 },
35 b: {
36 [V.KING]: pos.match(/k/g).length,
37 [V.QUEEN]: pos.match(/q/g).length,
38 [V.ROOK]: pos.match(/r/g).length,
39 [V.KNIGHT]: pos.match(/n/g).length,
40 [V.BISHOP]: pos.match(/b/g).length,
41 [V.PAWN]: pos.match(/p/g).length
42 }
43 };
44 }
45
46 getPotentialPawnMoves([x, y]) {
47 let moves = super.getPotentialPawnMoves([x, y]);
48 // Add potential promotions into king
49 const color = this.turn;
50 const shift = color == "w" ? -1 : 1;
51 const lastRank = color == "w" ? 0 : V.size.x - 1;
52
53 if (x + shift == lastRank) {
54 // Normal move
55 if (this.board[x + shift][y] == V.EMPTY)
56 moves.push(
57 this.getBasicMove([x, y], [x + shift, y], { c: color, p: V.KING })
58 );
59 // Captures
60 if (
61 y > 0 &&
62 this.board[x + shift][y - 1] != V.EMPTY &&
63 this.canTake([x, y], [x + shift, y - 1])
64 ) {
65 moves.push(
66 this.getBasicMove([x, y], [x + shift, y - 1], { c: color, p: V.KING })
67 );
68 }
69 if (
70 y < V.size.y - 1 &&
71 this.board[x + shift][y + 1] != V.EMPTY &&
72 this.canTake([x, y], [x + shift, y + 1])
73 ) {
74 moves.push(
75 this.getBasicMove([x, y], [x + shift, y + 1], { c: color, p: V.KING })
76 );
77 }
78 }
79
80 return moves;
81 }
82
83 // TODO: verify this assertion
84 atLeastOneMove() {
85 return true; //always at least one possible move
86 }
87
88 filterValid(moves) {
89 return moves; //there is no check
90 }
91
92 getCheckSquares() {
93 return [];
94 }
95
96 postPlay(move) {
97 super.postPlay(move);
98 // Treat the promotion case: (not the capture part)
99 if (move.appear[0].p != move.vanish[0].p) {
100 this.material[move.appear[0].c][move.appear[0].p]++;
101 this.material[move.appear[0].c][V.PAWN]--;
102 }
103 if (move.vanish.length == 2 && move.appear.length == 1)
104 //capture
105 this.material[move.vanish[1].c][move.vanish[1].p]--;
106 }
107
108 postUndo(move) {
109 super.postUndo(move);
110 if (move.appear[0].p != move.vanish[0].p) {
111 this.material[move.appear[0].c][move.appear[0].p]--;
112 this.material[move.appear[0].c][V.PAWN]++;
113 }
114 if (move.vanish.length == 2 && move.appear.length == 1)
115 this.material[move.vanish[1].c][move.vanish[1].p]++;
116 }
117
118 getCurrentScore() {
119 if (this.atLeastOneMove()) {
120 // Game not over?
121 const color = this.turn;
122 if (
123 Object.keys(this.material[color]).some(p => {
124 return this.material[color][p] == 0;
125 })
126 ) {
127 return this.turn == "w" ? "0-1" : "1-0";
128 }
129 return "*";
130 }
131
132 return this.turn == "w" ? "0-1" : "1-0"; //NOTE: currently unreachable...
133 }
134
135 evalPosition() {
136 const color = this.turn;
137 if (
138 Object.keys(this.material[color]).some(p => {
139 return this.material[color][p] == 0;
140 })
141 ) {
142 // Very negative (resp. positive) if white (reps. black) pieces set is incomplete
143 return (color == "w" ? -1 : 1) * V.INFINITY;
144 }
145 return super.evalPosition();
146 }
147 };