Commit | Line | Data |
---|---|---|
535c464b BA |
1 | import ChessRules from "/base_rules.js"; |
2 | import PiPo from "/utils/PiPo.js"; | |
3 | import Move from "/utils/Move.js"; | |
4 | ||
d621e620 BA |
5 | export 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 | }; |