Some bugs fixes
[xogo.git] / variants / Hex / class.js
CommitLineData
535c464b
BA
1import ChessRules from "/base_rules.js";
2import PiPo from "/utils/PiPo.js";
3import Move from "/utils/Move.js";
4
d621e620
BA
5export default class HexRules extends ChessRules {
6
7 static get Options() {
8 return {
9 input: [
10 {
11 label: "Board size",
535c464b 12 variable: "bsize",
d621e620 13 type: "number",
535c464b
BA
14 defaut: 11
15 },
d621e620
BA
16 {
17 label: "Swap",
535c464b
BA
18 variable: "swap",
19 type: "checkbox",
20 defaut: true
d621e620
BA
21 }
22 ]
23 };
24 }
25
535c464b
BA
26 get hasFlags() {
27 return false;
28 }
29 get hasEnpassant() {
30 return false;
31 }
d621e620
BA
32 get hasReserve() {
33 return false;
34 }
35
36 get noAnimate() {
37 return true;
38 }
39
40 doClick(coords) {
41 if (
535c464b
BA
42 this.playerColor != this.turn ||
43 (
44 this.board[coords.x][coords.y] != "" &&
45 (!this.options["swap"] || this.movesCount >= 2)
46 )
d621e620
BA
47 ) {
48 return null;
49 }
50 let res = new Move({
51 start: {x: coords.x, y: coords.y},
52 appear: [
53 new PiPo({
54 x: coords.x,
55 y: coords.y,
56 c: this.turn,
57 p: 'p'
58 })
59 ],
60 vanish: []
61 });
62 if (this.board[coords.x][coords.y] != "") {
63 res.vanish.push(
64 new PiPo({
65 x: coords.x,
66 y: coords.y,
67 c: C.GetOppCol(this.turn),
68 p: 'p'
69 })
70 );
71 }
72 return res;
73 }
74
75 genRandInitFen() {
76 // NOTE: size.x == size.y (square boards)
535c464b
BA
77 const emptyCount = C.FenEmptySquares(this.size.x);
78 return (emptyCount + "/").repeat(this.size.x).slice(0, -1) + " w 0";
d621e620
BA
79 }
80
a1b45f4c 81 // TODO: enable vertical board (rotate?)
728cb1e3 82 getSvgChessboard() {
535c464b
BA
83 // NOTE: with small margin seems nicer
84 let width = 173.2 * this.size.y + 173.2 * (this.size.y-1) / 2 + 30,
85 height = 50 + Math.floor(150 * this.size.x) + 30,
86 min_x = -86.6 - 15,
87 min_y = -100 - 15;
a1b45f4c 88// if (this.size.ratio < 1) {
535c464b
BA
89// [width, height] = [height, width];
90// [min_x, min_y] = [min_y, min_x];
91// }
728cb1e3
BA
92 let board = `
93 <svg
535c464b
BA
94 viewBox="${min_x} ${min_y} ${width} ${height}"
95 class="chessboard_SVG"`;
a1b45f4c 96// if (this.size.ratio < 1)
535c464b
BA
97// board += ` transform="rotate(90 ${min_x} ${min_y})"`
98 board += `>
728cb1e3
BA
99 <defs>
100 <g id="hexa">
101 <polygon
535c464b 102 style="stroke:#000000;stroke-width:1"
728cb1e3
BA
103 points="0,-100.0 86.6,-50.0 86.6,50.0 0,100.0 -86.6,50.0 -86.6,-50.0"
104 />
105 </g>
106 </defs>`;
535c464b 107 board += "<g>";
728cb1e3
BA
108 for (let i=0; i < this.size.x; i++) {
109 for (let j=0; j < this.size.y; j++) {
535c464b
BA
110 board += `
111 <use
112 href="#hexa"
113 class="neutral-square"
114 id="${this.coordsToId({x: i, y: j})}"
115 x="${173.2*j + 86.6*i}"
116 y="${150*i}"
117 />`;
728cb1e3
BA
118 }
119 }
535c464b
BA
120 board += `</g><g style="fill:none;stroke-width:10">`;
121 // Goals: up/down/left/right
122 board += `<polyline style="stroke:red" points="`
123 for (let i=0; i<=2*this.size.y; i++)
124 board += ((i-1)*86.6) + ',' + (i % 2 == 0 ? -50 : -100) + ' ';
125 board += `"/><polyline style="stroke:red" points="`;
126 for (let i=1; i<=2*this.size.y; i++) {
127 const jShift = 200 * Math.floor((this.size.y+1)/2) +
128 100 * (Math.floor(this.size.y/2) - 1) +
129 (i % 2 == 0 ? -50 : 0) +
130 (this.size.y % 2 == 0 ? 50 : 0);
131 board += ((i+this.size.y-2)*86.6) + ',' + jShift + ' ';
132 }
133 board += `"/><polyline style="stroke:blue" points="`;
134 let sumY = -100;
135 for (let i=0; i<=2*this.size.x; i++) {
136 board += ((Math.floor(i/2)-1) * 86.6) + ',' +
137 (sumY += (i % 2 == 0 ? 50 : 100)) + ' ';
138 }
139 board += `"/><polyline style="stroke:blue" points="`;
140 sumY = -100;
141 for (let i=0; i<2*this.size.x; i++) {
142 board += (173.2*this.size.x + (Math.floor(i/2)-1) * 86.6) + ',' +
143 (sumY += (i % 2 == 0 ? 50 : 100)) + ' ';
144 }
145 board += `"/></g></svg>`;
728cb1e3
BA
146 return board;
147 }
148
d621e620 149 setupPieces() {
535c464b
BA
150 for (let i=0; i<this.size.x; i++) {
151 for (let j=0; j<this.size.y; j++) {
152 if (this.board[i][j] != "") {
153 const sqColor = (this.getColor(i, j) == 'w' ? "white" : "black");
154 const elt = document.getElementById(this.coordsToId({x: i, y: j}));
155 elt.classList.remove("neutral-square");
156 elt.classList.add("bg-" + sqColor);
157 }
158 }
159 }
d621e620
BA
160 }
161
162 initMouseEvents() {
163 const mousedown = (e) => {
164 if (e.touches && e.touches.length > 1)
165 e.preventDefault();
166 const cd = this.idToCoords(e.target.id);
167 if (cd) {
168 const move = this.doClick(cd);
169 if (move)
170 this.playPlusVisual(move);
171 }
172 };
173
437dfd42 174 if ('onmousedown' in window) {
d621e620 175 document.addEventListener("mousedown", mousedown);
437dfd42
BA
176 document.addEventListener("wheel",
177 (e) => this.rescale(e.deltaY < 0 ? "up" : "down"));
178 }
d621e620
BA
179 if ('ontouchstart' in window)
180 document.addEventListener("touchstart", mousedown, {passive: false});
181 }
182
a1b45f4c 183 // TODO: enable rotation
d621e620 184 get size() {
535c464b 185 const baseRatio = 1.619191; // 2801.2 / 1730, "widescreen"
a1b45f4c 186 const rotate = false; //window.innerWidth < window.innerHeight; //"vertical screen"
d621e620 187 return {
535c464b
BA
188 x: this.options["bsize"],
189 y: this.options["bsize"],
a1b45f4c 190 ratio: (rotate ? 1 / baseRatio : baseRatio)
d621e620
BA
191 };
192 }
193
194 play(move) {
535c464b
BA
195 this.playOnBoard(move);
196 this.movesCount++;
197 this.turn = C.GetOppCol(this.turn);
d621e620
BA
198 }
199
d621e620
BA
200 getCurrentScore(move) {
201 const oppCol = C.GetOppCol(this.turn);
535c464b
BA
202 // Search for connecting path of opp color:
203 let explored = {}, component;
204 let min, max;
205 const getIndex = (x, y) => x + "." + y;
206 // Explore one connected component:
207 const neighborsSearch = ([x, y], index) => {
208 // Let's say "white" connects on x and "black" on y
209 const z = (oppCol == 'w' ? x : y);
210 if (z < min)
211 min = z;
212 if (z > max)
213 max = z;
214 explored[index] = true;
215 component[index] = true;
216 for (let [dx, dy] of super.pieces()['k'].moves[0].steps) {
217 const [nx, ny] = [x + dx, y + dy];
218 const nidx = getIndex(nx, ny);
219 if (
220 this.onBoard(nx, ny) &&
221 this.getColor(nx, ny) == oppCol &&
222 !component[nidx]
223 ) {
224 neighborsSearch([nx, ny], nidx);
225 }
226 }
227 };
228 // Explore all components:
229 for (let i=0; i<this.size.x; i++) {
230 for (let j=0; j<this.size.y; j++) {
231 const index = getIndex(i, j);
232 if (this.getColor(i, j) == oppCol && !explored[index]) {
233 component = {};
234 [min, max] = [this.size.x, 0];
235 neighborsSearch([i, j], index);
236 if (max - min == this.size.x - 1)
237 return (oppCol == "w" ? "1-0" : "0-1");
238 }
239 }
240 }
d621e620
BA
241 return "*";
242 }
243
244 playVisual(move) {
245 move.vanish.forEach(v => {
535c464b
BA
246 let elt = document.getElementById(this.coordsToId({x: v.x, y: v.y}));
247 elt.classList.remove("bg-" + (v.c == 'w' ? "white" : "black"));
d621e620
BA
248 });
249 move.appear.forEach(a => {
535c464b
BA
250 let elt = document.getElementById(this.coordsToId({x: a.x, y: a.y}));
251 elt.classList.add("bg-" + (a.c == 'w' ? "white" : "black"));
d621e620 252 });
728cb1e3
BA
253 }
254
d621e620 255};