Add Hamilton Chess
[vchess.git] / client / src / variants / Hamilton.js
1 import { ChessRules, Move, PiPo } from "@/base_rules";
2 import { randInt } from "@/utils/alea";
3
4 export class HamiltonRules extends ChessRules {
5 static get HasFlags() {
6 return false;
7 }
8
9 static get HasEnpassant() {
10 return false;
11 }
12
13 static get HOLE() {
14 return "xx";
15 }
16
17 static board2fen(b) {
18 if (b[0] == 'x') return 'x';
19 return ChessRules.board2fen(b);
20 }
21
22 static fen2board(f) {
23 if (f == 'x') return V.HOLE;
24 return ChessRules.fen2board(f);
25 }
26
27 getPpath(b) {
28 if (b[0] == 'x') return "Hamilton/hole";
29 return b;
30 }
31
32 static get PIECES() {
33 return [ChessRules.KNIGHT];
34 }
35
36 static IsGoodPosition(position) {
37 if (position.length == 0) return false;
38 const rows = position.split("/");
39 if (rows.length != V.size.x) return false;
40 for (let row of rows) {
41 let sumElts = 0;
42 for (let i = 0; i < row.length; i++) {
43 if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++;
44 else {
45 const num = parseInt(row[i]);
46 if (isNaN(num)) return false;
47 sumElts += num;
48 }
49 }
50 if (sumElts != V.size.y) return false;
51 }
52 return true;
53 }
54
55 static GenRandInitFen() {
56 return "8/8/8/8/8/8/8/8 w 0";
57 }
58
59 canIplay(side, [x, y]) {
60 return side == this.turn;
61 }
62
63 // Initiate the game by choosing a square for the knight:
64 doClick(square) {
65 if (this.movesCount > 0) return null;
66 return new Move({
67 appear: [
68 new PiPo({ x: square[0], y: square[1], c: 'w', p: V.KNIGHT })
69 ],
70 vanish: [],
71 start: { x: -1, y: -1 }
72 });
73 }
74
75 getAllPotentialMoves() {
76 if (this.movesCount == 0) {
77 return [...Array(64).keys()].map(k => {
78 const i = k % 8;
79 const j = (k - i) / 8;
80 return new Move({
81 appear: [
82 new PiPo({ x: i, y: j, c: 'w', p: V.KNIGHT })
83 ],
84 vanish: [],
85 start: { x: -1, y: -1 }
86 });
87 });
88 }
89 for (let i=0; i<8; i++) {
90 for (let j=0; j<8; j++) {
91 if (!([V.EMPTY, V.HOLE].includes(this.board[i][j])))
92 return this.getPotentialKnightMoves([i, j]);
93 }
94 }
95 return [];
96 }
97
98 getPotentialKnightMoves([x, y]) {
99 return (
100 V.steps[V.KNIGHT].filter(
101 s => {
102 const [i, j] = [x + s[0], y + s[1]];
103 return (V.OnBoard(i, j) && this.board[i][j] != V.HOLE);
104 }
105 ).map(s => {
106 return this.getBasicMove([x, y], [x + s[0], y + s[1]]);
107 })
108 );
109 }
110
111 atLeastOneMove() {
112 if (this.movesCount == 0) return true;
113 for (let i=0; i<8; i++) {
114 for (let j=0; j<8; j++) {
115 if (!([V.EMPTY, V.HOLE].includes(this.board[i][j])))
116 return this.getPotentialKnightMoves([i, j]).length > 0;
117 }
118 }
119 return false;
120 }
121
122 filterValid(moves) {
123 return moves;
124 }
125
126 static PlayOnBoard(board, move) {
127 if (move.vanish.length > 0)
128 board[move.vanish[0].x][move.vanish[0].y] = V.HOLE;
129 for (let psq of move.appear) board[psq.x][psq.y] = psq.c + psq.p;
130 }
131
132 getCheckSquares() {
133 return [];
134 }
135
136 getCurrentScore() {
137 if (this.atLeastOneMove()) return "*";
138 // No valid move: I lose
139 return this.turn == "w" ? "0-1" : "1-0";
140 }
141
142 getComputerMove() {
143 const moves = this.getAllValidMoves();
144 // Just a random mover for now...
145 return moves[randInt(moves.length)];
146 }
147
148 getNotation(move) {
149 if (move.vanish.length > 0) return super.getNotation(move);
150 // First game move:
151 return "N@" + V.CoordsToSquare(move.end);
152 }
153 };