Experimental symmetric randomness + deterministic option
[vchess.git] / client / src / variants / Arena.js
1 import { ChessRules } from "@/base_rules";
2
3 export const VariantRules = class ArenaRules extends ChessRules {
4 static get hasFlags() {
5 return false;
6 }
7
8 static GenRandInitFen(randomness) {
9 return ChessRules
10 .GenRandInitFen(randomness)
11 .replace("w 0 1111 -", "w 0 -");
12 }
13
14 static InArena(x) {
15 return Math.abs(3.5 - x) <= 1.5;
16 }
17
18 getPotentialMovesFrom([x, y]) {
19 const moves = super.getPotentialMovesFrom([x, y]);
20 // Eliminate moves which neither enter the arena or capture something
21 return moves.filter(m => {
22 const startInArena = V.InArena(m.start.x);
23 const endInArena = V.InArena(m.end.x);
24 return (
25 (startInArena && endInArena && m.vanish.length == 2) ||
26 (!startInArena && endInArena)
27 );
28 });
29
30 return moves;
31 }
32
33 getPotentialPawnMoves([x, y]) {
34 const color = this.turn;
35 let moves = [];
36 const [sizeX, sizeY] = [V.size.x, V.size.y];
37 const shiftX = color == "w" ? -1 : 1;
38 const startRank = color == "w" ? sizeX - 2 : 1;
39
40 if (this.board[x + shiftX][y] == V.EMPTY) {
41 // One square forward
42 moves.push(this.getBasicMove([x, y], [x + shiftX, y]));
43 // Next condition because pawns on 1st rank can generally jump
44 if (
45 x == startRank &&
46 this.board[x + 2 * shiftX][y] == V.EMPTY
47 ) {
48 // Two squares jump
49 moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y]));
50 }
51 }
52 // Captures: also possible backward
53 for (let shiftY of [-1, 1]) {
54 if (y + shiftY >= 0 && y + shiftY < sizeY) {
55 for (let direction of [-1,1]) {
56 if (
57 this.board[x + direction][y + shiftY] != V.EMPTY &&
58 this.canTake([x, y], [x + direction, y + shiftY])
59 ) {
60 moves.push(this.getBasicMove([x, y], [x + direction, y + shiftY]));
61 }
62 }
63 }
64 }
65
66 // En passant
67 const Lep = this.epSquares.length;
68 const epSquare = this.epSquares[Lep - 1]; //always at least one element
69 if (
70 !!epSquare &&
71 epSquare.x == x + shiftX &&
72 Math.abs(epSquare.y - y) == 1
73 ) {
74 let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]);
75 enpassantMove.vanish.push({
76 x: x,
77 y: epSquare.y,
78 p: "p",
79 c: this.getColor(x, epSquare.y)
80 });
81 moves.push(enpassantMove);
82 }
83
84 return moves;
85 }
86
87 getPotentialQueenMoves(sq) {
88 return this.getSlideNJumpMoves(
89 sq,
90 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
91 ).filter(m => {
92 // Filter out moves longer than 3 squares
93 return Math.max(
94 Math.abs(m.end.x - m.start.x),
95 Math.abs(m.end.y - m.start.y)) <= 3;
96 });
97 }
98
99 getPotentialKingMoves(sq) {
100 return this.getSlideNJumpMoves(
101 sq,
102 V.steps[V.ROOK].concat(V.steps[V.BISHOP])
103 ).filter(m => {
104 // Filter out moves longer than 3 squares
105 return Math.max(
106 Math.abs(m.end.x - m.start.x),
107 Math.abs(m.end.y - m.start.y)) <= 3;
108 });
109 }
110
111 getCheckSquares() {
112 return [];
113 }
114
115 filterValid(moves) {
116 // No check conditions
117 return moves;
118 }
119
120 getCurrentScore() {
121 const color = this.turn;
122 if (!this.atLeastOneMove())
123 // I cannot move anymore
124 return color == "w" ? "0-1" : "1-0";
125 // Win if the opponent has no more pieces left (in the Arena),
126 // (and/)or if he lost both his dukes.
127 let someUnitRemain = false;
128 let atLeastOneDuke = false;
129 let somethingInArena = false;
130 outerLoop: for (let i=0; i<V.size.x; i++) {
131 for (let j=0; j<V.size.y; j++) {
132 if (this.getColor(i,j) == color) {
133 someUnitRemain = true;
134 if (this.movesCount >= 2 && V.InArena(i)) {
135 somethingInArena = true;
136 if (atLeastOneDuke)
137 break outerLoop;
138 }
139 if ([V.QUEEN,V.KING].includes(this.getPiece(i,j))) {
140 atLeastOneDuke = true;
141 if (this.movesCount < 2 || somethingInArena)
142 break outerLoop;
143 }
144 }
145 }
146 }
147 if (
148 !someUnitRemain ||
149 !atLeastOneDuke ||
150 (this.movesCount >= 2 && !somethingInArena)
151 ) {
152 return color == "w" ? "0-1" : "1-0";
153 }
154 return "*";
155 }
156 };