Fix Konane and Yote computer play + a few bugs
[vchess.git] / client / src / variants / Konane.js
CommitLineData
d2af3400 1import { ChessRules, Move, PiPo } from "@/base_rules";
c11afcdf 2import { randInt } from "@/utils/alea";
d2af3400 3
d2af3400
BA
4export class KonaneRules extends ChessRules {
5
6 static get HasFlags() {
7 return false;
8 }
9
10 static get HasEnpassant() {
11 return false;
12 }
13
cbe95378
BA
14 static get ReverseColors() {
15 return true;
16 }
17
d2af3400
BA
18 static get PIECES() {
19 return V.PAWN;
20 }
21
cbe95378
BA
22 getPiece() {
23 return V.PAWN;
24 }
25
d2af3400
BA
26 getPpath(b) {
27 return "Konane/" + b;
28 }
29
30 static IsGoodPosition(position) {
31 if (position.length == 0) return false;
32 const rows = position.split("/");
33 if (rows.length != V.size.x) return false;
34 for (let row of rows) {
35 let sumElts = 0;
36 for (let i = 0; i < row.length; i++) {
37 if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
38 else {
39 const num = parseInt(row[i], 10);
40 if (isNaN(num) || num <= 0) return false;
41 sumElts += num;
42 }
43 }
44 if (sumElts != V.size.y) return false;
45 }
46 return true;
47 }
48
49 static GenRandInitFen() {
50 return (
51 "PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP/" +
52 "PpPpPpPp/pPpPpPpP/PpPpPpPp/pPpPpPpP w 0"
53 );
54 }
55
56 setOtherVariables(fen) {
57 this.captures = []; //reinit for each move
58 }
59
60 hoverHighlight(x, y) {
61 if (this.movesCount >= 2) return false;
62 const c = this.turn;
63 if (c == 'w') return (x == y && [0, 3, 4, 7].includes(x));
64 // "Black": search for empty square and allow nearby
65 for (let i of [0, 3, 4, 7]) {
66 if (this.board[i][i] == V.EMPTY)
67 return (Math.abs(x - i) + Math.abs(y - i) == 1)
68 }
69 }
70
71 onlyClick([x, y]) {
72 return (
73 this.movesCount <= 1 ||
74 // TODO: next line theoretically shouldn't be required...
75 (this.movesCount == 2 && this.getColor(x, y) != this.turn)
76 );
77 }
78
79 doClick([x, y]) {
80 if (this.movesCount >= 2) return null;
81 const color = this.turn;
82 if (color == 'w') {
83 if (x != y || ![0, 3, 4, 7].includes(x)) return null;
84 return new Move({
85 appear: [],
86 vanish: [ new PiPo({ x: x, y: y, c: color, p: V.PAWN }) ],
87 end: { x: x, y: y }
88 });
89 }
90 // "Black": search for empty square and allow nearby
91 for (let i of [0, 3, 4, 7]) {
92 if (this.board[i][i] == V.EMPTY) {
93 if (Math.abs(x - i) + Math.abs(y - i) != 1) return null;
94 return new Move({
95 appear: [],
96 vanish: [ new PiPo({ x: x, y: y, c: color, p: V.PAWN }) ],
97 end: { x: x, y: y }
98 });
99 }
100 }
101 }
102
103 getPotentialMovesFrom([x, y]) {
104 if (this.movesCount <= 1) {
105 const mv = this.doClick([x, y]);
106 return (!!mv ? [mv] : []);
107 }
108 const L = this.captures.length;
109 const c = (L > 0 ? this.captures[L-1] : null);
110 const color = this.turn;
111 const oppCol = V.GetOppCol(color);
112 let step = null;
113 let moves = [];
114 if (!!c) {
115 if (x != c.end.x || y != c.end.y) return [];
116 step = [(c.end.x - c.start.x) / 2, (c.end.y - c.start.y) / 2];
117 // Add move to adjacent empty square to mark "end of capture"
118 moves.push(
119 new Move({
120 appear: [],
121 vanish: [],
122 start: { x: x, y: y },
123 end: { x: x - step[0], y: y - step[1] }
124 })
125 );
126 }
127 // Examine captures from here
128 for (let s of (!!step ? [step] : V.steps[V.ROOK])) {
129 let [i, j] = [x + 2*s[0], y + 2*s[1]];
130 if (
131 !!c || //avoid redundant checks if continuation
132 (
133 V.OnBoard(i, j) &&
134 this.board[i][j] == V.EMPTY &&
135 this.board[i - s[0]][j - s[1]] != V.EMPTY &&
136 this.getColor(i - s[0], j - s[1]) == oppCol
137 )
138 ) {
139 let mv = new Move({
140 appear: [
141 new PiPo({ x: i, y: j, c: color, p: V.PAWN })
142 ],
143 vanish: [
144 new PiPo({ x: x, y: y, c: color, p: V.PAWN }),
145 new PiPo({ x: i - s[0], y: j - s[1], c: oppCol, p: V.PAWN })
146 ]
147 });
148 // Is there another capture possible then?
149 [i, j] = [i + 2*s[0], j + 2*s[1]];
150 if (
151 V.OnBoard(i, j) &&
152 this.board[i][j] == V.EMPTY &&
153 this.board[i - s[0]][j - s[1]] != V.EMPTY &&
154 this.getColor(i - s[0], j - s[1]) == oppCol
155 ) {
156 mv.end.moreCapture = true;
157 }
158 moves.push(mv);
159 }
160 }
161 return moves;
162 }
163
164 filterValid(moves) {
165 return moves;
166 }
167
168 getCheckSquares() {
169 return [];
170 }
171
172 getCurrentScore() {
173 if (this.atLeastOneMove()) return "*";
174 return (this.turn == "w" ? "0-1" : "1-0");
175 }
176
177 play(move) {
178 V.PlayOnBoard(this.board, move);
179 if (!move.end.moreCapture) {
180 this.turn = V.GetOppCol(this.turn);
181 this.movesCount++;
182 this.captures = [];
183 }
184 else {
185 this.captures.push(
186 {
187 start: move.start,
188 end: { x: move.end.x, y: move.end.y }
189 }
190 );
191 }
192 }
193
194 undo(move) {
195 V.UndoOnBoard(this.board, move);
196 if (!move.end.moreCapture) {
197 this.turn = V.GetOppCol(this.turn);
198 this.movesCount--;
199 }
200 else this.captures.pop();
201 }
202
c11afcdf
BA
203 getComputerMove() {
204 const color = this.turn;
205 let mvArray = [];
206 let mv = null;
207 const undoAll = () => {
208 for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]);
209 };
210 // Just play random moves (for now at least. TODO?)
211 while (this.turn == color) {
212 let moves = super.getAllValidMoves();
213 if (moves.length == 0) {
214 // Shouldn't happen, but...
215 undoAll();
216 return null;
217 }
218 mv = moves[randInt(moves.length)];
219 mvArray.push(mv);
220 this.play(mv);
221 }
222 undoAll();
223 return (mvArray.length > 1 ? mvArray : mvArray[0]);
d2af3400
BA
224 }
225
226 getNotation(move) {
227 if (this.movesCount <= 1) return V.CoordsToSquare(move.start) + "X";
228 if (move.vanish.length == 0) return "end";
229 return V.CoordsToSquare(move.start) + "x" + V.CoordsToSquare(move.end);
230 }
231
232};