doubletwelve 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,39 +1,39 @@
1
- import { jsx as b, Fragment as re, jsxs as T } from "react/jsx-runtime";
2
- import { useMemo as Se, useState as Y, useEffect as ye } from "react";
3
- const me = {
1
+ import { jsx as M, Fragment as xe, jsxs as D } from "react/jsx-runtime";
2
+ import { useEffect as ne, useMemo as Ye, useState as B, useRef as ce, useCallback as Re } from "react";
3
+ const Ae = {
4
4
  "3x3": { rows: [20, 50, 80], cols: [20, 50, 80], size: "18%" },
5
5
  "3x4": { rows: [24, 50, 76], cols: [22, 40, 60, 78], size: "12%" },
6
6
  "4x3": { rows: [15, 38, 62, 85], cols: [20, 50, 80], size: "14%" }
7
7
  };
8
- function ze(e) {
9
- const o = me[e.gridSize];
8
+ function Fe(e) {
9
+ const t = Ae[e.gridSize];
10
10
  return {
11
- top: e.top ?? `${o.rows[e.row]}%`,
12
- left: e.left ?? `${o.cols[e.col]}%`,
13
- width: o.size,
14
- height: o.size
11
+ top: e.top ?? `${t.rows[e.row]}%`,
12
+ left: e.left ?? `${t.cols[e.col]}%`,
13
+ width: t.size,
14
+ height: t.size
15
15
  };
16
16
  }
17
- const $e = ({
17
+ const Le = ({
18
18
  row: e,
19
- col: o,
20
- gridSize: t,
21
- color: n,
19
+ col: t,
20
+ gridSize: n,
21
+ color: o,
22
22
  hollow: r,
23
- top: l,
24
- left: i
23
+ top: i,
24
+ left: l
25
25
  }) => {
26
- const s = ze({ row: e, col: o, gridSize: t, top: l, left: i });
27
- return /* @__PURE__ */ b(
26
+ const s = Fe({ row: e, col: t, gridSize: n, top: i, left: l });
27
+ return /* @__PURE__ */ M(
28
28
  "div",
29
29
  {
30
30
  "data-testid": "pip",
31
31
  "data-row": e,
32
- "data-col": o,
33
- "data-grid": t,
32
+ "data-col": t,
33
+ "data-grid": n,
34
34
  style: {
35
35
  position: "absolute",
36
- backgroundColor: r ? "transparent" : n,
36
+ backgroundColor: r ? "transparent" : o,
37
37
  border: r ? "2px solid #888" : void 0,
38
38
  borderRadius: "50%",
39
39
  transform: "translate(-50%, -50%)",
@@ -42,7 +42,7 @@ const $e = ({
42
42
  }
43
43
  }
44
44
  );
45
- }, Me = {
45
+ }, _e = {
46
46
  0: [],
47
47
  1: [{ row: 1, col: 1, gridSize: "3x3" }],
48
48
  2: [
@@ -145,10 +145,10 @@ const $e = ({
145
145
  { row: 3, col: 2, gridSize: "4x3" }
146
146
  ]
147
147
  };
148
- function Ce(e) {
149
- return Me[e] ?? [];
148
+ function Ne(e) {
149
+ return _e[e] ?? [];
150
150
  }
151
- const X = {
151
+ const j = {
152
152
  0: { color: "transparent" },
153
153
  1: { color: "#1a1a1a" },
154
154
  2: { color: "#8B1A1A" },
@@ -162,44 +162,44 @@ const X = {
162
162
  10: { color: "#EA580C" },
163
163
  11: { color: "#166534" },
164
164
  12: { color: "#DC2626" }
165
- }, no = X;
166
- function ro(e) {
167
- return { ...X, ...e };
165
+ }, Mt = j;
166
+ function $t(e) {
167
+ return { ...j, ...e };
168
168
  }
169
- function le(e, o) {
170
- if (o !== void 0)
171
- return o[e] ?? X[e] ?? { color: "#1a1a1a" };
169
+ function ge(e, t) {
170
+ if (t !== void 0)
171
+ return t[e] ?? j[e] ?? { color: "#1a1a1a" };
172
172
  }
173
- function lo(e) {
174
- return le(e, X);
173
+ function kt(e) {
174
+ return ge(e, j);
175
175
  }
176
- const ke = (e, o, t) => {
177
- const n = le(e, t);
178
- return n ? { color: n.color, hollow: n.hollow } : { color: o };
179
- }, De = ({
176
+ const Be = (e, t, n) => {
177
+ const o = ge(e, n);
178
+ return o ? { color: o.color, hollow: o.hollow } : { color: t };
179
+ }, je = ({
180
180
  value: e,
181
- pipColor: o,
182
- pipColors: t
181
+ pipColor: t,
182
+ pipColors: n
183
183
  }) => {
184
- const { color: n, hollow: r } = ke(e, o, t), l = Ce(e);
185
- return /* @__PURE__ */ b(re, { children: l.map((i, s) => /* @__PURE__ */ b(
186
- $e,
184
+ const { color: o, hollow: r } = Be(e, t, n), i = Ne(e);
185
+ return /* @__PURE__ */ M(xe, { children: i.map((l, s) => /* @__PURE__ */ M(
186
+ Le,
187
187
  {
188
- row: i.row,
189
- col: i.col,
190
- gridSize: i.gridSize,
191
- color: n,
188
+ row: l.row,
189
+ col: l.col,
190
+ gridSize: l.gridSize,
191
+ color: o,
192
192
  hollow: r,
193
- top: i.top,
194
- left: i.left
193
+ top: l.top,
194
+ left: l.left
195
195
  },
196
196
  s
197
197
  )) });
198
- }, ee = ({
198
+ }, de = ({
199
199
  value: e,
200
- pipColor: o,
201
- pipColors: t
202
- }) => /* @__PURE__ */ b(
200
+ pipColor: t,
201
+ pipColors: n
202
+ }) => /* @__PURE__ */ M(
203
203
  "div",
204
204
  {
205
205
  style: {
@@ -209,26 +209,26 @@ const ke = (e, o, t) => {
209
209
  padding: "0",
210
210
  overflow: "hidden"
211
211
  },
212
- children: /* @__PURE__ */ b(De, { value: e, pipColor: o, pipColors: t })
212
+ children: /* @__PURE__ */ M(je, { value: e, pipColor: t, pipColors: n })
213
213
  }
214
- ), ie = ({
214
+ ), ve = ({
215
215
  value1: e = 0,
216
- value2: o = 0,
217
- width: t = 100,
218
- height: n = 200,
216
+ value2: t = 0,
217
+ width: n = 100,
218
+ height: o = 200,
219
219
  backgroundColor: r = "white",
220
- pipColor: l = "black",
221
- pipColors: i,
220
+ pipColor: i = "black",
221
+ pipColors: l,
222
222
  borderColor: s = "black",
223
223
  rotation: a = 0
224
224
  }) => {
225
- const u = Math.min(Math.max(e, 0), 12), f = Math.min(Math.max(o, 0), 12);
226
- return /* @__PURE__ */ T(
225
+ const c = Math.min(Math.max(e, 0), 12), d = Math.min(Math.max(t, 0), 12);
226
+ return /* @__PURE__ */ D(
227
227
  "div",
228
228
  {
229
229
  style: {
230
- width: `${t}px`,
231
- height: `${n}px`,
230
+ width: `${n}px`,
231
+ height: `${o}px`,
232
232
  backgroundColor: r,
233
233
  borderColor: s,
234
234
  borderWidth: "1px",
@@ -242,7 +242,7 @@ const ke = (e, o, t) => {
242
242
  overflow: "hidden"
243
243
  },
244
244
  children: [
245
- /* @__PURE__ */ b(
245
+ /* @__PURE__ */ M(
246
246
  "div",
247
247
  {
248
248
  style: {
@@ -252,345 +252,622 @@ const ke = (e, o, t) => {
252
252
  borderBottomStyle: "solid",
253
253
  borderBottomColor: s
254
254
  },
255
- children: /* @__PURE__ */ b(ee, { value: u, pipColor: l, pipColors: i })
255
+ children: /* @__PURE__ */ M(de, { value: c, pipColor: i, pipColors: l })
256
256
  }
257
257
  ),
258
- /* @__PURE__ */ b(
258
+ /* @__PURE__ */ M(
259
259
  "div",
260
260
  {
261
261
  style: {
262
262
  flex: 1,
263
263
  position: "relative"
264
264
  },
265
- children: /* @__PURE__ */ b(ee, { value: f, pipColor: l, pipColors: i })
265
+ children: /* @__PURE__ */ M(de, { value: d, pipColor: i, pipColors: l })
266
266
  }
267
267
  )
268
268
  ]
269
269
  }
270
270
  );
271
- }, D = 60, I = 120, Ie = [-45, 45];
272
- function oe(e, o = D, t = I) {
273
- return e ? o / 2 : t / 2;
271
+ }, T = 60, I = 120, Ue = [-45, 45];
272
+ function Z(e, t = T, n = I) {
273
+ return e ? t / 2 : n / 2;
274
274
  }
275
- function k(e, o, t = D, n = I) {
276
- return oe(e, t, n) + oe(o, t, n);
275
+ function O(e, t, n = T, o = I) {
276
+ return Z(e, n, o) + Z(t, n, o);
277
277
  }
278
- function _(e) {
279
- const o = e * Math.PI / 180;
278
+ function R(e) {
279
+ const t = e * Math.PI / 180;
280
280
  return {
281
- dirX: Math.cos(o),
282
- dirY: Math.sin(o)
281
+ dirX: Math.cos(t),
282
+ dirY: Math.sin(t)
283
283
  };
284
284
  }
285
- function N(e) {
286
- const { dirX: o, dirY: t } = _(e);
287
- return { perpX: -t, perpY: o };
285
+ function F(e) {
286
+ const { dirX: t, dirY: n } = R(e);
287
+ return { perpX: -n, perpY: t };
288
288
  }
289
- function Te(e, o) {
289
+ function Ve(e) {
290
290
  const t = e.map((n) => ({ ...n }));
291
291
  for (let n = 1; n < t.length; n++) {
292
- const r = t[n], i = t[n - 1].value2, s = r.value1 === r.value2;
293
- if (o === "linear" && !s) {
294
- t[n] = { value1: r.value2, value2: r.value1 };
295
- continue;
296
- }
297
- o === "offset" && !s && r.value1 !== i && r.value2 === i && (t[n] = { value1: r.value2, value2: r.value1 });
292
+ const o = t[n], r = t[n - 1].value2;
293
+ !(o.value1 === o.value2) && o.value1 !== r && o.value2 === r && (t[n] = { value1: o.value2, value2: o.value1 });
298
294
  }
299
295
  return t;
300
296
  }
301
- function j(e) {
302
- const { dirX: o, dirY: t } = _(e);
303
- return Math.abs(o) >= Math.abs(t) ? o >= 0 ? 1 : -1 : t >= 0 ? 1 : -1;
297
+ function U(e) {
298
+ const { dirX: t, dirY: n } = R(e);
299
+ return Math.abs(t) >= Math.abs(n) ? t >= 0 ? 1 : -1 : n >= 0 ? 1 : -1;
304
300
  }
305
- function se(e, o) {
306
- return e === 0 ? o : e === o ? -o : o;
301
+ function be(e, t) {
302
+ return e === 0 ? t : e === t ? -t : t;
307
303
  }
308
- function Oe({
309
- startX: e,
310
- startY: o,
311
- angle: t,
312
- dominoes: n,
304
+ function ye({
305
+ orientedDominoes: e,
306
+ startX: t,
307
+ startY: n,
308
+ angle: o,
313
309
  layoutStyle: r,
314
- dominoWidth: l = D,
315
- dominoHeight: i = I,
316
- leadGap: s = i * 0.3,
310
+ dominoWidth: i,
311
+ dominoHeight: l,
312
+ leadGap: s,
317
313
  outwardSign: a,
318
- hubIndex: u
314
+ hubIndex: c
319
315
  }) {
320
- const f = [], { dirX: c, dirY: d } = _(t), { perpX: v, perpY: y } = N(t), x = Te([...n], r), w = a ?? j(t), z = r === "offset" && u != null, $ = [];
321
- let m = e + c * s, p = o + d * s, h = 0, M = 0;
322
- const O = l / 2, A = (g) => {
323
- const S = (g - h) * O;
324
- m += v * S, p += y * S, h = g;
316
+ const d = [], { dirX: u, dirY: f } = R(o), { perpX: p, perpY: b } = F(o), m = r === "offset" && c != null, w = [];
317
+ let z = t + u * s, v = n + f * s, k = 0, x = 0;
318
+ const S = i / 2, C = (g) => {
319
+ const h = (g - k) * S;
320
+ z += p * h, v += b * h, k = g;
325
321
  };
326
- for (let g = 0; g < x.length; g++) {
327
- const S = x[g], E = S.value1 === S.value2, C = g > 0 && x[g - 1].value1 === x[g - 1].value2;
328
- r === "linear" ? g > 0 && (E ? (m += c * k(C, !0, l, i), p += d * k(C, !0, l, i)) : C ? (m += c * k(!0, !1, l, i), p += d * k(!0, !1, l, i)) : (m += c * i, p += d * i)) : E ? g > 0 && (m += c * k(C, !0, l, i), p += d * k(C, !0, l, i)) : (g === 0 ? M = w : C ? (m += c * k(!0, !1, l, i), p += d * k(!0, !1, l, i)) : (m += c * (i / 2), p += d * (i / 2), M = se(M, w)), A(M)), $.push(h), f.push({
329
- x: m,
330
- y: p,
331
- rotation: E ? t + 180 : t - 90,
332
- isDouble: E,
333
- value1: S.value1,
334
- value2: S.value2
322
+ for (let g = 0; g < e.length; g++) {
323
+ const h = e[g], y = h.value1 === h.value2, $ = g > 0 && e[g - 1].value1 === e[g - 1].value2;
324
+ r === "linear" ? g > 0 && (y ? (z += u * O($, !0, i, l), v += f * O($, !0, i, l)) : $ ? (z += u * O(!0, !1, i, l), v += f * O(!0, !1, i, l)) : (z += u * l, v += f * l)) : y ? g > 0 && (z += u * O($, !0, i, l), v += f * O($, !0, i, l)) : (g === 0 ? x = a : $ ? (z += u * O(!0, !1, i, l), v += f * O(!0, !1, i, l)) : (z += u * (l / 2), v += f * (l / 2), x = be(x, a)), C(x)), w.push(k), d.push({
325
+ x: z,
326
+ y: v,
327
+ rotation: y ? o + 180 : o - 90,
328
+ isDouble: y,
329
+ value1: h.value1,
330
+ value2: h.value2
335
331
  });
336
332
  }
337
- if (z && u != null) {
338
- const g = -$[u] * O;
333
+ if (m && c != null) {
334
+ const g = -w[c] * S;
339
335
  if (g !== 0)
340
- for (let S = 0; S < f.length; S++)
341
- f[S] = {
342
- ...f[S],
343
- x: f[S].x + v * g,
344
- y: f[S].y + y * g
336
+ for (let h = 0; h < d.length; h++)
337
+ d[h] = {
338
+ ...d[h],
339
+ x: d[h].x + p * g,
340
+ y: d[h].y + b * g
345
341
  };
346
342
  }
347
- return f;
343
+ return d;
344
+ }
345
+ function we(e, t) {
346
+ if (!e || e.length === 0) return [];
347
+ const n = /* @__PURE__ */ new Map();
348
+ for (const o of e)
349
+ Number.isInteger(o.index) && (o.index <= 0 || o.index >= t || n.set(o.index, o.turn));
350
+ return [...n.entries()].map(([o, r]) => ({ index: o, turn: r })).sort((o, r) => o.index - r.index);
351
+ }
352
+ function Ge(e, t, n, o = 1 / 0) {
353
+ const r = we(t, Number.isFinite(o) ? o : n + 1);
354
+ let i = e;
355
+ for (const l of r)
356
+ if (l.index <= n) i += l.turn;
357
+ else break;
358
+ return i;
359
+ }
360
+ function qe(e, t, n, o) {
361
+ const { startX: r, startY: i, angle: l, layoutStyle: s, dominoWidth: a, dominoHeight: c, leadGap: d, outwardSign: u } = t, f = [0, ...n.map((v) => v.index), e.length], p = [];
362
+ let b = l, m = r, w = i, z = d;
363
+ for (let v = 0; v < f.length - 1; v++) {
364
+ const k = e.slice(f[v], f[v + 1]);
365
+ if (k.length === 0) continue;
366
+ const x = v === 0 && o != null && o < f[1] ? o : void 0, S = ye({
367
+ orientedDominoes: k,
368
+ startX: m,
369
+ startY: w,
370
+ angle: b,
371
+ layoutStyle: s,
372
+ dominoWidth: a,
373
+ dominoHeight: c,
374
+ leadGap: z,
375
+ outwardSign: u,
376
+ hubIndex: x
377
+ });
378
+ if (p.push(...S), v >= f.length - 2) break;
379
+ const g = S[S.length - 1], h = R(b), y = Z(g.isDouble, a, c);
380
+ b += n[v].turn;
381
+ const $ = e[f[v + 1]], E = $.value1 === $.value2, A = Z(E, a, c), _ = R(b), N = F(b), P = a / 2, W = g.x + h.dirX * (y - P) + _.dirX * (A + P), V = g.y + h.dirY * (y - P) + _.dirY * (A + P), X = s === "offset" && !E, G = X ? N.perpX * P * u : 0, q = X ? N.perpY * P * u : 0;
382
+ m = W - G, w = V - q, z = 0;
383
+ }
384
+ return p;
348
385
  }
349
- function te(e, o = D, t = I) {
350
- const n = e.rotation * Math.PI / 180, r = Math.cos(n), l = Math.sin(n), i = o / 2, s = t / 2;
386
+ function Ke({
387
+ startX: e,
388
+ startY: t,
389
+ angle: n,
390
+ dominoes: o,
391
+ layoutStyle: r,
392
+ dominoWidth: i = T,
393
+ dominoHeight: l = I,
394
+ leadGap: s = l * 0.3,
395
+ outwardSign: a,
396
+ hubIndex: c,
397
+ bends: d
398
+ }) {
399
+ const u = Ve([...o]), f = a ?? U(n), p = we(d, u.length);
400
+ return p.length > 0 ? qe(
401
+ u,
402
+ {
403
+ startX: e,
404
+ startY: t,
405
+ angle: n,
406
+ layoutStyle: r,
407
+ dominoWidth: i,
408
+ dominoHeight: l,
409
+ leadGap: s,
410
+ outwardSign: f
411
+ },
412
+ p,
413
+ c
414
+ ) : ye({
415
+ orientedDominoes: u,
416
+ startX: e,
417
+ startY: t,
418
+ angle: n,
419
+ layoutStyle: r,
420
+ dominoWidth: i,
421
+ dominoHeight: l,
422
+ leadGap: s,
423
+ outwardSign: f,
424
+ hubIndex: c
425
+ });
426
+ }
427
+ function fe(e, t = T, n = I) {
428
+ const o = e.rotation * Math.PI / 180, r = Math.cos(o), i = Math.sin(o), l = t / 2, s = n / 2;
351
429
  return [
352
- [-i, -s],
353
- [i, -s],
354
- [i, s],
355
- [-i, s]
356
- ].map(([a, u]) => ({
357
- x: e.x + a * r - u * l,
358
- y: e.y + a * l + u * r
430
+ [-l, -s],
431
+ [l, -s],
432
+ [l, s],
433
+ [-l, s]
434
+ ].map(([a, c]) => ({
435
+ x: e.x + a * r - c * i,
436
+ y: e.y + a * i + c * r
359
437
  }));
360
438
  }
361
- function Pe(e, o, t) {
362
- let n = 1 / 0, r = -1 / 0, l = 1 / 0, i = -1 / 0;
439
+ function Ze(e, t, n) {
440
+ let o = 1 / 0, r = -1 / 0, i = 1 / 0, l = -1 / 0;
363
441
  for (const s of e) {
364
- const a = s.x * t.x + s.y * t.y;
365
- n = Math.min(n, a), r = Math.max(r, a);
442
+ const a = s.x * n.x + s.y * n.y;
443
+ o = Math.min(o, a), r = Math.max(r, a);
366
444
  }
367
- for (const s of o) {
368
- const a = s.x * t.x + s.y * t.y;
369
- l = Math.min(l, a), i = Math.max(i, a);
445
+ for (const s of t) {
446
+ const a = s.x * n.x + s.y * n.y;
447
+ i = Math.min(i, a), l = Math.max(l, a);
370
448
  }
371
- return Math.min(r, i) - Math.max(n, l);
449
+ return Math.min(r, l) - Math.max(o, i);
372
450
  }
373
- function ae(e, o, t = 1, n = D, r = I) {
374
- const l = te(e, n, r), i = te(o, n, r);
375
- for (const s of [l, i])
451
+ function J(e, t, n = 1, o = T, r = I) {
452
+ const i = fe(e, o, r), l = fe(t, o, r);
453
+ for (const s of [i, l])
376
454
  for (let a = 0; a < 4; a++) {
377
- const u = s[a], f = s[(a + 1) % 4], c = f.x - u.x, d = f.y - u.y, v = Math.hypot(c, d) || 1, y = { x: -d / v, y: c / v };
378
- if (Pe(l, i, y) <= t)
455
+ const c = s[a], d = s[(a + 1) % 4], u = d.x - c.x, f = d.y - c.y, p = Math.hypot(u, f) || 1, b = { x: -f / p, y: u / p };
456
+ if (Ze(i, l, b) <= n)
379
457
  return !1;
380
458
  }
381
459
  return !0;
382
460
  }
383
- function Ee(e, o, t, n) {
384
- return o.some(
385
- (r) => ae(e, r, 1, t, n)
461
+ function Je(e, t, n, o) {
462
+ return t.some(
463
+ (r) => J(e, r, 1, n, o)
386
464
  );
387
465
  }
388
- const P = D / 4, ne = 24;
389
- function ue({
466
+ function Se(e, t, n = 1, o = T, r = I) {
467
+ return e.some(
468
+ (i) => t.some(
469
+ (l) => J(i, l, n, o, r)
470
+ )
471
+ );
472
+ }
473
+ function me(e, t = 1, n = T, o = I) {
474
+ for (let r = 0; r < e.length; r++)
475
+ for (let i = r + 1; i < e.length; i++)
476
+ if (J(e[r], e[i], t, n, o))
477
+ return !0;
478
+ return !1;
479
+ }
480
+ const Y = T / 4, pe = 24;
481
+ function re({
390
482
  startX: e,
391
- startY: o,
392
- angle: t,
393
- branch: n,
483
+ startY: t,
484
+ angle: n,
485
+ branch: o,
394
486
  layoutStyle: r,
395
- dominoWidth: l = D,
396
- dominoHeight: i = I,
487
+ dominoWidth: i = T,
488
+ dominoHeight: l = I,
397
489
  leadGap: s,
398
490
  depth: a = 0,
399
- anchor: u,
400
- outwardSign: f,
401
- placed: c = [],
402
- pushAxis: d,
403
- minPushSteps: v = 0
491
+ anchor: c,
492
+ outwardSign: d,
493
+ placed: u = [],
494
+ pushAxis: f,
495
+ minPushSteps: p = 0
404
496
  }) {
405
- const y = f ?? j(t), x = n.feet ? Object.keys(n.feet).map(Number).filter((p) => {
406
- const h = n.dominoes[p];
407
- return h && h.value1 === h.value2;
408
- }).sort((p, h) => p - h)[0] : void 0, w = (p, h) => Oe({
409
- startX: p,
410
- startY: h,
411
- angle: t,
412
- dominoes: n.dominoes,
497
+ const b = d ?? U(n), m = o.feet ? Object.keys(o.feet).map(Number).filter((x) => {
498
+ const S = o.dominoes[x];
499
+ return S && S.value1 === S.value2;
500
+ }).sort((x, S) => x - S)[0] : void 0, w = (x, S) => Ke({
501
+ startX: x,
502
+ startY: S,
503
+ angle: n,
504
+ dominoes: o.dominoes,
413
505
  layoutStyle: r,
414
- dominoWidth: l,
415
- dominoHeight: i,
506
+ dominoWidth: i,
507
+ dominoHeight: l,
416
508
  leadGap: s,
417
- outwardSign: y,
418
- hubIndex: x
509
+ outwardSign: b,
510
+ hubIndex: m,
511
+ bends: o.bends
419
512
  });
420
513
  let z = w(
421
- e + (d?.x ?? 0) * P * v,
422
- o + (d?.y ?? 0) * P * v
423
- ), $ = u && d ? {
424
- x: u.x + d.x * P * v,
425
- y: u.y + d.y * P * v
426
- } : u;
427
- if (d && c.length > 0)
428
- for (let p = v; p <= ne; p++) {
429
- const h = e + d.x * P * p, M = o + d.y * P * p, O = w(h, M);
430
- if (!O.some(
431
- (g) => Ee(g, c, l, i)
432
- ) || p === ne) {
433
- z = O, $ = u && {
434
- x: u.x + d.x * P * p,
435
- y: u.y + d.y * P * p
514
+ e + (f?.x ?? 0) * Y * p,
515
+ t + (f?.y ?? 0) * Y * p
516
+ ), v = c && f ? {
517
+ x: c.x + f.x * Y * p,
518
+ y: c.y + f.y * Y * p
519
+ } : c;
520
+ if (f && u.length > 0)
521
+ for (let x = p; x <= pe; x++) {
522
+ const S = e + f.x * Y * x, C = t + f.y * Y * x, g = w(S, C);
523
+ if (!g.some(
524
+ (y) => Je(y, u, i, l)
525
+ ) || x === pe) {
526
+ z = g, v = c && {
527
+ x: c.x + f.x * Y * x,
528
+ y: c.y + f.y * Y * x
436
529
  };
437
530
  break;
438
531
  }
439
532
  }
440
- c.push(...z);
441
- const m = [
533
+ u.push(...z);
534
+ const k = [
442
535
  {
443
- angle: t,
536
+ angle: n,
444
537
  depth: a,
445
538
  layoutStyle: r,
446
- outwardSign: y,
447
- dominoes: n.dominoes,
539
+ outwardSign: b,
540
+ dominoes: o.dominoes,
448
541
  layout: z,
449
- anchor: $
542
+ anchor: v
450
543
  }
451
544
  ];
452
- if (n.feet) {
453
- const { dirX: p, dirY: h } = _(t), { perpX: M, perpY: O } = N(t), A = l / 2, g = i / 2;
454
- for (const S of Object.keys(n.feet)) {
455
- const E = Number(S), C = z[E], q = n.feet[E];
456
- if (!(!C || !C.isDouble || !q))
457
- for (let L = 0; L < q.length; L++) {
458
- const he = q[L], J = Ie[L] ?? 0, R = Math.sign(J), Q = t + J, Z = N(Q), V = -R, be = C.x + p * (l / 2) + M * (i / 2) * R, we = C.y + h * (l / 2) + O * (i / 2) * R, H = be - Z.perpX * V * A, W = we - Z.perpY * V * A;
459
- m.push(
460
- ...ue({
461
- startX: H,
462
- startY: W,
463
- angle: Q,
464
- branch: he,
465
- // Toes inherit the main style so they zigzag in offset mode.
466
- layoutStyle: r,
467
- dominoWidth: l,
468
- dominoHeight: i,
469
- leadGap: g,
470
- outwardSign: V,
471
- depth: a + 1,
472
- anchor: { x: H, y: W },
473
- placed: c,
474
- // If the snug spot is still blocked (the offset center toe leans into
475
- // one side), slide this toe along the double's open edge, away from
476
- // center, until it clears. This keeps it butted against the double
477
- // while stepping past the obstacle — independent per toe, so a foot
478
- // ends up snug and only as asymmetric as the obstruction requires.
479
- pushAxis: { x: M * R, y: O * R }
480
- })
481
- );
482
- }
545
+ if (o.feet) {
546
+ const x = i / 2, S = l / 2;
547
+ for (const C of Object.keys(o.feet)) {
548
+ const g = Number(C), h = z[g], y = o.feet[g];
549
+ if (!h || !h.isDouble || !y)
550
+ continue;
551
+ const $ = Ge(
552
+ n,
553
+ o.bends,
554
+ g,
555
+ o.dominoes.length
556
+ ), { dirX: E, dirY: A } = R($), { perpX: _, perpY: N } = F($);
557
+ for (let P = 0; P < y.length; P++) {
558
+ const W = y[P], V = Ue[P] ?? 0, X = Math.sign(V), G = $ + V, q = F(G), ee = -X, Ee = h.x + E * (i / 2) + _ * (l / 2) * X, Xe = h.y + A * (i / 2) + N * (l / 2) * X, ae = Ee - q.perpX * ee * x, ue = Xe - q.perpY * ee * x;
559
+ k.push(
560
+ ...re({
561
+ startX: ae,
562
+ startY: ue,
563
+ angle: G,
564
+ branch: W,
565
+ // Toes inherit the main style so they zigzag in offset mode.
566
+ layoutStyle: r,
567
+ dominoWidth: i,
568
+ dominoHeight: l,
569
+ leadGap: S,
570
+ outwardSign: ee,
571
+ depth: a + 1,
572
+ anchor: { x: ae, y: ue },
573
+ placed: u,
574
+ // If the snug spot is still blocked (the offset center toe leans into
575
+ // one side), slide this toe along the double's open edge, away from
576
+ // center, until it clears. This keeps it butted against the double
577
+ // while stepping past the obstacle — independent per toe, so a foot
578
+ // ends up snug and only as asymmetric as the obstruction requires.
579
+ pushAxis: { x: _ * X, y: N * X }
580
+ })
581
+ );
582
+ }
483
583
  }
484
584
  }
485
- return m;
585
+ return k;
486
586
  }
487
- function Fe(e) {
488
- return e.flatMap((o) => o.layout);
587
+ function ze(e) {
588
+ return e.flatMap((t) => t.layout);
489
589
  }
490
- function io(e, o = 24, t = D, n = I) {
491
- const r = Math.hypot(t, n) / 2;
590
+ function Ct(e, t = 24, n = T, o = I) {
591
+ const r = Math.hypot(n, o) / 2;
492
592
  if (e.length === 0)
493
593
  return {
494
- width: o * 2 + t,
495
- height: o * 2 + n,
496
- offsetX: o,
497
- offsetY: o
594
+ width: t * 2 + n,
595
+ height: t * 2 + o,
596
+ offsetX: t,
597
+ offsetY: t
498
598
  };
499
- let l = 1 / 0, i = 1 / 0, s = -1 / 0, a = -1 / 0;
500
- for (const u of e)
501
- l = Math.min(l, u.x - r), i = Math.min(i, u.y - r), s = Math.max(s, u.x + r), a = Math.max(a, u.y + r);
599
+ let i = 1 / 0, l = 1 / 0, s = -1 / 0, a = -1 / 0;
600
+ for (const c of e)
601
+ i = Math.min(i, c.x - r), l = Math.min(l, c.y - r), s = Math.max(s, c.x + r), a = Math.max(a, c.y + r);
502
602
  return {
503
- width: Math.ceil(s - l + o * 2),
504
- height: Math.ceil(a - i + o * 2),
505
- offsetX: o - l,
506
- offsetY: o - i
603
+ width: Math.ceil(s - i + t * 2),
604
+ height: Math.ceil(a - l + t * 2),
605
+ offsetX: t - i,
606
+ offsetY: t - l
507
607
  };
508
608
  }
509
- const Ae = ({
609
+ const Q = 1;
610
+ function Me(e, t, n) {
611
+ const { dirX: o, dirY: r } = R(n);
612
+ return e * o + t * r;
613
+ }
614
+ function Qe(e, t, n) {
615
+ const { perpX: o, perpY: r } = F(n);
616
+ return e * o + t * r;
617
+ }
618
+ function ie(e) {
619
+ const t = [];
620
+ for (let n = 1; n < e.length; n++)
621
+ e[n].value1 !== e[n - 1].value2 && t.push({
622
+ code: "chain-break",
623
+ message: `Domino ${n} does not connect to domino ${n - 1}`,
624
+ index: n
625
+ });
626
+ for (let n = 1; n < e.length; n++) {
627
+ const o = e[n - 1].value1 === e[n - 1].value2, r = e[n].value1 === e[n].value2;
628
+ o && r && t.push({
629
+ code: "consecutive-doubles",
630
+ message: `Consecutive doubles at index ${n - 1} and ${n}`,
631
+ index: n
632
+ });
633
+ }
634
+ return { valid: t.length === 0, issues: t };
635
+ }
636
+ function $e(e, t, n, o = T, r = I) {
637
+ const i = o / 2, l = e.map((u) => u.isDouble), s = [];
638
+ let a = 0, c = 0, d = 0;
639
+ for (let u = 0; u < e.length; u++) {
640
+ const f = l[u], p = u > 0 && l[u - 1];
641
+ t === "linear" ? (u > 0 && (a += O(p, f, o, r)), c = 0) : f ? u > 0 && (a += O(p, !0, o, r)) : u === 0 ? (d = n, c = d * i) : p ? a += O(!0, !1, o, r) : (a += r / 2, d = be(d, n), c = d * i), s.push({ along: a, perp: c });
642
+ }
643
+ return s;
644
+ }
645
+ function He(e, t, n, o = Q, r) {
646
+ const i = [], l = r ?? U(t), s = $e(e, n, l);
647
+ for (let a = 1; a < e.length; a++) {
648
+ const c = e[a - 1], d = e[a], u = d.x - c.x, f = d.y - c.y, p = Me(u, f, t), b = Qe(u, f, t), m = s[a].along - s[a - 1].along, w = s[a].perp - s[a - 1].perp;
649
+ Math.abs(p - m) > o && i.push({
650
+ code: "spacing-along-train",
651
+ message: `Along-train spacing between domino ${a - 1} and ${a} is ${p.toFixed(2)}px (expected ${m}px)`,
652
+ index: a
653
+ }), Math.abs(b - w) > o && i.push({
654
+ code: "spacing-perpendicular",
655
+ message: `Perpendicular spacing between domino ${a - 1} and ${a} is ${b.toFixed(2)}px (expected ${w}px)`,
656
+ index: a
657
+ });
658
+ }
659
+ return { valid: i.length === 0, issues: i };
660
+ }
661
+ function We(e, t, n, o = Q, r) {
662
+ const i = [], l = r ?? U(t), s = $e(e, n, l);
663
+ for (let a = 1; a < e.length; a++) {
664
+ const c = e[a - 1], d = e[a], u = d.x - c.x, f = d.y - c.y, p = Math.hypot(u, f), b = s[a].along - s[a - 1].along, m = s[a].perp - s[a - 1].perp, w = Math.hypot(b, m) * 0.9;
665
+ p + o < w && i.push({
666
+ code: "overlap",
667
+ message: `Domino ${a - 1} and ${a} centers are ${p.toFixed(2)}px apart (minimum ${w.toFixed(2)}px)`,
668
+ index: a
669
+ });
670
+ }
671
+ return { valid: i.length === 0, issues: i };
672
+ }
673
+ function Tt(e, t, n, o, r = Q, i) {
674
+ const l = [
675
+ ...ie(t).issues,
676
+ ...He(
677
+ e,
678
+ n,
679
+ o,
680
+ r,
681
+ i
682
+ ).issues,
683
+ ...We(
684
+ e,
685
+ n,
686
+ o,
687
+ r,
688
+ i
689
+ ).issues
690
+ ];
691
+ return e.length !== t.length && l.push({
692
+ code: "layout-length",
693
+ message: `Layout length ${e.length} does not match domino count ${t.length}`
694
+ }), { valid: l.length === 0, issues: l };
695
+ }
696
+ function et(e) {
697
+ const t = [], n = (o, r) => {
698
+ if (t.push(
699
+ ...ie(o.dominoes).issues.map((i) => ({
700
+ ...i,
701
+ message: `[${r}] ${i.message}`
702
+ }))
703
+ ), !!o.feet)
704
+ for (const i of Object.keys(o.feet)) {
705
+ const l = Number(i), s = o.dominoes[l], a = o.feet[l] ?? [];
706
+ if (!s) {
707
+ t.push({
708
+ code: "foot-host-missing",
709
+ message: `[${r}] Foot references missing tile ${l}`
710
+ });
711
+ continue;
712
+ }
713
+ s.value1 !== s.value2 && t.push({
714
+ code: "foot-host-not-double",
715
+ message: `[${r}] Foot host tile ${l} is not a double`
716
+ }), a.length > 2 && t.push({
717
+ code: "foot-too-many-toes",
718
+ message: `[${r}] Double ${l} has ${a.length} side toes (max 2; the center toe is the main line)`
719
+ }), a.forEach((c, d) => {
720
+ const u = c.dominoes[0];
721
+ u && u.value1 !== s.value1 && t.push({
722
+ code: "foot-connection",
723
+ message: `[${r}] Toe ${d} on double ${l} starts with ${u.value1} but the double is ${s.value1}`
724
+ }), n(c, `${r}.${l}.${d}`);
725
+ });
726
+ }
727
+ };
728
+ return n(e, "main"), { valid: t.length === 0, issues: t };
729
+ }
730
+ function It(e, t = Q) {
731
+ const n = [];
732
+ e.forEach((r, i) => {
733
+ if (n.push(
734
+ ...ie(r.dominoes).issues.map((l) => ({
735
+ ...l,
736
+ message: `[segment ${i} @${r.angle}°] ${l.message}`
737
+ }))
738
+ ), r.layout.length !== r.dominoes.length && n.push({
739
+ code: "layout-length",
740
+ message: `[segment ${i}] Layout length ${r.layout.length} does not match domino count ${r.dominoes.length}`
741
+ }), r.anchor && r.layout.length > 0) {
742
+ const l = r.layout[0], s = Me(
743
+ l.x - r.anchor.x,
744
+ l.y - r.anchor.y,
745
+ r.angle
746
+ ), a = I / 2;
747
+ Math.abs(s - a) > t && n.push({
748
+ code: "foot-anchor",
749
+ message: `[segment ${i}] First toe tile sits ${s.toFixed(2)}px from the double along the toe (expected ${a}px)`,
750
+ index: 0
751
+ });
752
+ }
753
+ });
754
+ const o = e.flatMap((r) => r.layout);
755
+ for (let r = 0; r < o.length; r++)
756
+ for (let i = r + 1; i < o.length; i++)
757
+ J(o[r], o[i]) && n.push({
758
+ code: "tile-overlap",
759
+ message: `Tiles ${r} and ${i} overlap`,
760
+ index: i
761
+ });
762
+ return { valid: n.length === 0, issues: n };
763
+ }
764
+ const tt = (() => {
765
+ try {
766
+ return !1;
767
+ } catch {
768
+ return !1;
769
+ }
770
+ })(), ot = ({
510
771
  startX: e,
511
- startY: o,
512
- angle: t,
513
- trainData: n,
772
+ startY: t,
773
+ angle: n,
774
+ trainData: o,
514
775
  layoutStyle: r,
515
- tableWidth: l,
516
- tableHeight: i,
776
+ tableWidth: i,
777
+ tableHeight: l,
517
778
  centerX: s,
518
779
  centerY: a,
519
- pipColors: u
780
+ pipColors: c
520
781
  }) => {
521
- const f = Se(
522
- () => Fe(
523
- ue({
782
+ ne(() => {
783
+ if (!tt) return;
784
+ const u = et({
785
+ dominoes: o.dominoes,
786
+ feet: o.feet
787
+ });
788
+ u.valid || console.warn(
789
+ `DominoTrain: player ${o.playerId} train does not follow the rules:`,
790
+ u.issues.map((f) => f.message)
791
+ );
792
+ }, [o.dominoes, o.feet, o.playerId]);
793
+ const d = Ye(
794
+ () => ze(
795
+ re({
524
796
  startX: e,
525
- startY: o,
526
- angle: t,
527
- branch: { dominoes: n.dominoes, feet: n.feet },
797
+ startY: t,
798
+ angle: n,
799
+ branch: { dominoes: o.dominoes, feet: o.feet },
528
800
  layoutStyle: r
529
801
  })
530
802
  ),
531
803
  [
532
804
  e,
533
- o,
534
805
  t,
535
- n.dominoes,
536
- n.feet,
806
+ n,
807
+ o.dominoes,
808
+ o.feet,
537
809
  r,
538
- l,
539
- i
810
+ i,
811
+ l
540
812
  ]
541
813
  );
542
- return /* @__PURE__ */ b(re, { children: f.map((c, d) => {
543
- const v = n.isPublic;
544
- return /* @__PURE__ */ b(
814
+ return /* @__PURE__ */ M(xe, { children: d.map((u, f) => {
815
+ const p = o.isPublic;
816
+ return /* @__PURE__ */ M(
545
817
  "div",
546
818
  {
547
819
  style: {
548
820
  position: "absolute",
549
- left: `${c.x - D / 2}px`,
550
- top: `${c.y - I / 2}px`,
821
+ left: `${u.x - T / 2}px`,
822
+ top: `${u.y - I / 2}px`,
551
823
  zIndex: 5
552
824
  },
553
- children: /* @__PURE__ */ b(
554
- ie,
825
+ children: /* @__PURE__ */ M(
826
+ ve,
555
827
  {
556
- value1: c.value1,
557
- value2: c.value2,
558
- width: D,
828
+ value1: u.value1,
829
+ value2: u.value2,
830
+ width: T,
559
831
  height: I,
560
832
  backgroundColor: "white",
561
833
  pipColor: "black",
562
- pipColors: u,
563
- borderColor: v ? "red" : "black",
564
- rotation: c.rotation
834
+ pipColors: c,
835
+ borderColor: p ? "red" : "black",
836
+ rotation: u.rotation
565
837
  }
566
838
  )
567
839
  },
568
- `main-train-${n.playerId}-${d}`
840
+ `main-train-${o.playerId}-${f}`
569
841
  );
570
842
  }) });
571
- }, Re = ({
843
+ };
844
+ function nt(e, t, n, o) {
845
+ const r = n * (o === "offset" ? 2.5 : 1.3);
846
+ return Math.max(t + 20, Math.ceil(r * e / (2 * Math.PI)));
847
+ }
848
+ const rt = ({
572
849
  playerCount: e,
573
- centerX: o,
574
- centerY: t,
575
- radius: n,
850
+ centerX: t,
851
+ centerY: n,
852
+ radius: o,
576
853
  engineValue: r,
577
- trains: l,
578
- layoutStyle: i,
854
+ trains: i,
855
+ layoutStyle: l,
579
856
  tableWidth: s,
580
857
  tableHeight: a,
581
- pipColors: u
858
+ pipColors: c
582
859
  }) => {
583
- const f = Math.max(8, e), c = 120;
584
- return /* @__PURE__ */ T("div", { style: { position: "relative", width: "100%", height: "100%" }, children: [
585
- /* @__PURE__ */ b(
860
+ const d = Math.max(8, e), u = 120, f = 60, p = 120, b = nt(d, o, f, l);
861
+ return /* @__PURE__ */ D("div", { style: { position: "relative", width: "100%", height: "100%" }, children: [
862
+ /* @__PURE__ */ M(
586
863
  "div",
587
864
  {
588
865
  style: {
589
866
  position: "absolute",
590
- width: `${c}px`,
591
- height: `${c}px`,
592
- left: `${o - c / 2}px`,
593
- top: `${t - c / 2}px`,
867
+ width: `${u}px`,
868
+ height: `${u}px`,
869
+ left: `${t - u / 2}px`,
870
+ top: `${n - u / 2}px`,
594
871
  backgroundColor: "#d1d5db",
595
872
  borderWidth: "3px",
596
873
  borderStyle: "solid",
@@ -602,186 +879,186 @@ const Ae = ({
602
879
  justifyContent: "center",
603
880
  alignItems: "center"
604
881
  },
605
- children: /* @__PURE__ */ b("div", { style: { transform: "rotate(0deg)" }, children: /* @__PURE__ */ b(
606
- ie,
882
+ children: /* @__PURE__ */ M("div", { style: { transform: "rotate(0deg)" }, children: /* @__PURE__ */ M(
883
+ ve,
607
884
  {
608
885
  value1: r,
609
886
  value2: r,
610
- width: 60,
611
- height: 120,
887
+ width: f,
888
+ height: p,
612
889
  backgroundColor: "white",
613
890
  pipColor: "black",
614
- pipColors: u,
891
+ pipColors: c,
615
892
  borderColor: "#333"
616
893
  }
617
894
  ) })
618
895
  }
619
896
  ),
620
- Array.from({ length: f }).map((y, x) => {
621
- const w = x * 360 / f, z = w * Math.PI / 180, $ = o + (n + 20) * Math.cos(z), m = t + (n + 20) * Math.sin(z), p = l.find((h) => h.playerId === x) || {
897
+ Array.from({ length: d }).map((m, w) => {
898
+ const z = w * 360 / d, v = z * Math.PI / 180, k = t + b * Math.cos(v), x = n + b * Math.sin(v), S = i.find((C) => C.playerId === w) || {
622
899
  dominoes: [],
623
- playerId: x,
900
+ playerId: w,
624
901
  isPublic: !1
625
902
  };
626
- return /* @__PURE__ */ b(
627
- Ae,
903
+ return /* @__PURE__ */ M(
904
+ ot,
628
905
  {
629
- startX: $,
630
- startY: m,
631
- angle: w,
632
- trainData: p,
633
- layoutStyle: i,
906
+ startX: k,
907
+ startY: x,
908
+ angle: z,
909
+ trainData: S,
910
+ layoutStyle: l,
634
911
  tableWidth: s,
635
912
  tableHeight: a,
636
- centerX: o,
637
- centerY: t,
638
- pipColors: u
913
+ centerX: t,
914
+ centerY: n,
915
+ pipColors: c
639
916
  },
640
- x
917
+ w
641
918
  );
642
919
  })
643
920
  ] });
644
921
  };
645
- function F(e, o) {
646
- return e <= o ? `${e}:${o}` : `${o}:${e}`;
922
+ function L(e, t) {
923
+ return e <= t ? `${e}:${t}` : `${t}:${e}`;
647
924
  }
648
- function ce(e) {
649
- return F(e.value1, e.value2);
925
+ function ke(e) {
926
+ return L(e.value1, e.value2);
650
927
  }
651
- function de(e) {
928
+ function Ce(e) {
652
929
  return e.value1 === e.value2;
653
930
  }
654
- function so(e, o) {
655
- return e.value1 === o || e.value2 === o;
656
- }
657
- function ao(e, o) {
658
- return e.value1 === o ? e.value2 : e.value2 === o ? e.value1 : null;
659
- }
660
- function fe(e, o) {
661
- return e.value1 === o ? { value1: e.value1, value2: e.value2 } : e.value2 === o ? { value1: e.value2, value2: e.value1 } : null;
662
- }
663
- function uo(e) {
664
- const o = [];
665
- for (let t = 0; t <= e; t++)
666
- for (let n = t; n <= e; n++)
667
- o.push({ value1: t, value2: n });
668
- return o;
669
- }
670
- function co(e) {
671
- const o = e + 1;
672
- return o * (o + 1) / 2;
673
- }
674
- function Xe(e, o = 12, t = {}) {
675
- const n = /* @__PURE__ */ new Set([F(o, o)]), r = [];
676
- for (let l = 0; l < e; l++) {
677
- const i = 4 + Math.floor(Math.random() * 7), s = [];
678
- let a = o, u = !1;
679
- for (let c = 0; c < i; c++) {
680
- const d = Ne(
931
+ function Dt(e, t) {
932
+ return e.value1 === t || e.value2 === t;
933
+ }
934
+ function Pt(e, t) {
935
+ return e.value1 === t ? e.value2 : e.value2 === t ? e.value1 : null;
936
+ }
937
+ function Te(e, t) {
938
+ return e.value1 === t ? { value1: e.value1, value2: e.value2 } : e.value2 === t ? { value1: e.value2, value2: e.value1 } : null;
939
+ }
940
+ function Ot(e) {
941
+ const t = [];
942
+ for (let n = 0; n <= e; n++)
943
+ for (let o = n; o <= e; o++)
944
+ t.push({ value1: n, value2: o });
945
+ return t;
946
+ }
947
+ function Et(e) {
948
+ const t = e + 1;
949
+ return t * (t + 1) / 2;
950
+ }
951
+ function it(e, t = 12, n = {}) {
952
+ const o = /* @__PURE__ */ new Set([L(t, t)]), r = [];
953
+ for (let i = 0; i < e; i++) {
954
+ const l = 4 + Math.floor(Math.random() * 7), s = [];
955
+ let a = t, c = !1;
956
+ for (let u = 0; u < l; u++) {
957
+ const f = ut(
681
958
  a,
682
- u,
683
- c === 0,
684
- o,
685
- n
959
+ c,
960
+ u === 0,
961
+ t,
962
+ o
686
963
  );
687
- if (d === null)
964
+ if (f === null)
688
965
  break;
689
- const v = d === a;
690
- n.add(F(a, d)), s.push({ value1: a, value2: d }), u = v, a = d;
966
+ const p = f === a;
967
+ o.add(L(a, f)), s.push({ value1: a, value2: f }), c = p, a = f;
691
968
  }
692
- const f = t.chickenFeet ? _e(s, n) : void 0;
969
+ const d = n.chickenFeet ? lt(s, o) : void 0;
693
970
  r.push({
694
- playerId: l,
971
+ playerId: i,
695
972
  dominoes: s,
696
973
  isPublic: Math.random() > 0.7,
697
- ...f ? { feet: f } : {}
974
+ ...d ? { feet: d } : {}
698
975
  });
699
976
  }
700
977
  return r;
701
978
  }
702
- function _e(e, o) {
703
- const t = {};
704
- for (let n = 0; n < e.length; n++) {
705
- if (e[n].value1 !== e[n].value2)
979
+ function lt(e, t) {
980
+ const n = {};
981
+ for (let o = 0; o < e.length; o++) {
982
+ if (e[o].value1 !== e[o].value2)
706
983
  continue;
707
- const r = e[n].value1, l = [];
708
- for (let i = 0; i < 2; i++) {
709
- const s = Le(r, o);
710
- s && l.push(s);
984
+ const r = e[o].value1, i = [];
985
+ for (let l = 0; l < 2; l++) {
986
+ const s = st(r, t);
987
+ s && i.push(s);
711
988
  }
712
- l.length && (t[n] = l);
989
+ i.length && (n[o] = i);
713
990
  }
714
- return Object.keys(t).length ? t : void 0;
991
+ return Object.keys(n).length ? n : void 0;
715
992
  }
716
- function Le(e, o) {
717
- const t = 1 + Math.floor(Math.random() * 2), n = [];
993
+ function st(e, t) {
994
+ const n = 1 + Math.floor(Math.random() * 2), o = [];
718
995
  let r = e;
719
- for (let l = 0; l < t; l++) {
720
- const i = Ye(r, o);
721
- if (i === null)
996
+ for (let i = 0; i < n; i++) {
997
+ const l = at(r, t);
998
+ if (l === null)
722
999
  break;
723
- o.add(F(r, i)), n.push({ value1: r, value2: i }), r = i;
1000
+ t.add(L(r, l)), o.push({ value1: r, value2: l }), r = l;
724
1001
  }
725
- return n.length ? { dominoes: n } : null;
1002
+ return o.length ? { dominoes: o } : null;
726
1003
  }
727
- function Ye(e, o) {
728
- const t = [];
729
- for (let n = 0; n < 13; n++)
730
- n !== e && (o.has(F(e, n)) || t.push(n));
731
- return t.length === 0 ? null : t[Math.floor(Math.random() * t.length)];
1004
+ function at(e, t) {
1005
+ const n = [];
1006
+ for (let o = 0; o < 13; o++)
1007
+ o !== e && (t.has(L(e, o)) || n.push(o));
1008
+ return n.length === 0 ? null : n[Math.floor(Math.random() * n.length)];
732
1009
  }
733
- function Ne(e, o, t, n, r) {
734
- const l = Array.from({ length: 13 }, (i, s) => s).filter(
735
- (i) => je(
1010
+ function ut(e, t, n, o, r) {
1011
+ const i = Array.from({ length: 13 }, (l, s) => s).filter(
1012
+ (l) => ct(
736
1013
  e,
737
- i,
738
- o,
1014
+ l,
739
1015
  t,
740
1016
  n,
1017
+ o,
741
1018
  r
742
1019
  )
743
1020
  );
744
- return l.length === 0 ? null : l[Math.floor(Math.random() * l.length)];
1021
+ return i.length === 0 ? null : i[Math.floor(Math.random() * i.length)];
745
1022
  }
746
- function je(e, o, t, n, r, l) {
747
- const i = o === e;
748
- return !(n && i && e === r || i && t || l.has(F(e, o)));
1023
+ function ct(e, t, n, o, r, i) {
1024
+ const l = t === e;
1025
+ return !(o && l && e === r || l && n || i.has(L(e, t)));
749
1026
  }
750
- const Be = {
1027
+ const dt = {
751
1028
  playerCount: 8,
752
1029
  trains: [],
753
1030
  engineValue: 12
754
- }, fo = ({
755
- initialState: e = Be,
756
- width: o = 1200,
757
- height: t = 800,
758
- pipColors: n,
1031
+ }, Xt = ({
1032
+ initialState: e = dt,
1033
+ width: t = 1200,
1034
+ height: n = 800,
1035
+ pipColors: o,
759
1036
  onPipColorsChange: r
760
1037
  }) => {
761
- const [l, i] = Y(e), [s, a] = Y("offset"), [u, f] = Y(!1), [c, d] = Y(void 0), v = n ?? c, y = r ?? d, x = v !== void 0, w = o / 2, z = t / 2, $ = (p = u) => {
762
- const h = Xe(
763
- l.playerCount,
764
- l.engineValue,
765
- { chickenFeet: p }
1038
+ const [i, l] = B(e), [s, a] = B("offset"), [c, d] = B(!1), [u, f] = B(void 0), p = o ?? u, b = r ?? f, m = p !== void 0, w = t / 2, z = n / 2, v = (x = c) => {
1039
+ const S = it(
1040
+ i.playerCount,
1041
+ i.engineValue,
1042
+ { chickenFeet: x }
766
1043
  );
767
- i((M) => ({
768
- ...M,
769
- trains: h
1044
+ l((C) => ({
1045
+ ...C,
1046
+ trains: S
770
1047
  }));
771
1048
  };
772
- ye(() => {
773
- $();
1049
+ ne(() => {
1050
+ v();
774
1051
  }, []);
775
- const m = () => {
776
- const p = !u;
777
- f(p), $(p);
1052
+ const k = () => {
1053
+ const x = !c;
1054
+ d(x), v(x);
778
1055
  };
779
- return /* @__PURE__ */ T(
1056
+ return /* @__PURE__ */ D(
780
1057
  "div",
781
1058
  {
782
1059
  style: {
783
- width: `${o}px`,
784
- height: `${t}px`,
1060
+ width: `${t}px`,
1061
+ height: `${n}px`,
785
1062
  position: "relative",
786
1063
  backgroundColor: "#1f8a55",
787
1064
  // Classic green felt background
@@ -790,15 +1067,15 @@ const Be = {
790
1067
  overflow: "hidden"
791
1068
  },
792
1069
  children: [
793
- /* @__PURE__ */ T(
1070
+ /* @__PURE__ */ D(
794
1071
  "div",
795
1072
  {
796
1073
  style: { position: "absolute", top: "10px", left: "10px", zIndex: 100 },
797
1074
  children: [
798
- /* @__PURE__ */ b(
1075
+ /* @__PURE__ */ M(
799
1076
  "button",
800
1077
  {
801
- onClick: () => $(),
1078
+ onClick: () => v(),
802
1079
  style: {
803
1080
  padding: "8px 12px",
804
1081
  backgroundColor: "#fff",
@@ -810,7 +1087,7 @@ const Be = {
810
1087
  children: "New trains"
811
1088
  }
812
1089
  ),
813
- /* @__PURE__ */ T(
1090
+ /* @__PURE__ */ D(
814
1091
  "button",
815
1092
  {
816
1093
  onClick: () => a(s === "offset" ? "linear" : "offset"),
@@ -828,45 +1105,45 @@ const Be = {
828
1105
  ]
829
1106
  }
830
1107
  ),
831
- /* @__PURE__ */ T(
1108
+ /* @__PURE__ */ D(
832
1109
  "button",
833
1110
  {
834
- onClick: m,
1111
+ onClick: k,
835
1112
  style: {
836
1113
  padding: "8px 12px",
837
- backgroundColor: u ? "#fef3c7" : "#fff",
838
- border: `1px solid ${u ? "#f59e0b" : "#ccc"}`,
1114
+ backgroundColor: c ? "#fef3c7" : "#fff",
1115
+ border: `1px solid ${c ? "#f59e0b" : "#ccc"}`,
839
1116
  borderRadius: "4px",
840
1117
  marginRight: "10px",
841
1118
  cursor: "pointer"
842
1119
  },
843
1120
  children: [
844
1121
  "Chicken Feet: ",
845
- u ? "On" : "Off"
1122
+ c ? "On" : "Off"
846
1123
  ]
847
1124
  }
848
1125
  ),
849
- /* @__PURE__ */ T(
1126
+ /* @__PURE__ */ D(
850
1127
  "button",
851
1128
  {
852
- onClick: () => y(x ? void 0 : X),
1129
+ onClick: () => b(m ? void 0 : j),
853
1130
  style: {
854
1131
  padding: "8px 12px",
855
- backgroundColor: x ? "#fef3c7" : "#fff",
856
- border: `1px solid ${x ? "#f59e0b" : "#ccc"}`,
1132
+ backgroundColor: m ? "#fef3c7" : "#fff",
1133
+ border: `1px solid ${m ? "#f59e0b" : "#ccc"}`,
857
1134
  borderRadius: "4px",
858
1135
  cursor: "pointer"
859
1136
  },
860
1137
  children: [
861
1138
  "Pip Colors: ",
862
- x ? "On" : "Off"
1139
+ m ? "On" : "Off"
863
1140
  ]
864
1141
  }
865
1142
  )
866
1143
  ]
867
1144
  }
868
1145
  ),
869
- /* @__PURE__ */ T(
1146
+ /* @__PURE__ */ D(
870
1147
  "div",
871
1148
  {
872
1149
  style: {
@@ -880,191 +1157,270 @@ const Be = {
880
1157
  fontSize: "14px"
881
1158
  },
882
1159
  children: [
883
- /* @__PURE__ */ T("div", { children: [
1160
+ /* @__PURE__ */ D("div", { children: [
884
1161
  "Engine: Double-",
885
- l.engineValue
1162
+ i.engineValue
886
1163
  ] }),
887
- /* @__PURE__ */ T("div", { children: [
1164
+ /* @__PURE__ */ D("div", { children: [
888
1165
  "Players: ",
889
- l.playerCount
1166
+ i.playerCount
890
1167
  ] })
891
1168
  ]
892
1169
  }
893
1170
  ),
894
- /* @__PURE__ */ b(
895
- Re,
1171
+ /* @__PURE__ */ M(
1172
+ rt,
896
1173
  {
897
- playerCount: l.playerCount,
1174
+ playerCount: i.playerCount,
898
1175
  centerX: w,
899
1176
  centerY: z,
900
1177
  radius: 80,
901
- engineValue: l.engineValue,
902
- trains: l.trains,
1178
+ engineValue: i.engineValue,
1179
+ trains: i.trains,
903
1180
  layoutStyle: s,
904
- tableWidth: o,
905
- tableHeight: t,
906
- pipColors: v
1181
+ tableWidth: t,
1182
+ tableHeight: n,
1183
+ pipColors: p
907
1184
  }
908
1185
  )
909
1186
  ]
910
1187
  }
911
1188
  );
912
- }, B = 1;
913
- function pe(e, o, t) {
914
- const { dirX: n, dirY: r } = _(t);
915
- return e * n + o * r;
916
- }
917
- function Ue(e, o, t) {
918
- const { perpX: n, perpY: r } = N(t);
919
- return e * n + o * r;
920
- }
921
- function K(e) {
922
- const o = [];
923
- for (let t = 1; t < e.length; t++)
924
- e[t].value1 !== e[t - 1].value2 && o.push({
925
- code: "chain-break",
926
- message: `Domino ${t} does not connect to domino ${t - 1}`,
927
- index: t
928
- });
929
- for (let t = 1; t < e.length; t++) {
930
- const n = e[t - 1].value1 === e[t - 1].value2, r = e[t].value1 === e[t].value2;
931
- n && r && o.push({
932
- code: "consecutive-doubles",
933
- message: `Consecutive doubles at index ${t - 1} and ${t}`,
934
- index: t
935
- });
936
- }
937
- return { valid: o.length === 0, issues: o };
938
- }
939
- function ve(e, o, t, n = D, r = I) {
940
- const l = n / 2, i = e.map((c) => c.isDouble), s = [];
941
- let a = 0, u = 0, f = 0;
942
- for (let c = 0; c < e.length; c++) {
943
- const d = i[c], v = c > 0 && i[c - 1];
944
- o === "linear" ? (c > 0 && (a += k(v, d, n, r)), u = 0) : d ? c > 0 && (a += k(v, !0, n, r)) : c === 0 ? (f = t, u = f * l) : v ? a += k(!0, !1, n, r) : (a += r / 2, f = se(f, t), u = f * l), s.push({ along: a, perp: u });
945
- }
946
- return s;
1189
+ }, le = 90;
1190
+ function oe(e, t = le) {
1191
+ return e === "right" ? t : -t;
947
1192
  }
948
- function qe(e, o, t, n = B, r) {
949
- const l = [], i = r ?? j(o), s = ve(e, t, i);
950
- for (let a = 1; a < e.length; a++) {
951
- const u = e[a - 1], f = e[a], c = f.x - u.x, d = f.y - u.y, v = pe(c, d, o), y = Ue(c, d, o), x = s[a].along - s[a - 1].along, w = s[a].perp - s[a - 1].perp;
952
- Math.abs(v - x) > n && l.push({
953
- code: "spacing-along-train",
954
- message: `Along-train spacing between domino ${a - 1} and ${a} is ${v.toFixed(2)}px (expected ${x}px)`,
955
- index: a
956
- }), Math.abs(y - w) > n && l.push({
957
- code: "spacing-perpendicular",
958
- message: `Perpendicular spacing between domino ${a - 1} and ${a} is ${y.toFixed(2)}px (expected ${w}px)`,
959
- index: a
960
- });
1193
+ function Ie(e) {
1194
+ return e === "right" ? "left" : "right";
1195
+ }
1196
+ function Yt(e, t) {
1197
+ return (t ?? U(e)) >= 0 ? "left" : "right";
1198
+ }
1199
+ function Rt(e, t, n) {
1200
+ const { perpX: o, perpY: r } = F(t), i = (a, c) => {
1201
+ let d = 1 / 0;
1202
+ return a > 0 ? d = Math.min(d, (n.width - e.x) / a) : a < 0 && (d = Math.min(d, (0 - e.x) / a)), c > 0 ? d = Math.min(d, (n.height - e.y) / c) : c < 0 && (d = Math.min(d, (0 - e.y) / c)), Number.isFinite(d) ? Math.max(0, d) : 1 / 0;
1203
+ }, l = i(o, r), s = i(-o, -r);
1204
+ return l >= s ? "right" : "left";
1205
+ }
1206
+ function De(e, t) {
1207
+ return ze(
1208
+ re({
1209
+ startX: t.startX,
1210
+ startY: t.startY,
1211
+ angle: t.angle,
1212
+ branch: e,
1213
+ layoutStyle: t.layoutStyle
1214
+ })
1215
+ );
1216
+ }
1217
+ function K(e, t, n) {
1218
+ const o = (e ?? []).filter((r) => r.index !== t);
1219
+ return n === null ? o : [...o, { index: t, turn: n }].sort((r, i) => r.index - i.index);
1220
+ }
1221
+ function At({
1222
+ branch: e,
1223
+ index: t,
1224
+ build: n,
1225
+ obstacles: o,
1226
+ preferredSide: r,
1227
+ degrees: i = le
1228
+ }) {
1229
+ const l = [r, Ie(r)];
1230
+ for (const s of l) {
1231
+ const a = oe(s, i), c = {
1232
+ ...e,
1233
+ bends: K(e.bends, t, a)
1234
+ }, d = De(c, n);
1235
+ if (!me(d) && !Se(d, o))
1236
+ return { turn: a };
961
1237
  }
962
- return { valid: l.length === 0, issues: l };
1238
+ return { turn: null, reason: "blocked" };
963
1239
  }
964
- function Ve(e, o, t, n = B, r) {
965
- const l = [], i = r ?? j(o), s = ve(e, t, i);
966
- for (let a = 1; a < e.length; a++) {
967
- const u = e[a - 1], f = e[a], c = f.x - u.x, d = f.y - u.y, v = Math.hypot(c, d), y = s[a].along - s[a - 1].along, x = s[a].perp - s[a - 1].perp, w = Math.hypot(y, x) * 0.9;
968
- v + n < w && l.push({
969
- code: "overlap",
970
- message: `Domino ${a - 1} and ${a} centers are ${v.toFixed(2)}px apart (minimum ${w.toFixed(2)}px)`,
971
- index: a
972
- });
1240
+ function Ft(e, t, n, o, r, i = le) {
1241
+ const l = (e.bends ?? []).find((u) => u.index === t), s = oe(r, i), a = oe(Ie(r), i), c = (u) => {
1242
+ const f = {
1243
+ ...e,
1244
+ bends: K(e.bends, t, u)
1245
+ }, p = De(f, n);
1246
+ return !me(p) && !Se(p, o);
1247
+ };
1248
+ let d;
1249
+ l ? l.turn === s ? d = [a, null] : l.turn === a ? d = [null] : d = [s, a, null] : d = [s, a];
1250
+ for (const u of d) {
1251
+ if (u === null)
1252
+ return { bends: K(e.bends, t, null), changed: !0, blocked: !1 };
1253
+ if (c(u))
1254
+ return { bends: K(e.bends, t, u), changed: !0, blocked: !1 };
973
1255
  }
974
- return { valid: l.length === 0, issues: l };
1256
+ return { bends: e.bends ?? [], changed: !1, blocked: !0 };
975
1257
  }
976
- function po(e, o, t, n, r = B, l) {
977
- const i = [
978
- ...K(o).issues,
979
- ...qe(
980
- e,
981
- t,
982
- n,
983
- r,
984
- l
985
- ).issues,
986
- ...Ve(
987
- e,
988
- t,
989
- n,
990
- r,
991
- l
992
- ).issues
993
- ];
994
- return e.length !== o.length && i.push({
995
- code: "layout-length",
996
- message: `Layout length ${e.length} does not match domino count ${o.length}`
997
- }), { valid: i.length === 0, issues: i };
1258
+ function se(e, t, n) {
1259
+ return Math.min(n, Math.max(t, e));
998
1260
  }
999
- function vo(e) {
1000
- const o = [], t = (n, r) => {
1001
- if (o.push(
1002
- ...K(n.dominoes).issues.map((l) => ({
1003
- ...l,
1004
- message: `[${r}] ${l.message}`
1005
- }))
1006
- ), !!n.feet)
1007
- for (const l of Object.keys(n.feet)) {
1008
- const i = Number(l), s = n.dominoes[i], a = n.feet[i] ?? [];
1009
- if (!s) {
1010
- o.push({
1011
- code: "foot-host-missing",
1012
- message: `[${r}] Foot references missing tile ${i}`
1013
- });
1014
- continue;
1015
- }
1016
- s.value1 !== s.value2 && o.push({
1017
- code: "foot-host-not-double",
1018
- message: `[${r}] Foot host tile ${i} is not a double`
1019
- }), a.length > 2 && o.push({
1020
- code: "foot-too-many-toes",
1021
- message: `[${r}] Double ${i} has ${a.length} side toes (max 2; the center toe is the main line)`
1022
- }), a.forEach((u, f) => {
1023
- const c = u.dominoes[0];
1024
- c && c.value1 !== s.value1 && o.push({
1025
- code: "foot-connection",
1026
- message: `[${r}] Toe ${f} on double ${i} starts with ${c.value1} but the double is ${s.value1}`
1027
- }), t(u, `${r}.${i}.${f}`);
1028
- });
1029
- }
1261
+ function he(e, t, n, o, r) {
1262
+ const i = se(e.scale * t, o, r), l = i / e.scale;
1263
+ return {
1264
+ scale: i,
1265
+ x: n.x - (n.x - e.x) * l,
1266
+ y: n.y - (n.y - e.y) * l
1030
1267
  };
1031
- return t(e, "main"), { valid: o.length === 0, issues: o };
1032
1268
  }
1033
- function xo(e, o = B) {
1034
- const t = [];
1035
- e.forEach((r, l) => {
1036
- if (t.push(
1037
- ...K(r.dominoes).issues.map((i) => ({
1038
- ...i,
1039
- message: `[segment ${l} @${r.angle}°] ${i.message}`
1040
- }))
1041
- ), r.layout.length !== r.dominoes.length && t.push({
1042
- code: "layout-length",
1043
- message: `[segment ${l}] Layout length ${r.layout.length} does not match domino count ${r.dominoes.length}`
1044
- }), r.anchor && r.layout.length > 0) {
1045
- const i = r.layout[0], s = pe(
1046
- i.x - r.anchor.x,
1047
- i.y - r.anchor.y,
1048
- r.angle
1049
- ), a = I / 2;
1050
- Math.abs(s - a) > o && t.push({
1051
- code: "foot-anchor",
1052
- message: `[segment ${l}] First toe tile sits ${s.toFixed(2)}px from the double along the toe (expected ${a}px)`,
1053
- index: 0
1054
- });
1055
- }
1056
- });
1057
- const n = e.flatMap((r) => r.layout);
1058
- for (let r = 0; r < n.length; r++)
1059
- for (let l = r + 1; l < n.length; l++)
1060
- ae(n[r], n[l]) && t.push({
1061
- code: "tile-overlap",
1062
- message: `Tiles ${r} and ${l} overlap`,
1063
- index: l
1064
- });
1065
- return { valid: t.length === 0, issues: t };
1269
+ function ft(e, t, n, o, r) {
1270
+ const i = Math.max(1, e.width), l = Math.max(1, e.height), s = Math.min(
1271
+ (t.width - n * 2) / i,
1272
+ (t.height - n * 2) / l
1273
+ ), a = se(s, o, r);
1274
+ return {
1275
+ scale: a,
1276
+ x: (t.width - i * a) / 2,
1277
+ y: (t.height - l * a) / 2
1278
+ };
1279
+ }
1280
+ function Lt(e, t) {
1281
+ return {
1282
+ x: (t.x - e.x) / e.scale,
1283
+ y: (t.y - e.y) / e.scale
1284
+ };
1066
1285
  }
1067
- const go = [
1286
+ const pt = 3, _t = ({
1287
+ width: e,
1288
+ height: t,
1289
+ contentWidth: n,
1290
+ contentHeight: o,
1291
+ children: r,
1292
+ minScale: i = 0.2,
1293
+ maxScale: l = 4,
1294
+ zoomStep: s = 1.15,
1295
+ padding: a = 40,
1296
+ background: c = "#1f8a55",
1297
+ showControls: d = !0,
1298
+ testId: u = "viewport"
1299
+ }) => {
1300
+ const f = ce(null), p = Re(
1301
+ () => ft(
1302
+ { width: n, height: o },
1303
+ { width: e, height: t },
1304
+ a,
1305
+ i,
1306
+ l
1307
+ ),
1308
+ [n, o, e, t, a, i, l]
1309
+ ), [b, m] = B(p);
1310
+ ne(() => {
1311
+ m(p());
1312
+ }, [p]);
1313
+ const w = ce(null), z = (h, y) => {
1314
+ const $ = f.current?.getBoundingClientRect();
1315
+ return { x: h - ($?.left ?? 0), y: y - ($?.top ?? 0) };
1316
+ }, v = (h) => {
1317
+ w.current = {
1318
+ pointerX: h.clientX,
1319
+ pointerY: h.clientY,
1320
+ startX: b.x,
1321
+ startY: b.y,
1322
+ moved: !1
1323
+ };
1324
+ }, k = (h) => {
1325
+ const y = w.current;
1326
+ if (!y) return;
1327
+ const $ = h.clientX - y.pointerX, E = h.clientY - y.pointerY;
1328
+ !y.moved && Math.hypot($, E) < pt || (y.moved = !0, f.current?.setPointerCapture?.(h.pointerId), m((A) => ({ ...A, x: y.startX + $, y: y.startY + E })));
1329
+ }, x = (h) => {
1330
+ w.current?.moved && h.preventDefault(), w.current = null;
1331
+ }, S = (h) => {
1332
+ h.preventDefault();
1333
+ const y = z(h.clientX, h.clientY), $ = h.deltaY < 0 ? s : 1 / s;
1334
+ m((E) => he(E, $, y, i, l));
1335
+ }, C = (h) => m(
1336
+ (y) => he(y, h, { x: e / 2, y: t / 2 }, i, l)
1337
+ ), g = {
1338
+ width: 32,
1339
+ height: 32,
1340
+ fontSize: 18,
1341
+ lineHeight: "30px",
1342
+ textAlign: "center",
1343
+ cursor: "pointer",
1344
+ background: "#fff",
1345
+ border: "1px solid #d1d5db",
1346
+ borderRadius: 6,
1347
+ userSelect: "none"
1348
+ };
1349
+ return /* @__PURE__ */ D(
1350
+ "div",
1351
+ {
1352
+ ref: f,
1353
+ "data-testid": u,
1354
+ onPointerDown: v,
1355
+ onPointerMove: k,
1356
+ onPointerUp: x,
1357
+ onPointerLeave: x,
1358
+ onWheel: S,
1359
+ style: {
1360
+ position: "relative",
1361
+ width: e,
1362
+ height: t,
1363
+ overflow: "hidden",
1364
+ background: c,
1365
+ borderRadius: 8,
1366
+ cursor: "grab",
1367
+ touchAction: "none"
1368
+ },
1369
+ children: [
1370
+ /* @__PURE__ */ M(
1371
+ "div",
1372
+ {
1373
+ "data-testid": `${u}-content`,
1374
+ style: {
1375
+ position: "absolute",
1376
+ left: 0,
1377
+ top: 0,
1378
+ transformOrigin: "0 0",
1379
+ transform: `translate(${b.x}px, ${b.y}px) scale(${b.scale})`
1380
+ },
1381
+ children: r
1382
+ }
1383
+ ),
1384
+ d && /* @__PURE__ */ D(
1385
+ "div",
1386
+ {
1387
+ "data-testid": `${u}-controls`,
1388
+ style: {
1389
+ position: "absolute",
1390
+ right: 12,
1391
+ bottom: 12,
1392
+ display: "flex",
1393
+ flexDirection: "column",
1394
+ gap: 6
1395
+ },
1396
+ children: [
1397
+ /* @__PURE__ */ M("div", { style: g, role: "button", "aria-label": "Zoom in", onClick: () => C(s), children: "+" }),
1398
+ /* @__PURE__ */ M("div", { style: g, role: "button", "aria-label": "Zoom out", onClick: () => C(1 / s), children: "−" }),
1399
+ /* @__PURE__ */ M(
1400
+ "div",
1401
+ {
1402
+ style: { ...g, fontSize: 12, lineHeight: "30px" },
1403
+ role: "button",
1404
+ "aria-label": "Reset view",
1405
+ onClick: () => m(p()),
1406
+ children: "⤢"
1407
+ }
1408
+ ),
1409
+ /* @__PURE__ */ M(
1410
+ "div",
1411
+ {
1412
+ "data-testid": `${u}-zoom-readout`,
1413
+ style: { ...g, fontSize: 11, cursor: "default" },
1414
+ children: Math.round(se(b.scale, i, l) * 100)
1415
+ }
1416
+ )
1417
+ ]
1418
+ }
1419
+ )
1420
+ ]
1421
+ }
1422
+ );
1423
+ }, Nt = [
1068
1424
  {
1069
1425
  id: "regular-after-double",
1070
1426
  name: "Regular after double",
@@ -1151,7 +1507,7 @@ const go = [
1151
1507
  ],
1152
1508
  layoutStyles: ["linear", "offset"]
1153
1509
  }
1154
- ], ho = [
1510
+ ], Bt = [
1155
1511
  {
1156
1512
  id: "single-foot",
1157
1513
  name: "Single foot",
@@ -1259,7 +1615,7 @@ const go = [
1259
1615
  },
1260
1616
  layoutStyles: ["linear", "offset"]
1261
1617
  }
1262
- ], G = {
1618
+ ], te = {
1263
1619
  maxPips: 12,
1264
1620
  engineValue: 12,
1265
1621
  allowConsecutiveDoubles: !1,
@@ -1271,7 +1627,7 @@ const go = [
1271
1627
  sideToeAngles: [-45, 45]
1272
1628
  }
1273
1629
  };
1274
- function Ge(e) {
1630
+ function ht(e) {
1275
1631
  switch (e.doubleObligation) {
1276
1632
  case "chicken-foot":
1277
1633
  return Math.max(1, e.chickenFoot.toeCount);
@@ -1281,89 +1637,89 @@ function Ge(e) {
1281
1637
  return 0;
1282
1638
  }
1283
1639
  }
1284
- function Ke(e) {
1640
+ function xt(e) {
1285
1641
  return e.doubleObligation === "chicken-foot" ? Math.max(0, e.chickenFoot.toeCount - 1) : 0;
1286
1642
  }
1287
- function bo(e = {}) {
1288
- const o = e.maxPips ?? G.maxPips;
1643
+ function jt(e = {}) {
1644
+ const t = e.maxPips ?? te.maxPips;
1289
1645
  return {
1290
- ...G,
1646
+ ...te,
1291
1647
  ...e,
1292
- maxPips: o,
1293
- engineValue: e.engineValue ?? o,
1648
+ maxPips: t,
1649
+ engineValue: e.engineValue ?? t,
1294
1650
  chickenFoot: {
1295
- ...G.chickenFoot,
1651
+ ...te.chickenFoot,
1296
1652
  ...e.chickenFoot ?? {}
1297
1653
  }
1298
1654
  };
1299
1655
  }
1300
- function Je(e, o) {
1301
- let t = e;
1302
- for (const n of o)
1303
- if (t = t?.feet?.[n.doubleIndex]?.[n.toeIndex], !t) return;
1304
- return t;
1656
+ function gt(e, t) {
1657
+ let n = e;
1658
+ for (const o of t)
1659
+ if (n = n?.feet?.[o.doubleIndex]?.[o.toeIndex], !n) return;
1660
+ return n;
1305
1661
  }
1306
- function U(e, o, t) {
1307
- if (t(e, o), !!e.feet)
1308
- for (const n of Object.keys(e.feet)) {
1309
- const r = Number(n);
1310
- e.feet[r].forEach((l, i) => {
1311
- U(l, [...o, { doubleIndex: r, toeIndex: i }], t);
1662
+ function H(e, t, n) {
1663
+ if (n(e, t), !!e.feet)
1664
+ for (const o of Object.keys(e.feet)) {
1665
+ const r = Number(o);
1666
+ e.feet[r].forEach((i, l) => {
1667
+ H(i, [...t, { doubleIndex: r, toeIndex: l }], n);
1312
1668
  });
1313
1669
  }
1314
1670
  }
1315
- function Qe(e) {
1316
- const o = [];
1317
- return U(e, [], (t, n) => {
1318
- t.dominoes.forEach((r, l) => {
1671
+ function vt(e) {
1672
+ const t = [];
1673
+ return H(e, [], (n, o) => {
1674
+ n.dominoes.forEach((r, i) => {
1319
1675
  if (r.value1 !== r.value2) return;
1320
- const i = l < t.dominoes.length - 1, s = t.feet?.[l]?.length ?? 0;
1321
- o.push({
1322
- path: n,
1323
- doubleIndex: l,
1676
+ const l = i < n.dominoes.length - 1, s = n.feet?.[i]?.length ?? 0;
1677
+ t.push({
1678
+ path: o,
1679
+ doubleIndex: i,
1324
1680
  value: r.value1,
1325
- hasCenter: i,
1681
+ hasCenter: l,
1326
1682
  sideToes: s,
1327
- answers: (i ? 1 : 0) + s
1683
+ answers: (l ? 1 : 0) + s
1328
1684
  });
1329
1685
  });
1330
- }), o;
1686
+ }), t;
1331
1687
  }
1332
- function Ze(e, o) {
1333
- const t = Ge(o);
1334
- return t <= 0 ? [] : Qe(e).filter((n) => n.answers < t);
1688
+ function bt(e, t) {
1689
+ const n = ht(t);
1690
+ return n <= 0 ? [] : vt(e).filter((o) => o.answers < n);
1335
1691
  }
1336
- function He(e) {
1337
- const o = /* @__PURE__ */ new Set();
1338
- return U(e, [], (t) => {
1339
- for (const n of t.dominoes)
1340
- o.add(ce(n));
1341
- }), o;
1692
+ function yt(e) {
1693
+ const t = /* @__PURE__ */ new Set();
1694
+ return H(e, [], (n) => {
1695
+ for (const o of n.dominoes)
1696
+ t.add(ke(o));
1697
+ }), t;
1342
1698
  }
1343
- function We(e, o, t) {
1699
+ function wt(e, t, n) {
1344
1700
  if (e.dominoes.length === 0)
1345
1701
  return [
1346
1702
  {
1347
1703
  path: [],
1348
1704
  attach: "run-tail",
1349
- value: o,
1705
+ value: t,
1350
1706
  attachToDouble: !0,
1351
1707
  // a train starts off the engine double
1352
1708
  obligation: !1
1353
1709
  }
1354
1710
  ];
1355
- const n = Ze(e, t);
1356
- if (t.doubleObligation !== "none" && n.length > 0) {
1357
- const l = Ke(t), i = [];
1358
- for (const s of n) {
1359
- const a = Je(e, s.path);
1360
- a && (!s.hasCenter && s.doubleIndex === a.dominoes.length - 1 && i.push({
1711
+ const o = bt(e, n);
1712
+ if (n.doubleObligation !== "none" && o.length > 0) {
1713
+ const i = xt(n), l = [];
1714
+ for (const s of o) {
1715
+ const a = gt(e, s.path);
1716
+ a && (!s.hasCenter && s.doubleIndex === a.dominoes.length - 1 && l.push({
1361
1717
  path: s.path,
1362
1718
  attach: "run-tail",
1363
1719
  value: s.value,
1364
1720
  attachToDouble: !0,
1365
1721
  obligation: !0
1366
- }), s.sideToes < l && i.push({
1722
+ }), s.sideToes < i && l.push({
1367
1723
  path: s.path,
1368
1724
  attach: "side-toe",
1369
1725
  value: s.value,
@@ -1373,109 +1729,129 @@ function We(e, o, t) {
1373
1729
  obligation: !0
1374
1730
  }));
1375
1731
  }
1376
- return i;
1732
+ return l;
1377
1733
  }
1378
1734
  const r = [];
1379
- return U(e, [], (l, i) => {
1380
- const s = l.dominoes[l.dominoes.length - 1];
1735
+ return H(e, [], (i, l) => {
1736
+ const s = i.dominoes[i.dominoes.length - 1];
1381
1737
  s && r.push({
1382
- path: i,
1738
+ path: l,
1383
1739
  attach: "run-tail",
1384
1740
  value: s.value2,
1385
- attachToDouble: de(s),
1741
+ attachToDouble: Ce(s),
1386
1742
  obligation: !1
1387
1743
  });
1388
1744
  }), r;
1389
1745
  }
1390
- function xe(e, o, t, n) {
1391
- const r = [], l = fe(e, o.value);
1392
- return n.requireSequential && !l && r.push("value-mismatch"), n.requireUniqueTiles && t.has(ce(e)) && r.push("duplicate-tile"), !n.allowConsecutiveDoubles && o.attachToDouble && de(e) && r.push("consecutive-doubles"), { legal: r.length === 0, violations: r };
1746
+ function Pe(e, t, n, o) {
1747
+ const r = [], i = Te(e, t.value);
1748
+ return o.requireSequential && !i && r.push("value-mismatch"), o.requireUniqueTiles && n.has(ke(e)) && r.push("duplicate-tile"), !o.allowConsecutiveDoubles && t.attachToDouble && Ce(e) && r.push("consecutive-doubles"), { legal: r.length === 0, violations: r };
1393
1749
  }
1394
- function wo(e, o, t, n, r) {
1395
- const l = We(e, o, r), i = [];
1396
- for (const s of l)
1397
- for (const a of t)
1398
- xe(a, s, n, r).legal && i.push({ end: s, tile: a });
1399
- return i;
1750
+ function Ut(e, t, n, o, r) {
1751
+ const i = wt(e, t, r), l = [];
1752
+ for (const s of i)
1753
+ for (const a of n)
1754
+ Pe(a, s, o, r).legal && l.push({ end: s, tile: a });
1755
+ return l;
1400
1756
  }
1401
- function ge(e, o, t) {
1402
- if (o.length === 0)
1403
- return t(e);
1404
- const [n, ...r] = o, i = (e.feet?.[n.doubleIndex] ?? []).map(
1405
- (s, a) => a === n.toeIndex ? ge(s, r, t) : s
1757
+ function Oe(e, t, n) {
1758
+ if (t.length === 0)
1759
+ return n(e);
1760
+ const [o, ...r] = t, l = (e.feet?.[o.doubleIndex] ?? []).map(
1761
+ (s, a) => a === o.toeIndex ? Oe(s, r, n) : s
1406
1762
  );
1407
1763
  return {
1408
1764
  ...e,
1409
- feet: { ...e.feet, [n.doubleIndex]: i }
1765
+ feet: { ...e.feet, [o.doubleIndex]: l }
1410
1766
  };
1411
1767
  }
1412
- function eo(e, o, t) {
1413
- const n = fe(o.tile, o.end.value) ?? { ...o.tile };
1414
- return ge(e, o.end.path, (r) => {
1415
- if (o.end.attach === "run-tail")
1416
- return { ...r, dominoes: [...r.dominoes, n] };
1417
- const l = o.end.doubleIndex ?? 0, i = o.end.toeSlot ?? r.feet?.[l]?.length ?? 0, s = r.feet?.[l] ? [...r.feet[l]] : [];
1418
- return s[i] = { dominoes: [n] }, {
1768
+ function St(e, t, n) {
1769
+ const o = Te(t.tile, t.end.value) ?? { ...t.tile };
1770
+ return Oe(e, t.end.path, (r) => {
1771
+ if (t.end.attach === "run-tail")
1772
+ return { ...r, dominoes: [...r.dominoes, o] };
1773
+ const i = t.end.doubleIndex ?? 0, l = t.end.toeSlot ?? r.feet?.[i]?.length ?? 0, s = r.feet?.[i] ? [...r.feet[i]] : [];
1774
+ return s[l] = { dominoes: [o] }, {
1419
1775
  ...r,
1420
- feet: { ...r.feet, [l]: s }
1776
+ feet: { ...r.feet, [i]: s }
1421
1777
  };
1422
1778
  });
1423
1779
  }
1424
- function So(e, o, t) {
1425
- const n = xe(
1426
- o.tile,
1427
- o.end,
1428
- He(e),
1429
- t
1780
+ function Vt(e, t, n) {
1781
+ const o = Pe(
1782
+ t.tile,
1783
+ t.end,
1784
+ yt(e),
1785
+ n
1430
1786
  );
1431
- return n.legal ? { ok: !0, board: eo(e, o), violations: [] } : { ok: !1, board: e, violations: n.violations };
1787
+ return o.legal ? { ok: !0, board: St(e, t), violations: [] } : { ok: !1, board: e, violations: o.violations };
1432
1788
  }
1433
1789
  export {
1434
- ho as CHICKEN_FOOT_FIXTURES,
1435
- Ie as CHICKEN_FOOT_TOE_ANGLES,
1436
- X as DEFAULT_PIP_COLORS,
1437
- G as DEFAULT_RULES,
1438
- Re as DominoHub,
1439
- Ae as DominoTrain,
1440
- ie as DoubleTwelve,
1441
- fo as MexicanTrainGame,
1442
- no as PIP_COLORS,
1443
- Me as PIP_LAYOUTS,
1444
- go as TRAIN_FIXTURES,
1445
- eo as applyMove,
1446
- He as collectPlayedKeys,
1447
- Oe as computeTrainLayout,
1448
- ue as computeTrainTree,
1449
- ce as dominoKey,
1450
- co as dominoSetSize,
1451
- xe as evaluatePlacement,
1452
- Fe as flattenSegments,
1453
- uo as generateDominoSet,
1454
- Je as getBranchAt,
1455
- wo as getLegalMoves,
1456
- We as getOpenEnds,
1457
- Ce as getPipLayout,
1458
- lo as getPipStyle,
1459
- io as getTrainLayoutBounds,
1460
- Ze as getUnsatisfiedDoubles,
1461
- de as isDouble,
1462
- ro as mergePipColors,
1463
- fe as orientForConnection,
1464
- ao as otherEnd,
1465
- j as outwardPerpSign,
1466
- So as playMove,
1467
- Ge as requiredDoubleAnswers,
1468
- ze as resolvePipPosition,
1469
- le as resolvePipStyle,
1470
- bo as resolveRules,
1471
- Ke as sideToeSlots,
1472
- k as stepAlongTrain,
1473
- te as tileCorners,
1474
- so as tileHasValue,
1475
- F as tileKey,
1476
- ae as tilesOverlap,
1477
- vo as validateChickenFootChain,
1478
- po as validateTrainLayout,
1479
- xo as validateTrainTree
1790
+ Bt as CHICKEN_FOOT_FIXTURES,
1791
+ Ue as CHICKEN_FOOT_TOE_ANGLES,
1792
+ j as DEFAULT_PIP_COLORS,
1793
+ te as DEFAULT_RULES,
1794
+ rt as DominoHub,
1795
+ ot as DominoTrain,
1796
+ ve as DoubleTwelve,
1797
+ Xt as MexicanTrainGame,
1798
+ Mt as PIP_COLORS,
1799
+ _e as PIP_LAYOUTS,
1800
+ Nt as TRAIN_FIXTURES,
1801
+ le as TURN_DEGREES,
1802
+ _t as Viewport,
1803
+ St as applyMove,
1804
+ De as buildBranchTiles,
1805
+ se as clampScale,
1806
+ yt as collectPlayedKeys,
1807
+ Ke as computeTrainLayout,
1808
+ re as computeTrainTree,
1809
+ Ft as cycleBendAt,
1810
+ ke as dominoKey,
1811
+ Et as dominoSetSize,
1812
+ Pe as evaluatePlacement,
1813
+ ft as fitToBounds,
1814
+ ze as flattenSegments,
1815
+ Ot as generateDominoSet,
1816
+ it as generateSampleTrains,
1817
+ gt as getBranchAt,
1818
+ Ut as getLegalMoves,
1819
+ wt as getOpenEnds,
1820
+ Ne as getPipLayout,
1821
+ kt as getPipStyle,
1822
+ Ct as getTrainLayoutBounds,
1823
+ bt as getUnsatisfiedDoubles,
1824
+ Ge as headingAtIndex,
1825
+ nt as hubTrainStartDistance,
1826
+ Ce as isDouble,
1827
+ me as layoutSelfIntersects,
1828
+ Se as layoutsCollide,
1829
+ Rt as linearDefaultSide,
1830
+ $t as mergePipColors,
1831
+ we as normalizeBends,
1832
+ Yt as offsetDefaultSide,
1833
+ Ie as oppositeSide,
1834
+ Te as orientForConnection,
1835
+ Pt as otherEnd,
1836
+ U as outwardPerpSign,
1837
+ Vt as playMove,
1838
+ ht as requiredDoubleAnswers,
1839
+ At as resolveBend,
1840
+ Fe as resolvePipPosition,
1841
+ ge as resolvePipStyle,
1842
+ jt as resolveRules,
1843
+ Lt as screenToContent,
1844
+ oe as sideToTurn,
1845
+ xt as sideToeSlots,
1846
+ O as stepAlongTrain,
1847
+ fe as tileCorners,
1848
+ Dt as tileHasValue,
1849
+ L as tileKey,
1850
+ J as tilesOverlap,
1851
+ et as validateChickenFootChain,
1852
+ Tt as validateTrainLayout,
1853
+ It as validateTrainTree,
1854
+ K as withBendAt,
1855
+ he as zoomAt
1480
1856
  };
1481
1857
  //# sourceMappingURL=index.js.map