Cleaner fen generation + first draft of Apocalypse + a few 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 }
d621e620
BA
35 get noAnimate() {
36 return true;
37 }
6b9320bb
BA
38 get clickOnly() {
39 return true;
40 }
d621e620
BA
41
42 doClick(coords) {
43 if (
535c464b
BA
44 this.playerColor != this.turn ||
45 (
46 this.board[coords.x][coords.y] != "" &&
47 (!this.options["swap"] || this.movesCount >= 2)
48 )
d621e620
BA
49 ) {
50 return null;
51 }
52 let res = new Move({
53 start: {x: coords.x, y: coords.y},
54 appear: [
55 new PiPo({
56 x: coords.x,
57 y: coords.y,
58 c: this.turn,
59 p: 'p'
60 })
61 ],
62 vanish: []
63 });
64 if (this.board[coords.x][coords.y] != "") {
65 res.vanish.push(
66 new PiPo({
67 x: coords.x,
68 y: coords.y,
69 c: C.GetOppCol(this.turn),
70 p: 'p'
71 })
72 );
73 }
74 return res;
75 }
76
f31de5e4 77 genRandInitBaseFen() {
d621e620 78 // NOTE: size.x == size.y (square boards)
535c464b 79 const emptyCount = C.FenEmptySquares(this.size.x);
f31de5e4
BA
80 return {
81 fen: (emptyCount + "/").repeat(this.size.x).slice(0, -1) + " w 0",
82 o: {}
83 };
d621e620
BA
84 }
85
728cb1e3 86 getSvgChessboard() {
535c464b
BA
87 // NOTE: with small margin seems nicer
88 let width = 173.2 * this.size.y + 173.2 * (this.size.y-1) / 2 + 30,
89 height = 50 + Math.floor(150 * this.size.x) + 30,
90 min_x = -86.6 - 15,
91 min_y = -100 - 15;
fcede3ef
BA
92 if (this.size.ratio < 1) {
93 // Rotate by 30 degrees to display vertically
94 [width, height] = [height, width];
95 [min_x, min_y] = [min_y, min_x];
96 }
728cb1e3
BA
97 let board = `
98 <svg
535c464b 99 viewBox="${min_x} ${min_y} ${width} ${height}"
fcede3ef 100 class="chessboard_SVG">
728cb1e3
BA
101 <defs>
102 <g id="hexa">
103 <polygon
535c464b 104 style="stroke:#000000;stroke-width:1"
728cb1e3
BA
105 points="0,-100.0 86.6,-50.0 86.6,50.0 0,100.0 -86.6,50.0 -86.6,-50.0"
106 />
107 </g>
108 </defs>`;
fcede3ef
BA
109 board += "<g";
110 if (this.size.ratio < 1)
111 board += ` transform="rotate(30)"`
112 board += ">";
728cb1e3
BA
113 for (let i=0; i < this.size.x; i++) {
114 for (let j=0; j < this.size.y; j++) {
535c464b
BA
115 board += `
116 <use
117 href="#hexa"
118 class="neutral-square"
119 id="${this.coordsToId({x: i, y: j})}"
120 x="${173.2*j + 86.6*i}"
121 y="${150*i}"
122 />`;
728cb1e3
BA
123 }
124 }
fcede3ef
BA
125 board += `</g><g style="fill:none;stroke-width:10"`;
126 if (this.size.ratio < 1)
127 board += ` transform="rotate(30)"`
535c464b 128 // Goals: up/down/left/right
fcede3ef 129 board += `><polyline style="stroke:red" points="`
535c464b
BA
130 for (let i=0; i<=2*this.size.y; i++)
131 board += ((i-1)*86.6) + ',' + (i % 2 == 0 ? -50 : -100) + ' ';
132 board += `"/><polyline style="stroke:red" points="`;
133 for (let i=1; i<=2*this.size.y; i++) {
134 const jShift = 200 * Math.floor((this.size.y+1)/2) +
135 100 * (Math.floor(this.size.y/2) - 1) +
136 (i % 2 == 0 ? -50 : 0) +
137 (this.size.y % 2 == 0 ? 50 : 0);
138 board += ((i+this.size.y-2)*86.6) + ',' + jShift + ' ';
139 }
140 board += `"/><polyline style="stroke:blue" points="`;
141 let sumY = -100;
142 for (let i=0; i<=2*this.size.x; i++) {
143 board += ((Math.floor(i/2)-1) * 86.6) + ',' +
144 (sumY += (i % 2 == 0 ? 50 : 100)) + ' ';
145 }
146 board += `"/><polyline style="stroke:blue" points="`;
147 sumY = -100;
148 for (let i=0; i<2*this.size.x; i++) {
149 board += (173.2*this.size.x + (Math.floor(i/2)-1) * 86.6) + ',' +
150 (sumY += (i % 2 == 0 ? 50 : 100)) + ' ';
151 }
152 board += `"/></g></svg>`;
728cb1e3
BA
153 return board;
154 }
155
d621e620 156 setupPieces() {
535c464b
BA
157 for (let i=0; i<this.size.x; i++) {
158 for (let j=0; j<this.size.y; j++) {
159 if (this.board[i][j] != "") {
160 const sqColor = (this.getColor(i, j) == 'w' ? "white" : "black");
161 const elt = document.getElementById(this.coordsToId({x: i, y: j}));
162 elt.classList.remove("neutral-square");
163 elt.classList.add("bg-" + sqColor);
164 }
165 }
166 }
d621e620
BA
167 }
168
d621e620 169 get size() {
fcede3ef 170 const baseRatio = 1.6191907514450865; //2801.2 / 1730, "widescreen"
5abaabb3 171 const rc =
f31de5e4 172 document.getElementById(this.containerId).getBoundingClientRect();
5abaabb3 173 const rotate = rc.width < rc.height; //"vertical screen"
d621e620 174 return {
535c464b
BA
175 x: this.options["bsize"],
176 y: this.options["bsize"],
a1b45f4c 177 ratio: (rotate ? 1 / baseRatio : baseRatio)
d621e620
BA
178 };
179 }
180
181 play(move) {
535c464b
BA
182 this.playOnBoard(move);
183 this.movesCount++;
184 this.turn = C.GetOppCol(this.turn);
d621e620
BA
185 }
186
d621e620
BA
187 getCurrentScore(move) {
188 const oppCol = C.GetOppCol(this.turn);
535c464b
BA
189 // Search for connecting path of opp color:
190 let explored = {}, component;
191 let min, max;
192 const getIndex = (x, y) => x + "." + y;
193 // Explore one connected component:
194 const neighborsSearch = ([x, y], index) => {
195 // Let's say "white" connects on x and "black" on y
196 const z = (oppCol == 'w' ? x : y);
197 if (z < min)
198 min = z;
199 if (z > max)
200 max = z;
201 explored[index] = true;
202 component[index] = true;
203 for (let [dx, dy] of super.pieces()['k'].moves[0].steps) {
204 const [nx, ny] = [x + dx, y + dy];
205 const nidx = getIndex(nx, ny);
206 if (
207 this.onBoard(nx, ny) &&
208 this.getColor(nx, ny) == oppCol &&
209 !component[nidx]
210 ) {
211 neighborsSearch([nx, ny], nidx);
212 }
213 }
214 };
215 // Explore all components:
216 for (let i=0; i<this.size.x; i++) {
217 for (let j=0; j<this.size.y; j++) {
218 const index = getIndex(i, j);
219 if (this.getColor(i, j) == oppCol && !explored[index]) {
220 component = {};
221 [min, max] = [this.size.x, 0];
222 neighborsSearch([i, j], index);
223 if (max - min == this.size.x - 1)
224 return (oppCol == "w" ? "1-0" : "0-1");
225 }
226 }
227 }
d621e620
BA
228 return "*";
229 }
230
231 playVisual(move) {
232 move.vanish.forEach(v => {
535c464b
BA
233 let elt = document.getElementById(this.coordsToId({x: v.x, y: v.y}));
234 elt.classList.remove("bg-" + (v.c == 'w' ? "white" : "black"));
d621e620
BA
235 });
236 move.appear.forEach(a => {
535c464b
BA
237 let elt = document.getElementById(this.coordsToId({x: a.x, y: a.y}));
238 elt.classList.add("bg-" + (a.c == 'w' ? "white" : "black"));
d621e620 239 });
728cb1e3
BA
240 }
241
d621e620 242};