Add 'Connect' variant
[vchess.git] / client / src / variants / Connect.js
1 import { ChessRules, Move, PiPo } from "@/base_rules";
2
3 export class ConnectRules extends ChessRules {
4
5 static GenRandInitFen(randomness) {
6 const baseFen = ChessRules.GenRandInitFen(Math.min(randomness, 1));
7 return "4k3/8" + baseFen.substring(17, 50) + " -";
8 }
9
10 getReservePpath() {
11 return "bp";
12 }
13
14 static get RESERVE_PIECES() {
15 return [V.PAWN]; //only black pawns
16 }
17
18 getColor(i, j) {
19 if (i >= V.size.x) return "b";
20 return this.board[i][j].charAt(0);
21 }
22
23 getPiece(i, j) {
24 if (i >= V.size.x) return V.PAWN;
25 return this.board[i][j].charAt(1);
26 }
27
28 static IsGoodFlags(flags) {
29 // Only white can castle
30 return !!flags.match(/^[a-z]{2,2}$/);
31 }
32
33 getFlagsFen() {
34 return this.castleFlags['w'].map(V.CoordToColumn).join("");
35 }
36
37 setFlags(fenflags) {
38 this.castleFlags = { 'w': [-1, -1] };
39 for (let i = 0; i < 2; i++)
40 this.castleFlags['w'][i] = V.ColumnToCoord(fenflags.charAt(i));
41 }
42
43 setOtherVariables(fen) {
44 super.setOtherVariables(fen);
45 this.reserve = { b: { [V.PAWN]: 1 } };
46 }
47
48 getReserveMoves() {
49 if (this.turn != 'b') return [];
50 let moves = [];
51 for (let i = 1; i <= 6; i++) {
52 for (let j = 0; j < V.size.y; j++) {
53 if (this.board[i][j] == V.EMPTY) {
54 let mv = new Move({
55 appear: [
56 new PiPo({
57 x: i,
58 y: j,
59 c: 'b',
60 p: 'p'
61 })
62 ],
63 vanish: [],
64 start: { x: 9, y: 0 },
65 end: { x: i, y: j }
66 });
67 moves.push(mv);
68 }
69 }
70 }
71 return moves;
72 }
73
74 getPotentialMovesFrom(sq) {
75 if (sq[0] >= V.size.x) return this.getReserveMoves();
76 return super.getPotentialMovesFrom(sq);
77 }
78
79 getPotentialKingMoves([x, y]) {
80 if (this.getColor(x, y) == 'w') return super.getPotentialKingMoves([x, y]);
81 // Black doesn't castle:
82 return super.getSlideNJumpMoves(
83 [x, y],
84 V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
85 "oneStep"
86 );
87 }
88
89 getAllValidMoves() {
90 return (
91 super.getAllValidMoves().concat(
92 super.filterValid(this.getReserveMoves()))
93 );
94 }
95
96 atLeastOneMove() {
97 if (super.atLeastOneMove()) return true;
98 // Search one reserve move
99 if (this.filterValid(this.getReserveMoves()).length > 0) return true;
100 return false;
101 }
102
103 updateCastleFlags(move, piece) {
104 // Only white can castle:
105 const firstRank = 7;
106 if (piece == V.KING && move.appear[0].c == 'w')
107 this.castleFlags['w'] = [8, 8];
108 else if (
109 move.start.x == firstRank &&
110 this.castleFlags['w'].includes(move.start.y)
111 ) {
112 const flagIdx = (move.start.y == this.castleFlags['w'][0] ? 0 : 1);
113 this.castleFlags['w'][flagIdx] = 8;
114 }
115 else if (
116 move.end.x == firstRank &&
117 this.castleFlags['w'].includes(move.end.y)
118 ) {
119 const flagIdx = (move.end.y == this.castleFlags['w'][0] ? 0 : 1);
120 this.castleFlags['w'][flagIdx] = 8;
121 }
122 }
123
124 getCurrentScore() {
125 const score = super.getCurrentScore();
126 if (score != "*") return score;
127 // Check pawns connection:
128 for (let i = 0; i < V.size.x; i++) {
129 for (let j = 0; j < V.size.y; j++) {
130 if (
131 this.board[i][j] != V.EMPTY &&
132 this.getColor(i, j) == 'b' &&
133 this.getPiece(i, j) == V.PAWN
134 ) {
135 // Exploration "rightward + downward" is enough
136 for (let step of [[1, 0], [0, 1], [1, 1], [-1, 1]]) {
137 let [ii, jj] = [i + step[0], j + step[1]];
138 let kounter = 1;
139 while (
140 V.OnBoard(ii, jj) &&
141 this.board[ii][jj] != V.EMPTY &&
142 this.getColor(ii, jj) == 'b' &&
143 this.getPiece(ii, jj) != V.KING
144 ) {
145 kounter++;
146 ii += step[0];
147 jj += step[1];
148 }
149 if (kounter == 4) return "0-1";
150 }
151 }
152 }
153 }
154 return "*";
155 }
156
157 evalPosition() {
158 let evaluation = 0;
159 // Count white material + check pawns alignments
160 let maxAlign = 0;
161 for (let i = 0; i < V.size.x; i++) {
162 for (let j = 0; j < V.size.y; j++) {
163 if (this.board[i][j] != V.EMPTY) {
164 const piece = this.getPiece(i, j);
165 if (piece != V.KING) {
166 const color = this.getColor(i, j);
167 if (color == 'w') evaluation += V.VALUES[piece];
168 else {
169 // Exploration "rightward + downward" is enough
170 for (let step of [[1, 0], [0, 1], [1, 1], [-1, 1]]) {
171 let [ii, jj] = [i + step[0], j + step[1]];
172 let kounter = 1;
173 while (
174 V.OnBoard(ii, jj) &&
175 this.board[ii][jj] != V.EMPTY &&
176 this.getColor(ii, jj) == 'b' &&
177 this.getPiece(ii, jj) != V.KING
178 ) {
179 kounter++;
180 ii += step[0];
181 jj += step[1];
182 }
183 if (kounter > maxAlign) maxAlign = kounter;
184 }
185 }
186 }
187 }
188 }
189 }
190 // -1 for two aligned pawns, -3 for 3 aligned pawns.
191 if ([1, 2].includes(maxAlign)) maxAlign--;
192 return evaluation - maxAlign;
193 }
194
195 getNotation(move) {
196 if (move.vanish.length == 0) return "@" + V.CoordsToSquare(move.end);
197 return super.getNotation(move);
198 }
199
200 };
201