doubletwelve 0.1.0 → 0.3.1

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.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("react/jsx-runtime"),j=require("react"),Ee={"3x3":{rows:[20,50,80],cols:[20,50,80],size:"18%"},"3x4":{rows:[24,50,76],cols:[22,40,60,78],size:"12%"},"4x3":{rows:[15,38,62,85],cols:[20,50,80],size:"14%"}};function de(e){const o=Ee[e.gridSize];return{top:e.top??`${o.rows[e.row]}%`,left:e.left??`${o.cols[e.col]}%`,width:o.size,height:o.size}}const je=({row:e,col:o,gridSize:t,color:n,hollow:r,top:i,left:l})=>{const s=de({row:e,col:o,gridSize:t,top:i,left:l});return v.jsx("div",{"data-testid":"pip","data-row":e,"data-col":o,"data-grid":t,style:{position:"absolute",backgroundColor:r?"transparent":n,border:r?"2px solid #888":void 0,borderRadius:"50%",transform:"translate(-50%, -50%)",boxShadow:r?void 0:"1px 2px 3px rgba(0,0,0,0.3)",...s}})},fe={0:[],1:[{row:1,col:1,gridSize:"3x3"}],2:[{row:0,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"}],3:[{row:0,col:2,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"}],4:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],5:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],6:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],7:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],8:[{row:0,col:0,gridSize:"3x3"},{row:0,col:1,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:1,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],9:[{row:0,col:0,gridSize:"3x3"},{row:0,col:1,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:1,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],10:[{row:0,col:0,gridSize:"3x4"},{row:0,col:1,gridSize:"3x4"},{row:0,col:2,gridSize:"3x4"},{row:0,col:3,gridSize:"3x4"},{row:1,col:0,gridSize:"3x4"},{row:1,col:3,gridSize:"3x4"},{row:2,col:0,gridSize:"3x4"},{row:2,col:1,gridSize:"3x4"},{row:2,col:2,gridSize:"3x4"},{row:2,col:3,gridSize:"3x4"}],11:[{row:0,col:0,gridSize:"4x3"},{row:1,col:0,gridSize:"4x3"},{row:2,col:0,gridSize:"4x3"},{row:3,col:0,gridSize:"4x3"},{row:0,col:1,gridSize:"4x3"},{row:2,col:1,gridSize:"4x3",top:"50%"},{row:3,col:1,gridSize:"4x3"},{row:0,col:2,gridSize:"4x3"},{row:1,col:2,gridSize:"4x3"},{row:2,col:2,gridSize:"4x3"},{row:3,col:2,gridSize:"4x3"}],12:[{row:0,col:0,gridSize:"4x3"},{row:1,col:0,gridSize:"4x3"},{row:2,col:0,gridSize:"4x3"},{row:3,col:0,gridSize:"4x3"},{row:0,col:1,gridSize:"4x3"},{row:1,col:1,gridSize:"4x3"},{row:2,col:1,gridSize:"4x3"},{row:3,col:1,gridSize:"4x3"},{row:0,col:2,gridSize:"4x3"},{row:1,col:2,gridSize:"4x3"},{row:2,col:2,gridSize:"4x3"},{row:3,col:2,gridSize:"4x3"}]};function pe(e){return fe[e]??[]}const F={0:{color:"transparent"},1:{color:"#1a1a1a"},2:{color:"#8B1A1A"},3:{color:"#E6B800"},4:{color:"#e8e8e8",hollow:!0},5:{color:"#2E8B57"},6:{color:"#2563EB"},7:{color:"#E8A87C"},8:{color:"#DC2626"},9:{color:"#1E3A8A"},10:{color:"#EA580C"},11:{color:"#166534"},12:{color:"#DC2626"}},Fe=F;function Ae(e){return{...F,...e}}function K(e,o){if(o!==void 0)return o[e]??F[e]??{color:"#1a1a1a"}}function Le(e){return K(e,F)}const _e=(e,o,t)=>{const n=K(e,t);return n?{color:n.color,hollow:n.hollow}:{color:o}},Re=({value:e,pipColor:o,pipColors:t})=>{const{color:n,hollow:r}=_e(e,o,t),i=pe(e);return v.jsx(v.Fragment,{children:i.map((l,s)=>v.jsx(je,{row:l.row,col:l.col,gridSize:l.gridSize,color:n,hollow:r,top:l.top,left:l.left},s))})},ae=({value:e,pipColor:o,pipColors:t})=>v.jsx("div",{style:{width:"100%",height:"100%",position:"relative",padding:"0",overflow:"hidden"},children:v.jsx(Re,{value:e,pipColor:o,pipColors:t})}),J=({value1:e=0,value2:o=0,width:t=100,height:n=200,backgroundColor:r="white",pipColor:i="black",pipColors:l,borderColor:s="black",rotation:a=0})=>{const u=Math.min(Math.max(e,0),12),f=Math.min(Math.max(o,0),12);return v.jsxs("div",{style:{width:`${t}px`,height:`${n}px`,backgroundColor:r,borderColor:s,borderWidth:"1px",borderStyle:"solid",borderRadius:"10px",transform:`rotate(${a}deg)`,transformOrigin:"center center",boxShadow:"0 1px 2px rgba(0,0,0,0.2)",display:"flex",flexDirection:"column",overflow:"hidden"},children:[v.jsx("div",{style:{flex:1,position:"relative",borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:s},children:v.jsx(ae,{value:u,pipColor:i,pipColors:l})}),v.jsx("div",{style:{flex:1,position:"relative"},children:v.jsx(ae,{value:f,pipColor:i,pipColors:l})})]})},D=60,O=120,ve=[-45,45];function ue(e,o=D,t=O){return e?o/2:t/2}function z(e,o,t=D,n=O){return ue(e,t,n)+ue(o,t,n)}function _(e){const o=e*Math.PI/180;return{dirX:Math.cos(o),dirY:Math.sin(o)}}function Y(e){const{dirX:o,dirY:t}=_(e);return{perpX:-t,perpY:o}}function Xe(e,o){const t=e.map(n=>({...n}));for(let n=1;n<t.length;n++){const r=t[n],l=t[n-1].value2,s=r.value1===r.value2;if(o==="linear"&&!s){t[n]={value1:r.value2,value2:r.value1};continue}o==="offset"&&!s&&r.value1!==l&&r.value2===l&&(t[n]={value1:r.value2,value2:r.value1})}return t}function R(e){const{dirX:o,dirY:t}=_(e);return Math.abs(o)>=Math.abs(t)?o>=0?1:-1:t>=0?1:-1}function xe(e,o){return e===0?o:e===o?-o:o}function ge({startX:e,startY:o,angle:t,dominoes:n,layoutStyle:r,dominoWidth:i=D,dominoHeight:l=O,leadGap:s=l*.3,outwardSign:a,hubIndex:u}){const f=[],{dirX:c,dirY:d}=_(t),{perpX:x,perpY:y}=Y(t),g=Xe([...n],r),S=a??R(t),T=r==="offset"&&u!=null,C=[];let m=e+c*s,p=o+d*s,b=0,M=0;const I=i/2,A=h=>{const w=(h-b)*I;m+=x*w,p+=y*w,b=h};for(let h=0;h<g.length;h++){const w=g[h],k=w.value1===w.value2,$=h>0&&g[h-1].value1===g[h-1].value2;r==="linear"?h>0&&(k?(m+=c*z($,!0,i,l),p+=d*z($,!0,i,l)):$?(m+=c*z(!0,!1,i,l),p+=d*z(!0,!1,i,l)):(m+=c*l,p+=d*l)):k?h>0&&(m+=c*z($,!0,i,l),p+=d*z($,!0,i,l)):(h===0?M=S:$?(m+=c*z(!0,!1,i,l),p+=d*z(!0,!1,i,l)):(m+=c*(l/2),p+=d*(l/2),M=xe(M,S)),A(M)),C.push(b),f.push({x:m,y:p,rotation:k?t+180:t-90,isDouble:k,value1:w.value1,value2:w.value2})}if(T&&u!=null){const h=-C[u]*I;if(h!==0)for(let w=0;w<f.length;w++)f[w]={...f[w],x:f[w].x+x*h,y:f[w].y+y*h}}return f}function G(e,o=D,t=O){const n=e.rotation*Math.PI/180,r=Math.cos(n),i=Math.sin(n),l=o/2,s=t/2;return[[-l,-s],[l,-s],[l,s],[-l,s]].map(([a,u])=>({x:e.x+a*r-u*i,y:e.y+a*i+u*r}))}function Ne(e,o,t){let n=1/0,r=-1/0,i=1/0,l=-1/0;for(const s of e){const a=s.x*t.x+s.y*t.y;n=Math.min(n,a),r=Math.max(r,a)}for(const s of o){const a=s.x*t.x+s.y*t.y;i=Math.min(i,a),l=Math.max(l,a)}return Math.min(r,l)-Math.max(n,i)}function Q(e,o,t=1,n=D,r=O){const i=G(e,n,r),l=G(o,n,r);for(const s of[i,l])for(let a=0;a<4;a++){const u=s[a],f=s[(a+1)%4],c=f.x-u.x,d=f.y-u.y,x=Math.hypot(c,d)||1,y={x:-d/x,y:c/x};if(Ne(i,l,y)<=t)return!1}return!0}function Ye(e,o,t,n){return o.some(r=>Q(e,r,1,t,n))}const P=D/4,ce=24;function Z({startX:e,startY:o,angle:t,branch:n,layoutStyle:r,dominoWidth:i=D,dominoHeight:l=O,leadGap:s,depth:a=0,anchor:u,outwardSign:f,placed:c=[],pushAxis:d,minPushSteps:x=0}){const y=f??R(t),g=n.feet?Object.keys(n.feet).map(Number).filter(p=>{const b=n.dominoes[p];return b&&b.value1===b.value2}).sort((p,b)=>p-b)[0]:void 0,S=(p,b)=>ge({startX:p,startY:b,angle:t,dominoes:n.dominoes,layoutStyle:r,dominoWidth:i,dominoHeight:l,leadGap:s,outwardSign:y,hubIndex:g});let T=S(e+(d?.x??0)*P*x,o+(d?.y??0)*P*x),C=u&&d?{x:u.x+d.x*P*x,y:u.y+d.y*P*x}:u;if(d&&c.length>0)for(let p=x;p<=ce;p++){const b=e+d.x*P*p,M=o+d.y*P*p,I=S(b,M);if(!I.some(h=>Ye(h,c,i,l))||p===ce){T=I,C=u&&{x:u.x+d.x*P*p,y:u.y+d.y*P*p};break}}c.push(...T);const m=[{angle:t,depth:a,layoutStyle:r,outwardSign:y,dominoes:n.dominoes,layout:T,anchor:C}];if(n.feet){const{dirX:p,dirY:b}=_(t),{perpX:M,perpY:I}=Y(t),A=i/2,h=l/2;for(const w of Object.keys(n.feet)){const k=Number(w),$=T[k],B=n.feet[k];if(!(!$||!$.isDouble||!B))for(let X=0;X<B.length;X++){const Ie=B[X],ne=ve[X]??0,L=Math.sign(ne),re=t+ne,ie=Y(re),V=-L,Pe=$.x+p*(i/2)+M*(l/2)*L,ke=$.y+b*(i/2)+I*(l/2)*L,le=Pe-ie.perpX*V*A,se=ke-ie.perpY*V*A;m.push(...Z({startX:le,startY:se,angle:re,branch:Ie,layoutStyle:r,dominoWidth:i,dominoHeight:l,leadGap:h,outwardSign:V,depth:a+1,anchor:{x:le,y:se},placed:c,pushAxis:{x:M*L,y:I*L}}))}}}return m}function he(e){return e.flatMap(o=>o.layout)}function Ue(e,o=24,t=D,n=O){const r=Math.hypot(t,n)/2;if(e.length===0)return{width:o*2+t,height:o*2+n,offsetX:o,offsetY:o};let i=1/0,l=1/0,s=-1/0,a=-1/0;for(const u of e)i=Math.min(i,u.x-r),l=Math.min(l,u.y-r),s=Math.max(s,u.x+r),a=Math.max(a,u.y+r);return{width:Math.ceil(s-i+o*2),height:Math.ceil(a-l+o*2),offsetX:o-i,offsetY:o-l}}const be=({startX:e,startY:o,angle:t,trainData:n,layoutStyle:r,tableWidth:i,tableHeight:l,centerX:s,centerY:a,pipColors:u})=>{const f=j.useMemo(()=>he(Z({startX:e,startY:o,angle:t,branch:{dominoes:n.dominoes,feet:n.feet},layoutStyle:r})),[e,o,t,n.dominoes,n.feet,r,i,l]);return v.jsx(v.Fragment,{children:f.map((c,d)=>{const x=n.isPublic;return v.jsx("div",{style:{position:"absolute",left:`${c.x-D/2}px`,top:`${c.y-O/2}px`,zIndex:5},children:v.jsx(J,{value1:c.value1,value2:c.value2,width:D,height:O,backgroundColor:"white",pipColor:"black",pipColors:u,borderColor:x?"red":"black",rotation:c.rotation})},`main-train-${n.playerId}-${d}`)})})},Se=({playerCount:e,centerX:o,centerY:t,radius:n,engineValue:r,trains:i,layoutStyle:l,tableWidth:s,tableHeight:a,pipColors:u})=>{const f=Math.max(8,e),c=120;return v.jsxs("div",{style:{position:"relative",width:"100%",height:"100%"},children:[v.jsx("div",{style:{position:"absolute",width:`${c}px`,height:`${c}px`,left:`${o-c/2}px`,top:`${t-c/2}px`,backgroundColor:"#d1d5db",borderWidth:"3px",borderStyle:"solid",borderColor:"#6b7280",borderRadius:"50%",boxShadow:"0 4px 6px rgba(0,0,0,0.1)",zIndex:10,display:"flex",justifyContent:"center",alignItems:"center"},children:v.jsx("div",{style:{transform:"rotate(0deg)"},children:v.jsx(J,{value1:r,value2:r,width:60,height:120,backgroundColor:"white",pipColor:"black",pipColors:u,borderColor:"#333"})})}),Array.from({length:f}).map((y,g)=>{const S=g*360/f,T=S*Math.PI/180,C=o+(n+20)*Math.cos(T),m=t+(n+20)*Math.sin(T),p=i.find(b=>b.playerId===g)||{dominoes:[],playerId:g,isPublic:!1};return v.jsx(be,{startX:C,startY:m,angle:S,trainData:p,layoutStyle:l,tableWidth:s,tableHeight:a,centerX:o,centerY:t,pipColors:u},g)})]})};function E(e,o){return e<=o?`${e}:${o}`:`${o}:${e}`}function H(e){return E(e.value1,e.value2)}function W(e){return e.value1===e.value2}function qe(e,o){return e.value1===o||e.value2===o}function Be(e,o){return e.value1===o?e.value2:e.value2===o?e.value1:null}function ee(e,o){return e.value1===o?{value1:e.value1,value2:e.value2}:e.value2===o?{value1:e.value2,value2:e.value1}:null}function Ve(e){const o=[];for(let t=0;t<=e;t++)for(let n=t;n<=e;n++)o.push({value1:t,value2:n});return o}function Ge(e){const o=e+1;return o*(o+1)/2}function Ke(e,o=12,t={}){const n=new Set([E(o,o)]),r=[];for(let i=0;i<e;i++){const l=4+Math.floor(Math.random()*7),s=[];let a=o,u=!1;for(let c=0;c<l;c++){const d=He(a,u,c===0,o,n);if(d===null)break;const x=d===a;n.add(E(a,d)),s.push({value1:a,value2:d}),u=x,a=d}const f=t.chickenFeet?Je(s,n):void 0;r.push({playerId:i,dominoes:s,isPublic:Math.random()>.7,...f?{feet:f}:{}})}return r}function Je(e,o){const t={};for(let n=0;n<e.length;n++){if(e[n].value1!==e[n].value2)continue;const r=e[n].value1,i=[];for(let l=0;l<2;l++){const s=Qe(r,o);s&&i.push(s)}i.length&&(t[n]=i)}return Object.keys(t).length?t:void 0}function Qe(e,o){const t=1+Math.floor(Math.random()*2),n=[];let r=e;for(let i=0;i<t;i++){const l=Ze(r,o);if(l===null)break;o.add(E(r,l)),n.push({value1:r,value2:l}),r=l}return n.length?{dominoes:n}:null}function Ze(e,o){const t=[];for(let n=0;n<13;n++)n!==e&&(o.has(E(e,n))||t.push(n));return t.length===0?null:t[Math.floor(Math.random()*t.length)]}function He(e,o,t,n,r){const i=Array.from({length:13},(l,s)=>s).filter(l=>We(e,l,o,t,n,r));return i.length===0?null:i[Math.floor(Math.random()*i.length)]}function We(e,o,t,n,r,i){const l=o===e;return!(n&&l&&e===r||l&&t||i.has(E(e,o)))}const eo={playerCount:8,trains:[],engineValue:12},oo=({initialState:e=eo,width:o=1200,height:t=800,pipColors:n,onPipColorsChange:r})=>{const[i,l]=j.useState(e),[s,a]=j.useState("offset"),[u,f]=j.useState(!1),[c,d]=j.useState(void 0),x=n??c,y=r??d,g=x!==void 0,S=o/2,T=t/2,C=(p=u)=>{const b=Ke(i.playerCount,i.engineValue,{chickenFeet:p});l(M=>({...M,trains:b}))};j.useEffect(()=>{C()},[]);const m=()=>{const p=!u;f(p),C(p)};return v.jsxs("div",{style:{width:`${o}px`,height:`${t}px`,position:"relative",backgroundColor:"#1f8a55",borderRadius:"8px",boxShadow:"0 4px 12px rgba(0,0,0,0.2)",overflow:"hidden"},children:[v.jsxs("div",{style:{position:"absolute",top:"10px",left:"10px",zIndex:100},children:[v.jsx("button",{onClick:()=>C(),style:{padding:"8px 12px",backgroundColor:"#fff",border:"1px solid #ccc",borderRadius:"4px",marginRight:"10px",cursor:"pointer"},children:"New trains"}),v.jsxs("button",{onClick:()=>a(s==="offset"?"linear":"offset"),style:{padding:"8px 12px",backgroundColor:"#fff",border:"1px solid #ccc",borderRadius:"4px",marginRight:"10px",cursor:"pointer"},children:["Layout: ",s==="offset"?"Offset":"Linear"]}),v.jsxs("button",{onClick:m,style:{padding:"8px 12px",backgroundColor:u?"#fef3c7":"#fff",border:`1px solid ${u?"#f59e0b":"#ccc"}`,borderRadius:"4px",marginRight:"10px",cursor:"pointer"},children:["Chicken Feet: ",u?"On":"Off"]}),v.jsxs("button",{onClick:()=>y(g?void 0:F),style:{padding:"8px 12px",backgroundColor:g?"#fef3c7":"#fff",border:`1px solid ${g?"#f59e0b":"#ccc"}`,borderRadius:"4px",cursor:"pointer"},children:["Pip Colors: ",g?"On":"Off"]})]}),v.jsxs("div",{style:{position:"absolute",top:"10px",right:"10px",zIndex:100,backgroundColor:"rgba(255,255,255,0.8)",padding:"8px",borderRadius:"4px",fontSize:"14px"},children:[v.jsxs("div",{children:["Engine: Double-",i.engineValue]}),v.jsxs("div",{children:["Players: ",i.playerCount]})]}),v.jsx(Se,{playerCount:i.playerCount,centerX:S,centerY:T,radius:80,engineValue:i.engineValue,trains:i.trains,layoutStyle:s,tableWidth:o,tableHeight:t,pipColors:x})]})},U=1;function we(e,o,t){const{dirX:n,dirY:r}=_(t);return e*n+o*r}function to(e,o,t){const{perpX:n,perpY:r}=Y(t);return e*n+o*r}function oe(e){const o=[];for(let t=1;t<e.length;t++)e[t].value1!==e[t-1].value2&&o.push({code:"chain-break",message:`Domino ${t} does not connect to domino ${t-1}`,index:t});for(let t=1;t<e.length;t++){const n=e[t-1].value1===e[t-1].value2,r=e[t].value1===e[t].value2;n&&r&&o.push({code:"consecutive-doubles",message:`Consecutive doubles at index ${t-1} and ${t}`,index:t})}return{valid:o.length===0,issues:o}}function ye(e,o,t,n=D,r=O){const i=n/2,l=e.map(c=>c.isDouble),s=[];let a=0,u=0,f=0;for(let c=0;c<e.length;c++){const d=l[c],x=c>0&&l[c-1];o==="linear"?(c>0&&(a+=z(x,d,n,r)),u=0):d?c>0&&(a+=z(x,!0,n,r)):c===0?(f=t,u=f*i):x?a+=z(!0,!1,n,r):(a+=r/2,f=xe(f,t),u=f*i),s.push({along:a,perp:u})}return s}function no(e,o,t,n=U,r){const i=[],l=r??R(o),s=ye(e,t,l);for(let a=1;a<e.length;a++){const u=e[a-1],f=e[a],c=f.x-u.x,d=f.y-u.y,x=we(c,d,o),y=to(c,d,o),g=s[a].along-s[a-1].along,S=s[a].perp-s[a-1].perp;Math.abs(x-g)>n&&i.push({code:"spacing-along-train",message:`Along-train spacing between domino ${a-1} and ${a} is ${x.toFixed(2)}px (expected ${g}px)`,index:a}),Math.abs(y-S)>n&&i.push({code:"spacing-perpendicular",message:`Perpendicular spacing between domino ${a-1} and ${a} is ${y.toFixed(2)}px (expected ${S}px)`,index:a})}return{valid:i.length===0,issues:i}}function ro(e,o,t,n=U,r){const i=[],l=r??R(o),s=ye(e,t,l);for(let a=1;a<e.length;a++){const u=e[a-1],f=e[a],c=f.x-u.x,d=f.y-u.y,x=Math.hypot(c,d),y=s[a].along-s[a-1].along,g=s[a].perp-s[a-1].perp,S=Math.hypot(y,g)*.9;x+n<S&&i.push({code:"overlap",message:`Domino ${a-1} and ${a} centers are ${x.toFixed(2)}px apart (minimum ${S.toFixed(2)}px)`,index:a})}return{valid:i.length===0,issues:i}}function io(e,o,t,n,r=U,i){const l=[...oe(o).issues,...no(e,t,n,r,i).issues,...ro(e,t,n,r,i).issues];return e.length!==o.length&&l.push({code:"layout-length",message:`Layout length ${e.length} does not match domino count ${o.length}`}),{valid:l.length===0,issues:l}}function lo(e){const o=[],t=(n,r)=>{if(o.push(...oe(n.dominoes).issues.map(i=>({...i,message:`[${r}] ${i.message}`}))),!!n.feet)for(const i of Object.keys(n.feet)){const l=Number(i),s=n.dominoes[l],a=n.feet[l]??[];if(!s){o.push({code:"foot-host-missing",message:`[${r}] Foot references missing tile ${l}`});continue}s.value1!==s.value2&&o.push({code:"foot-host-not-double",message:`[${r}] Foot host tile ${l} is not a double`}),a.length>2&&o.push({code:"foot-too-many-toes",message:`[${r}] Double ${l} has ${a.length} side toes (max 2; the center toe is the main line)`}),a.forEach((u,f)=>{const c=u.dominoes[0];c&&c.value1!==s.value1&&o.push({code:"foot-connection",message:`[${r}] Toe ${f} on double ${l} starts with ${c.value1} but the double is ${s.value1}`}),t(u,`${r}.${l}.${f}`)})}};return t(e,"main"),{valid:o.length===0,issues:o}}function so(e,o=U){const t=[];e.forEach((r,i)=>{if(t.push(...oe(r.dominoes).issues.map(l=>({...l,message:`[segment ${i} @${r.angle}°] ${l.message}`}))),r.layout.length!==r.dominoes.length&&t.push({code:"layout-length",message:`[segment ${i}] Layout length ${r.layout.length} does not match domino count ${r.dominoes.length}`}),r.anchor&&r.layout.length>0){const l=r.layout[0],s=we(l.x-r.anchor.x,l.y-r.anchor.y,r.angle),a=O/2;Math.abs(s-a)>o&&t.push({code:"foot-anchor",message:`[segment ${i}] First toe tile sits ${s.toFixed(2)}px from the double along the toe (expected ${a}px)`,index:0})}});const n=e.flatMap(r=>r.layout);for(let r=0;r<n.length;r++)for(let i=r+1;i<n.length;i++)Q(n[r],n[i])&&t.push({code:"tile-overlap",message:`Tiles ${r} and ${i} overlap`,index:i});return{valid:t.length===0,issues:t}}const ao=[{id:"regular-after-double",name:"Regular after double",description:"Double followed by a two-tile offset run",angle:0,dominoes:[{value1:12,value2:6},{value1:6,value2:6},{value1:6,value2:3},{value1:3,value2:1}],layoutStyles:["linear","offset"]},{id:"double-after-regular",name:"Double after regular",description:"Offset run, a double, then another offset run",angle:0,dominoes:[{value1:12,value2:9},{value1:9,value2:4},{value1:4,value2:4},{value1:4,value2:2},{value1:2,value2:7}],layoutStyles:["linear","offset"]},{id:"double-after-double",name:"Double after double",description:"Offset runs at the head, middle, and tail around two doubles",angle:90,dominoes:[{value1:12,value2:7},{value1:7,value2:8},{value1:8,value2:8},{value1:8,value2:3},{value1:3,value2:5},{value1:5,value2:5},{value1:5,value2:2},{value1:2,value2:1}],layoutStyles:["linear","offset"]},{id:"offset-zigzag",name:"Offset zigzag",description:"Alternating perpendicular tiles without doubles",angle:0,dominoes:[{value1:12,value2:5},{value1:5,value2:9},{value1:9,value2:2},{value1:2,value2:7},{value1:7,value2:1}],layoutStyles:["offset"]},{id:"horizontal-open",name:"Horizontal train",description:"Rightward train: offset head, double, offset tail",angle:0,dominoes:[{value1:5,value2:12},{value1:12,value2:11},{value1:11,value2:11},{value1:11,value2:6},{value1:6,value2:2}],layoutStyles:["linear","offset"]},{id:"vertical-open",name:"Vertical train",description:"Downward train: offset head, double, offset tail",angle:90,dominoes:[{value1:3,value2:12},{value1:12,value2:10},{value1:10,value2:10},{value1:10,value2:4},{value1:4,value2:1}],layoutStyles:["linear","offset"]}],uo=[{id:"single-foot",name:"Single foot",description:"A double fans two angled toes (±45°) while the main line continues straight as the center toe",angle:0,branch:{dominoes:[{value1:12,value2:6},{value1:6,value2:6},{value1:6,value2:3},{value1:3,value2:1}],feet:{1:[{dominoes:[{value1:6,value2:2},{value1:2,value2:5}]},{dominoes:[{value1:6,value2:4},{value1:4,value2:0}]}]}},layoutStyles:["linear","offset"]},{id:"foot-no-center",name:"Foot at the tail",description:"Double ends the main line, so both side toes are present with no straight continuation",angle:0,branch:{dominoes:[{value1:9,value2:7},{value1:7,value2:7}],feet:{1:[{dominoes:[{value1:7,value2:3},{value1:3,value2:8}]},{dominoes:[{value1:7,value2:5},{value1:5,value2:0}]}]}},layoutStyles:["linear","offset"]},{id:"nested-foot",name:"Nested foot",description:"A side toe contains its own double, which sprouts a second-level foot",angle:90,branch:{dominoes:[{value1:12,value2:8},{value1:8,value2:8},{value1:8,value2:3}],feet:{1:[{dominoes:[{value1:8,value2:5},{value1:5,value2:5},{value1:5,value2:2}],feet:{1:[{dominoes:[{value1:5,value2:9},{value1:9,value2:1}]},{dominoes:[{value1:5,value2:4},{value1:4,value2:6}]}]}},{dominoes:[{value1:8,value2:1},{value1:1,value2:7}]}]}},layoutStyles:["linear","offset"]}],N={maxPips:12,engineValue:12,allowConsecutiveDoubles:!1,requireUniqueTiles:!0,requireSequential:!0,doubleObligation:"cover",chickenFoot:{toeCount:3,sideToeAngles:[-45,45]}};function me(e){switch(e.doubleObligation){case"chicken-foot":return Math.max(1,e.chickenFoot.toeCount);case"cover":return 1;default:return 0}}function ze(e){return e.doubleObligation==="chicken-foot"?Math.max(0,e.chickenFoot.toeCount-1):0}function co(e={}){const o=e.maxPips??N.maxPips;return{...N,...e,maxPips:o,engineValue:e.engineValue??o,chickenFoot:{...N.chickenFoot,...e.chickenFoot??{}}}}function Te(e,o){let t=e;for(const n of o)if(t=t?.feet?.[n.doubleIndex]?.[n.toeIndex],!t)return;return t}function q(e,o,t){if(t(e,o),!!e.feet)for(const n of Object.keys(e.feet)){const r=Number(n);e.feet[r].forEach((i,l)=>{q(i,[...o,{doubleIndex:r,toeIndex:l}],t)})}}function fo(e){const o=[];return q(e,[],(t,n)=>{t.dominoes.forEach((r,i)=>{if(r.value1!==r.value2)return;const l=i<t.dominoes.length-1,s=t.feet?.[i]?.length??0;o.push({path:n,doubleIndex:i,value:r.value1,hasCenter:l,sideToes:s,answers:(l?1:0)+s})})}),o}function Ce(e,o){const t=me(o);return t<=0?[]:fo(e).filter(n=>n.answers<t)}function Me(e){const o=new Set;return q(e,[],t=>{for(const n of t.dominoes)o.add(H(n))}),o}function $e(e,o,t){if(e.dominoes.length===0)return[{path:[],attach:"run-tail",value:o,attachToDouble:!0,obligation:!1}];const n=Ce(e,t);if(t.doubleObligation!=="none"&&n.length>0){const i=ze(t),l=[];for(const s of n){const a=Te(e,s.path);a&&(!s.hasCenter&&s.doubleIndex===a.dominoes.length-1&&l.push({path:s.path,attach:"run-tail",value:s.value,attachToDouble:!0,obligation:!0}),s.sideToes<i&&l.push({path:s.path,attach:"side-toe",value:s.value,doubleIndex:s.doubleIndex,toeSlot:s.sideToes,attachToDouble:!0,obligation:!0}))}return l}const r=[];return q(e,[],(i,l)=>{const s=i.dominoes[i.dominoes.length-1];s&&r.push({path:l,attach:"run-tail",value:s.value2,attachToDouble:W(s),obligation:!1})}),r}function te(e,o,t,n){const r=[],i=ee(e,o.value);return n.requireSequential&&!i&&r.push("value-mismatch"),n.requireUniqueTiles&&t.has(H(e))&&r.push("duplicate-tile"),!n.allowConsecutiveDoubles&&o.attachToDouble&&W(e)&&r.push("consecutive-doubles"),{legal:r.length===0,violations:r}}function po(e,o,t,n,r){const i=$e(e,o,r),l=[];for(const s of i)for(const a of t)te(a,s,n,r).legal&&l.push({end:s,tile:a});return l}function De(e,o,t){if(o.length===0)return t(e);const[n,...r]=o,l=(e.feet?.[n.doubleIndex]??[]).map((s,a)=>a===n.toeIndex?De(s,r,t):s);return{...e,feet:{...e.feet,[n.doubleIndex]:l}}}function Oe(e,o,t){const n=ee(o.tile,o.end.value)??{...o.tile};return De(e,o.end.path,r=>{if(o.end.attach==="run-tail")return{...r,dominoes:[...r.dominoes,n]};const i=o.end.doubleIndex??0,l=o.end.toeSlot??r.feet?.[i]?.length??0,s=r.feet?.[i]?[...r.feet[i]]:[];return s[l]={dominoes:[n]},{...r,feet:{...r.feet,[i]:s}}})}function vo(e,o,t){const n=te(o.tile,o.end,Me(e),t);return n.legal?{ok:!0,board:Oe(e,o),violations:[]}:{ok:!1,board:e,violations:n.violations}}exports.CHICKEN_FOOT_FIXTURES=uo;exports.CHICKEN_FOOT_TOE_ANGLES=ve;exports.DEFAULT_PIP_COLORS=F;exports.DEFAULT_RULES=N;exports.DominoHub=Se;exports.DominoTrain=be;exports.DoubleTwelve=J;exports.MexicanTrainGame=oo;exports.PIP_COLORS=Fe;exports.PIP_LAYOUTS=fe;exports.TRAIN_FIXTURES=ao;exports.applyMove=Oe;exports.collectPlayedKeys=Me;exports.computeTrainLayout=ge;exports.computeTrainTree=Z;exports.dominoKey=H;exports.dominoSetSize=Ge;exports.evaluatePlacement=te;exports.flattenSegments=he;exports.generateDominoSet=Ve;exports.getBranchAt=Te;exports.getLegalMoves=po;exports.getOpenEnds=$e;exports.getPipLayout=pe;exports.getPipStyle=Le;exports.getTrainLayoutBounds=Ue;exports.getUnsatisfiedDoubles=Ce;exports.isDouble=W;exports.mergePipColors=Ae;exports.orientForConnection=ee;exports.otherEnd=Be;exports.outwardPerpSign=R;exports.playMove=vo;exports.requiredDoubleAnswers=me;exports.resolvePipPosition=de;exports.resolvePipStyle=K;exports.resolveRules=co;exports.sideToeSlots=ze;exports.stepAlongTrain=z;exports.tileCorners=G;exports.tileHasValue=qe;exports.tileKey=E;exports.tilesOverlap=Q;exports.validateChickenFootChain=lo;exports.validateTrainLayout=io;exports.validateTrainTree=so;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const x=require("react/jsx-runtime"),M=require("react"),$e={};function pe(e,t){return t?{...e,...t,tileDataAttributes:{...e.tileDataAttributes,...t.tileDataAttributes}}:e}function Oe(e){if(!e)return{};const t={};for(const[o,n]of Object.entries(e))n===void 0||n===!1||(t[`data-${o}`]=n===!0?"true":String(n));return t}const ce=M.createContext($e),Tt=({theme:e,children:t})=>{const o=M.useContext(ce),n=pe(o,e);return x.jsx(ce.Provider,{value:n,children:t})};function he(e){const t=M.useContext(ce);return pe(t,e)}const Re=({ctx:e,theme:t})=>{const{row:o,col:n,gridSize:r,color:i,hollow:l,positionStyle:s}=e,a=t?.pipStyle?.(e)??{};return x.jsx("div",{"data-testid":"pip","data-row":o,"data-col":n,"data-grid":r,"data-pip-value":e.value,"data-hollow":l?"true":void 0,style:{position:"absolute",borderRadius:"50%",transform:"translate(-50%, -50%)",backgroundColor:l?"transparent":i,border:l?`2px solid ${i}`:void 0,boxShadow:l?void 0:"1px 2px 3px rgba(0,0,0,0.3)",...s,...a}})},It={"3x3":{rows:[20,50,80],cols:[20,50,80],size:"18%"},"3x4":{rows:[24,50,76],cols:[22,40,60,78],size:"12%"},"4x3":{rows:[15,38,62,85],cols:[20,50,80],size:"14%"}};function Fe(e){const t=It[e.gridSize];return{top:e.top??`${t.rows[e.row]}%`,left:e.left??`${t.cols[e.col]}%`,width:t.size,height:t.size}}const je={0:[],1:[{row:1,col:1,gridSize:"3x3"}],2:[{row:0,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"}],3:[{row:0,col:2,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"}],4:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],5:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],6:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],7:[{row:0,col:0,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],8:[{row:0,col:0,gridSize:"3x3"},{row:0,col:1,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:1,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],9:[{row:0,col:0,gridSize:"3x3"},{row:0,col:1,gridSize:"3x3"},{row:0,col:2,gridSize:"3x3"},{row:1,col:0,gridSize:"3x3"},{row:1,col:1,gridSize:"3x3"},{row:1,col:2,gridSize:"3x3"},{row:2,col:0,gridSize:"3x3"},{row:2,col:1,gridSize:"3x3"},{row:2,col:2,gridSize:"3x3"}],10:[{row:0,col:0,gridSize:"3x4"},{row:0,col:1,gridSize:"3x4"},{row:0,col:2,gridSize:"3x4"},{row:0,col:3,gridSize:"3x4"},{row:1,col:0,gridSize:"3x4"},{row:1,col:3,gridSize:"3x4"},{row:2,col:0,gridSize:"3x4"},{row:2,col:1,gridSize:"3x4"},{row:2,col:2,gridSize:"3x4"},{row:2,col:3,gridSize:"3x4"}],11:[{row:0,col:0,gridSize:"4x3"},{row:1,col:0,gridSize:"4x3"},{row:2,col:0,gridSize:"4x3"},{row:3,col:0,gridSize:"4x3"},{row:0,col:1,gridSize:"4x3"},{row:2,col:1,gridSize:"4x3",top:"50%"},{row:3,col:1,gridSize:"4x3"},{row:0,col:2,gridSize:"4x3"},{row:1,col:2,gridSize:"4x3"},{row:2,col:2,gridSize:"4x3"},{row:3,col:2,gridSize:"4x3"}],12:[{row:0,col:0,gridSize:"4x3"},{row:1,col:0,gridSize:"4x3"},{row:2,col:0,gridSize:"4x3"},{row:3,col:0,gridSize:"4x3"},{row:0,col:1,gridSize:"4x3"},{row:1,col:1,gridSize:"4x3"},{row:2,col:1,gridSize:"4x3"},{row:3,col:1,gridSize:"4x3"},{row:0,col:2,gridSize:"4x3"},{row:1,col:2,gridSize:"4x3"},{row:2,col:2,gridSize:"4x3"},{row:3,col:2,gridSize:"4x3"}]};function _e(e){return je[e]??[]}const U={0:{color:"transparent"},1:{color:"#1a1a1a"},2:{color:"#8B1A1A"},3:{color:"#E6B800"},4:{color:"#e8e8e8",hollow:!0},5:{color:"#2E8B57"},6:{color:"#2563EB"},7:{color:"#E8A87C"},8:{color:"#DC2626"},9:{color:"#1E3A8A"},10:{color:"#EA580C"},11:{color:"#166534"},12:{color:"#DC2626"}},Pt=U;function zt(e){return{...U,...e}}function xe(e,t){if(t!==void 0)return t[e]??U[e]??{color:"#1a1a1a"}}function Dt(e){return xe(e,U)}const Mt=(e,t,o)=>{const n=xe(e,o);return n?{color:n.color,hollow:n.hollow}:{color:t}},Ct=({value:e,pipColor:t,pipColors:o})=>{const n=he(),{color:r,hollow:i}=Mt(e,t,o),l=_e(e);return x.jsx(x.Fragment,{children:l.map((s,a)=>{const c={value:e,row:s.row,col:s.col,gridSize:s.gridSize,color:r,hollow:i,top:s.top,left:s.left,positionStyle:Fe(s)};return n.renderPip?x.jsx("span",{children:n.renderPip(c)},a):x.jsx(Re,{ctx:c,theme:n},a)})})},ke=({value:e,pipColor:t,pipColors:o})=>x.jsx("div",{style:{width:"100%",height:"100%",position:"relative",padding:"0",overflow:"hidden"},children:x.jsx(Ct,{value:e,pipColor:t,pipColors:o})}),ge=({value1:e=0,value2:t=0,width:o=100,height:n=200,backgroundColor:r="white",pipColor:i="black",pipColors:l,borderColor:s="black",rotation:a=0,theme:c})=>{const d=he(c),u=Math.min(Math.max(e,0),12),f=Math.min(Math.max(t,0),12),p={value1:u,value2:f,width:o,height:n,backgroundColor:r,borderColor:s,rotation:a},v=d.tileStyle?.(p)??{},T=d.halfDividerStyle?.(p)??{};return x.jsxs("div",{className:d.tileClassName,...Oe(d.tileDataAttributes),style:{width:`${o}px`,height:`${n}px`,backgroundColor:r,borderColor:s,borderWidth:"1px",borderStyle:"solid",borderRadius:"10px",transform:`rotate(${a}deg)`,transformOrigin:"center center",boxShadow:"0 1px 2px rgba(0,0,0,0.2)",display:"flex",flexDirection:"column",overflow:"hidden",...v},children:[x.jsx("div",{style:{flex:1,position:"relative",borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:s,...T},children:x.jsx(ke,{value:u,pipColor:i,pipColors:l})}),x.jsx("div",{style:{flex:1,position:"relative"},children:x.jsx(ke,{value:f,pipColor:i,pipColors:l})})]})},E=60,k=120,Ne=[-45,45];function ee(e,t=E,o=k){return e?t/2:o/2}function A(e,t,o=E,n=k){return ee(e,o,n)+ee(t,o,n)}function N(e){const t=e*Math.PI/180;return{dirX:Math.cos(t),dirY:Math.sin(t)}}function B(e){const{dirX:t,dirY:o}=N(e);return{perpX:-o,perpY:t}}function Et(e){const t=e.map(o=>({...o}));for(let o=1;o<t.length;o++){const n=t[o],r=t[o-1].value2;!(n.value1===n.value2)&&n.value1!==r&&n.value2===r&&(t[o]={value1:n.value2,value2:n.value1})}return t}function V(e){const{dirX:t,dirY:o}=N(e);return Math.abs(t)>=Math.abs(o)?t>=0?1:-1:o>=0?1:-1}function Le(e,t){return e===0?t:e===t?-t:t}function Ye({orientedDominoes:e,startX:t,startY:o,angle:n,layoutStyle:r,dominoWidth:i,dominoHeight:l,leadGap:s,outwardSign:a,hubIndex:c}){const d=[],{dirX:u,dirY:f}=N(n),{perpX:p,perpY:v}=B(n),T=r==="offset"&&c!=null,S=[];let P=t+u*s,y=o+f*s,D=0,g=0;const I=i/2,C=b=>{const h=(b-D)*I;P+=p*h,y+=v*h,D=b};for(let b=0;b<e.length;b++){const h=e[b],m=h.value1===h.value2,z=b>0&&e[b-1].value1===e[b-1].value2;r==="linear"?b>0&&(m?(P+=u*A(z,!0,i,l),y+=f*A(z,!0,i,l)):z?(P+=u*A(!0,!1,i,l),y+=f*A(!0,!1,i,l)):(P+=u*l,y+=f*l)):m?b>0&&(P+=u*A(z,!0,i,l),y+=f*A(z,!0,i,l)):(b===0?g=a:z?(P+=u*A(!0,!1,i,l),y+=f*A(!0,!1,i,l)):(P+=u*(l/2),y+=f*(l/2),g=Le(g,a)),C(g)),S.push(D),d.push({x:P,y,rotation:m?n+180:n-90,isDouble:m,value1:h.value1,value2:h.value2})}if(T&&c!=null){const b=-S[c]*I;if(b!==0)for(let h=0;h<d.length;h++)d[h]={...d[h],x:d[h].x+p*b,y:d[h].y+v*b}}return d}function ve(e,t){if(!e||e.length===0)return[];const o=new Map;for(const n of e)Number.isInteger(n.index)&&(n.index<=0||n.index>=t||o.set(n.index,n.turn));return[...o.entries()].map(([n,r])=>({index:n,turn:r})).sort((n,r)=>n.index-r.index)}function Xe(e,t,o,n=1/0){const r=ve(t,Number.isFinite(n)?n:o+1);let i=e;for(const l of r)if(l.index<=o)i+=l.turn;else break;return i}function kt(e,t,o,n){const{startX:r,startY:i,angle:l,layoutStyle:s,dominoWidth:a,dominoHeight:c,leadGap:d,outwardSign:u}=t,f=[0,...o.map(y=>y.index),e.length],p=[];let v=l,T=r,S=i,P=d;for(let y=0;y<f.length-1;y++){const D=e.slice(f[y],f[y+1]);if(D.length===0)continue;const g=y===0&&n!=null&&n<f[1]?n:void 0,I=Ye({orientedDominoes:D,startX:T,startY:S,angle:v,layoutStyle:s,dominoWidth:a,dominoHeight:c,leadGap:P,outwardSign:u,hubIndex:g});if(p.push(...I),y>=f.length-2)break;const b=I[I.length-1],h=N(v),m=ee(b.isDouble,a,c);v+=o[y].turn;const z=e[f[y+1]],O=z.value1===z.value2,X=ee(O,a,c),G=N(v),K=B(v),$=a/2,ae=b.x+h.dirX*(m-$)+G.dirX*(X+$),Z=b.y+h.dirY*(m-$)+G.dirY*(X+$),F=s==="offset"&&!O,J=F?K.perpX*$*u:0,Q=F?K.perpY*$*u:0;T=ae-J,S=Z-Q,P=0}return p}function Be({startX:e,startY:t,angle:o,dominoes:n,layoutStyle:r,dominoWidth:i=E,dominoHeight:l=k,leadGap:s=l*.3,outwardSign:a,hubIndex:c,bends:d}){const u=Et([...n]),f=a??V(o),p=ve(d,u.length);return p.length>0?kt(u,{startX:e,startY:t,angle:o,layoutStyle:r,dominoWidth:i,dominoHeight:l,leadGap:s,outwardSign:f},p,c):Ye({orientedDominoes:u,startX:e,startY:t,angle:o,layoutStyle:r,dominoWidth:i,dominoHeight:l,leadGap:s,outwardSign:f,hubIndex:c})}function de(e,t=E,o=k){const n=e.rotation*Math.PI/180,r=Math.cos(n),i=Math.sin(n),l=t/2,s=o/2;return[[-l,-s],[l,-s],[l,s],[-l,s]].map(([a,c])=>({x:e.x+a*r-c*i,y:e.y+a*i+c*r}))}function At(e,t,o){let n=1/0,r=-1/0,i=1/0,l=-1/0;for(const s of e){const a=s.x*o.x+s.y*o.y;n=Math.min(n,a),r=Math.max(r,a)}for(const s of t){const a=s.x*o.x+s.y*o.y;i=Math.min(i,a),l=Math.max(l,a)}return Math.min(r,l)-Math.max(n,i)}function H(e,t,o=1,n=E,r=k){const i=de(e,n,r),l=de(t,n,r);for(const s of[i,l])for(let a=0;a<4;a++){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,v={x:-f/p,y:u/p};if(At(i,l,v)<=o)return!1}return!0}function $t(e,t,o,n){return t.some(r=>H(e,r,1,o,n))}function be(e,t,o=1,n=E,r=k){return e.some(i=>t.some(l=>H(i,l,o,n,r)))}function ye(e,t=1,o=E,n=k){for(let r=0;r<e.length;r++)for(let i=r+1;i<e.length;i++)if(H(e[r],e[i],t,o,n))return!0;return!1}const j=E/4,Ae=24;function ne({startX:e,startY:t,angle:o,branch:n,layoutStyle:r,dominoWidth:i=E,dominoHeight:l=k,leadGap:s,depth:a=0,anchor:c,outwardSign:d,placed:u=[],pushAxis:f,minPushSteps:p=0}){const v=d??V(o),T=n.feet?Object.keys(n.feet).map(Number).filter(g=>{const I=n.dominoes[g];return I&&I.value1===I.value2}).sort((g,I)=>g-I)[0]:void 0,S=(g,I)=>Be({startX:g,startY:I,angle:o,dominoes:n.dominoes,layoutStyle:r,dominoWidth:i,dominoHeight:l,leadGap:s,outwardSign:v,hubIndex:T,bends:n.bends});let P=S(e+(f?.x??0)*j*p,t+(f?.y??0)*j*p),y=c&&f?{x:c.x+f.x*j*p,y:c.y+f.y*j*p}:c;if(f&&u.length>0)for(let g=p;g<=Ae;g++){const I=e+f.x*j*g,C=t+f.y*j*g,b=S(I,C);if(!b.some(m=>$t(m,u,i,l))||g===Ae){P=b,y=c&&{x:c.x+f.x*j*g,y:c.y+f.y*j*g};break}}u.push(...P);const D=[{angle:o,depth:a,layoutStyle:r,outwardSign:v,dominoes:n.dominoes,layout:P,anchor:y}];if(n.feet){const g=i/2,I=l/2;for(const C of Object.keys(n.feet)){const b=Number(C),h=P[b],m=n.feet[b];if(!h||!h.isDouble||!m)continue;const z=Xe(o,n.bends,b,n.dominoes.length),{dirX:O,dirY:X}=N(z),{perpX:G,perpY:K}=B(z);for(let $=0;$<m.length;$++){const ae=m[$],Z=Ne[$]??0,F=Math.sign(Z),J=z+Z,Q=B(J),ue=-F,St=h.x+O*(i/2)+G*(l/2)*F,wt=h.y+X*(i/2)+K*(l/2)*F,Ce=St-Q.perpX*ue*g,Ee=wt-Q.perpY*ue*g;D.push(...ne({startX:Ce,startY:Ee,angle:J,branch:ae,layoutStyle:r,dominoWidth:i,dominoHeight:l,leadGap:I,outwardSign:ue,depth:a+1,anchor:{x:Ce,y:Ee},placed:u,pushAxis:{x:G*F,y:K*F}}))}}}return D}function me(e){return e.flatMap(t=>t.layout)}function Ot(e,t=24,o=E,n=k){const r=Math.hypot(o,n)/2;if(e.length===0)return{width:t*2+o,height:t*2+n,offsetX:t,offsetY:t};let i=1/0,l=1/0,s=-1/0,a=-1/0;for(const c of e)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);return{width:Math.ceil(s-i+t*2),height:Math.ceil(a-l+t*2),offsetX:t-i,offsetY:t-l}}const oe=1;function Ue(e,t,o){const{dirX:n,dirY:r}=N(o);return e*n+t*r}function Rt(e,t,o){const{perpX:n,perpY:r}=B(o);return e*n+t*r}function Se(e){const t=[];for(let o=1;o<e.length;o++)e[o].value1!==e[o-1].value2&&t.push({code:"chain-break",message:`Domino ${o} does not connect to domino ${o-1}`,index:o});for(let o=1;o<e.length;o++){const n=e[o-1].value1===e[o-1].value2,r=e[o].value1===e[o].value2;n&&r&&t.push({code:"consecutive-doubles",message:`Consecutive doubles at index ${o-1} and ${o}`,index:o})}return{valid:t.length===0,issues:t}}function Ve(e,t,o,n=E,r=k){const i=n/2,l=e.map(u=>u.isDouble),s=[];let a=0,c=0,d=0;for(let u=0;u<e.length;u++){const f=l[u],p=u>0&&l[u-1];t==="linear"?(u>0&&(a+=A(p,f,n,r)),c=0):f?u>0&&(a+=A(p,!0,n,r)):u===0?(d=o,c=d*i):p?a+=A(!0,!1,n,r):(a+=r/2,d=Le(d,o),c=d*i),s.push({along:a,perp:c})}return s}function Ft(e,t,o,n=oe,r){const i=[],l=r??V(t),s=Ve(e,o,l);for(let a=1;a<e.length;a++){const c=e[a-1],d=e[a],u=d.x-c.x,f=d.y-c.y,p=Ue(u,f,t),v=Rt(u,f,t),T=s[a].along-s[a-1].along,S=s[a].perp-s[a-1].perp;Math.abs(p-T)>n&&i.push({code:"spacing-along-train",message:`Along-train spacing between domino ${a-1} and ${a} is ${p.toFixed(2)}px (expected ${T}px)`,index:a}),Math.abs(v-S)>n&&i.push({code:"spacing-perpendicular",message:`Perpendicular spacing between domino ${a-1} and ${a} is ${v.toFixed(2)}px (expected ${S}px)`,index:a})}return{valid:i.length===0,issues:i}}function jt(e,t,o,n=oe,r){const i=[],l=r??V(t),s=Ve(e,o,l);for(let a=1;a<e.length;a++){const c=e[a-1],d=e[a],u=d.x-c.x,f=d.y-c.y,p=Math.hypot(u,f),v=s[a].along-s[a-1].along,T=s[a].perp-s[a-1].perp,S=Math.hypot(v,T)*.9;p+n<S&&i.push({code:"overlap",message:`Domino ${a-1} and ${a} centers are ${p.toFixed(2)}px apart (minimum ${S.toFixed(2)}px)`,index:a})}return{valid:i.length===0,issues:i}}function _t(e,t,o,n,r=oe,i){const l=[...Se(t).issues,...Ft(e,o,n,r,i).issues,...jt(e,o,n,r,i).issues];return e.length!==t.length&&l.push({code:"layout-length",message:`Layout length ${e.length} does not match domino count ${t.length}`}),{valid:l.length===0,issues:l}}function Ge(e){const t=[],o=(n,r)=>{if(t.push(...Se(n.dominoes).issues.map(i=>({...i,message:`[${r}] ${i.message}`}))),!!n.feet)for(const i of Object.keys(n.feet)){const l=Number(i),s=n.dominoes[l],a=n.feet[l]??[];if(!s){t.push({code:"foot-host-missing",message:`[${r}] Foot references missing tile ${l}`});continue}s.value1!==s.value2&&t.push({code:"foot-host-not-double",message:`[${r}] Foot host tile ${l} is not a double`}),a.length>2&&t.push({code:"foot-too-many-toes",message:`[${r}] Double ${l} has ${a.length} side toes (max 2; the center toe is the main line)`}),a.forEach((c,d)=>{const u=c.dominoes[0];u&&u.value1!==s.value1&&t.push({code:"foot-connection",message:`[${r}] Toe ${d} on double ${l} starts with ${u.value1} but the double is ${s.value1}`}),o(c,`${r}.${l}.${d}`)})}};return o(e,"main"),{valid:t.length===0,issues:t}}function Nt(e,t=oe){const o=[];e.forEach((r,i)=>{if(o.push(...Se(r.dominoes).issues.map(l=>({...l,message:`[segment ${i} @${r.angle}°] ${l.message}`}))),r.layout.length!==r.dominoes.length&&o.push({code:"layout-length",message:`[segment ${i}] Layout length ${r.layout.length} does not match domino count ${r.dominoes.length}`}),r.anchor&&r.layout.length>0){const l=r.layout[0],s=Ue(l.x-r.anchor.x,l.y-r.anchor.y,r.angle),a=k/2;Math.abs(s-a)>t&&o.push({code:"foot-anchor",message:`[segment ${i}] First toe tile sits ${s.toFixed(2)}px from the double along the toe (expected ${a}px)`,index:0})}});const n=e.flatMap(r=>r.layout);for(let r=0;r<n.length;r++)for(let i=r+1;i<n.length;i++)H(n[r],n[i])&&o.push({code:"tile-overlap",message:`Tiles ${r} and ${i} overlap`,index:i});return{valid:o.length===0,issues:o}}const Lt=(()=>{try{return!1}catch{return!1}})(),Ke=({startX:e,startY:t,angle:o,trainData:n,layoutStyle:r,tableWidth:i,tableHeight:l,centerX:s,centerY:a,pipColors:c})=>{M.useEffect(()=>{if(!Lt)return;const u=Ge({dominoes:n.dominoes,feet:n.feet});u.valid||console.warn(`DominoTrain: player ${n.playerId} train does not follow the rules:`,u.issues.map(f=>f.message))},[n.dominoes,n.feet,n.playerId]);const d=M.useMemo(()=>me(ne({startX:e,startY:t,angle:o,branch:{dominoes:n.dominoes,feet:n.feet},layoutStyle:r})),[e,t,o,n.dominoes,n.feet,r,i,l]);return x.jsx(x.Fragment,{children:d.map((u,f)=>{const p=n.isPublic;return x.jsx("div",{style:{position:"absolute",left:`${u.x-E/2}px`,top:`${u.y-k/2}px`,zIndex:5},children:x.jsx(ge,{value1:u.value1,value2:u.value2,width:E,height:k,backgroundColor:"white",pipColor:"black",pipColors:c,borderColor:p?"red":"black",rotation:u.rotation})},`main-train-${n.playerId}-${f}`)})})};function qe(e,t,o,n){const r=o*(n==="offset"?2.5:1.3);return Math.max(t+20,Math.ceil(r*e/(2*Math.PI)))}const He=({playerCount:e,centerX:t,centerY:o,radius:n,engineValue:r,trains:i,layoutStyle:l,tableWidth:s,tableHeight:a,pipColors:c})=>{const d=Math.max(8,e),u=120,f=60,p=120,v=qe(d,n,f,l);return x.jsxs("div",{style:{position:"relative",width:"100%",height:"100%"},children:[x.jsx("div",{style:{position:"absolute",width:`${u}px`,height:`${u}px`,left:`${t-u/2}px`,top:`${o-u/2}px`,backgroundColor:"#d1d5db",borderWidth:"3px",borderStyle:"solid",borderColor:"#6b7280",borderRadius:"50%",boxShadow:"0 4px 6px rgba(0,0,0,0.1)",zIndex:10,display:"flex",justifyContent:"center",alignItems:"center"},children:x.jsx("div",{style:{transform:"rotate(0deg)"},children:x.jsx(ge,{value1:r,value2:r,width:f,height:p,backgroundColor:"white",pipColor:"black",pipColors:c,borderColor:"#333"})})}),Array.from({length:d}).map((T,S)=>{const P=S*360/d,y=P*Math.PI/180,D=t+v*Math.cos(y),g=o+v*Math.sin(y),I=i.find(C=>C.playerId===S)||{dominoes:[],playerId:S,isPublic:!1};return x.jsx(Ke,{startX:D,startY:g,angle:P,trainData:I,layoutStyle:l,tableWidth:s,tableHeight:a,centerX:t,centerY:o,pipColors:c},S)})]})};function L(e,t){return e<=t?`${e}:${t}`:`${t}:${e}`}function Y(e){return L(e.value1,e.value2)}function we(e){return e.value1===e.value2}function Te(e,t){return e.value1===t||e.value2===t}function Yt(e,t){return e.value1===t?e.value2:e.value2===t?e.value1:null}function re(e,t){return e.value1===t?{value1:e.value1,value2:e.value2}:e.value2===t?{value1:e.value2,value2:e.value1}:null}function Ze(e){const t=[];for(let o=0;o<=e;o++)for(let n=o;n<=e;n++)t.push({value1:o,value2:n});return t}function Xt(e){const t=e+1;return t*(t+1)/2}function Je(e,t=12,o={}){const n=new Set([L(t,t)]),r=[];for(let i=0;i<e;i++){const l=4+Math.floor(Math.random()*7),s=[];let a=t,c=!1;for(let u=0;u<l;u++){const f=Gt(a,c,u===0,t,n);if(f===null)break;const p=f===a;n.add(L(a,f)),s.push({value1:a,value2:f}),c=p,a=f}const d=o.chickenFeet?Bt(s,n):void 0;r.push({playerId:i,dominoes:s,isPublic:Math.random()>.7,...d?{feet:d}:{}})}return r}function Bt(e,t){const o={};for(let n=0;n<e.length;n++){if(e[n].value1!==e[n].value2)continue;const r=e[n].value1,i=[];for(let l=0;l<2;l++){const s=Ut(r,t);s&&i.push(s)}i.length&&(o[n]=i)}return Object.keys(o).length?o:void 0}function Ut(e,t){const o=1+Math.floor(Math.random()*2),n=[];let r=e;for(let i=0;i<o;i++){const l=Vt(r,t);if(l===null)break;t.add(L(r,l)),n.push({value1:r,value2:l}),r=l}return n.length?{dominoes:n}:null}function Vt(e,t){const o=[];for(let n=0;n<13;n++)n!==e&&(t.has(L(e,n))||o.push(n));return o.length===0?null:o[Math.floor(Math.random()*o.length)]}function Gt(e,t,o,n,r){const i=Array.from({length:13},(l,s)=>s).filter(l=>Kt(e,l,t,o,n,r));return i.length===0?null:i[Math.floor(Math.random()*i.length)]}function Kt(e,t,o,n,r,i){const l=t===e;return!(n&&l&&e===r||l&&o||i.has(L(e,t)))}const qt={playerCount:8,trains:[],engineValue:12},Ht=({initialState:e=qt,width:t=1200,height:o=800,pipColors:n,onPipColorsChange:r})=>{const[i,l]=M.useState(e),[s,a]=M.useState("offset"),[c,d]=M.useState(!1),[u,f]=M.useState(void 0),p=n??u,v=r??f,T=p!==void 0,S=t/2,P=o/2,y=(g=c)=>{const I=Je(i.playerCount,i.engineValue,{chickenFeet:g});l(C=>({...C,trains:I}))};M.useEffect(()=>{y()},[]);const D=()=>{const g=!c;d(g),y(g)};return x.jsxs("div",{style:{width:`${t}px`,height:`${o}px`,position:"relative",backgroundColor:"#1f8a55",borderRadius:"8px",boxShadow:"0 4px 12px rgba(0,0,0,0.2)",overflow:"hidden"},children:[x.jsxs("div",{style:{position:"absolute",top:"10px",left:"10px",zIndex:100},children:[x.jsx("button",{onClick:()=>y(),style:{padding:"8px 12px",backgroundColor:"#fff",border:"1px solid #ccc",borderRadius:"4px",marginRight:"10px",cursor:"pointer"},children:"New trains"}),x.jsxs("button",{onClick:()=>a(s==="offset"?"linear":"offset"),style:{padding:"8px 12px",backgroundColor:"#fff",border:"1px solid #ccc",borderRadius:"4px",marginRight:"10px",cursor:"pointer"},children:["Layout: ",s==="offset"?"Offset":"Linear"]}),x.jsxs("button",{onClick:D,style:{padding:"8px 12px",backgroundColor:c?"#fef3c7":"#fff",border:`1px solid ${c?"#f59e0b":"#ccc"}`,borderRadius:"4px",marginRight:"10px",cursor:"pointer"},children:["Chicken Feet: ",c?"On":"Off"]}),x.jsxs("button",{onClick:()=>v(T?void 0:U),style:{padding:"8px 12px",backgroundColor:T?"#fef3c7":"#fff",border:`1px solid ${T?"#f59e0b":"#ccc"}`,borderRadius:"4px",cursor:"pointer"},children:["Pip Colors: ",T?"On":"Off"]})]}),x.jsxs("div",{style:{position:"absolute",top:"10px",right:"10px",zIndex:100,backgroundColor:"rgba(255,255,255,0.8)",padding:"8px",borderRadius:"4px",fontSize:"14px"},children:[x.jsxs("div",{children:["Engine: Double-",i.engineValue]}),x.jsxs("div",{children:["Players: ",i.playerCount]})]}),x.jsx(He,{playerCount:i.playerCount,centerX:S,centerY:P,radius:80,engineValue:i.engineValue,trains:i.trains,layoutStyle:s,tableWidth:t,tableHeight:o,pipColors:p})]})},ie=90;function te(e,t=ie){return e==="right"?t:-t}function Ie(e){return e==="right"?"left":"right"}function Zt(e,t){return(t??V(e))>=0?"left":"right"}function Jt(e,t,o){const{perpX:n,perpY:r}=B(t),i=(a,c)=>{let d=1/0;return a>0?d=Math.min(d,(o.width-e.x)/a):a<0&&(d=Math.min(d,(0-e.x)/a)),c>0?d=Math.min(d,(o.height-e.y)/c):c<0&&(d=Math.min(d,(0-e.y)/c)),Number.isFinite(d)?Math.max(0,d):1/0},l=i(n,r),s=i(-n,-r);return l>=s?"right":"left"}function Pe(e,t){return me(ne({startX:t.startX,startY:t.startY,angle:t.angle,branch:e,layoutStyle:t.layoutStyle}))}function q(e,t,o){const n=(e??[]).filter(r=>r.index!==t);return o===null?n:[...n,{index:t,turn:o}].sort((r,i)=>r.index-i.index)}function Qt({branch:e,index:t,build:o,obstacles:n,preferredSide:r,degrees:i=ie}){const l=[r,Ie(r)];for(const s of l){const a=te(s,i),c={...e,bends:q(e.bends,t,a)},d=Pe(c,o);if(!ye(d)&&!be(d,n))return{turn:a}}return{turn:null,reason:"blocked"}}function Wt(e,t,o,n,r,i=ie){const l=(e.bends??[]).find(u=>u.index===t),s=te(r,i),a=te(Ie(r),i),c=u=>{const f={...e,bends:q(e.bends,t,u)},p=Pe(f,o);return!ye(p)&&!be(p,n)};let d;l?l.turn===s?d=[a,null]:l.turn===a?d=[null]:d=[s,a,null]:d=[s,a];for(const u of d){if(u===null)return{bends:q(e.bends,t,null),changed:!0,blocked:!1};if(c(u))return{bends:q(e.bends,t,u),changed:!0,blocked:!1}}return{bends:e.bends??[],changed:!1,blocked:!0}}function le(e,t,o){return Math.min(o,Math.max(t,e))}function fe(e,t,o,n,r){const i=le(e.scale*t,n,r),l=i/e.scale;return{scale:i,x:o.x-(o.x-e.x)*l,y:o.y-(o.y-e.y)*l}}function Qe(e,t,o,n,r){const i=Math.max(1,e.width),l=Math.max(1,e.height),s=Math.min((t.width-o*2)/i,(t.height-o*2)/l),a=le(s,n,r);return{scale:a,x:(t.width-i*a)/2,y:(t.height-l*a)/2}}function en(e,t){return{x:(t.x-e.x)/e.scale,y:(t.y-e.y)/e.scale}}const tn=3,nn=({width:e,height:t,contentWidth:o,contentHeight:n,children:r,minScale:i=.2,maxScale:l=4,zoomStep:s=1.15,padding:a=40,background:c="#1f8a55",showControls:d=!0,testId:u="viewport"})=>{const f=M.useRef(null),p=M.useCallback(()=>Qe({width:o,height:n},{width:e,height:t},a,i,l),[o,n,e,t,a,i,l]),[v,T]=M.useState(p);M.useEffect(()=>{T(p())},[p]);const S=M.useRef(null),P=(h,m)=>{const z=f.current?.getBoundingClientRect();return{x:h-(z?.left??0),y:m-(z?.top??0)}},y=h=>{S.current={pointerX:h.clientX,pointerY:h.clientY,startX:v.x,startY:v.y,moved:!1}},D=h=>{const m=S.current;if(!m)return;const z=h.clientX-m.pointerX,O=h.clientY-m.pointerY;!m.moved&&Math.hypot(z,O)<tn||(m.moved=!0,f.current?.setPointerCapture?.(h.pointerId),T(X=>({...X,x:m.startX+z,y:m.startY+O})))},g=h=>{S.current?.moved&&h.preventDefault(),S.current=null},I=h=>{h.preventDefault();const m=P(h.clientX,h.clientY),z=h.deltaY<0?s:1/s;T(O=>fe(O,z,m,i,l))},C=h=>T(m=>fe(m,h,{x:e/2,y:t/2},i,l)),b={width:32,height:32,fontSize:18,lineHeight:"30px",textAlign:"center",cursor:"pointer",background:"#fff",border:"1px solid #d1d5db",borderRadius:6,userSelect:"none"};return x.jsxs("div",{ref:f,"data-testid":u,onPointerDown:y,onPointerMove:D,onPointerUp:g,onPointerLeave:g,onWheel:I,style:{position:"relative",width:e,height:t,overflow:"hidden",background:c,borderRadius:8,cursor:"grab",touchAction:"none"},children:[x.jsx("div",{"data-testid":`${u}-content`,style:{position:"absolute",left:0,top:0,transformOrigin:"0 0",transform:`translate(${v.x}px, ${v.y}px) scale(${v.scale})`},children:r}),d&&x.jsxs("div",{"data-testid":`${u}-controls`,style:{position:"absolute",right:12,bottom:12,display:"flex",flexDirection:"column",gap:6},children:[x.jsx("div",{style:b,role:"button","aria-label":"Zoom in",onClick:()=>C(s),children:"+"}),x.jsx("div",{style:b,role:"button","aria-label":"Zoom out",onClick:()=>C(1/s),children:"−"}),x.jsx("div",{style:{...b,fontSize:12,lineHeight:"30px"},role:"button","aria-label":"Reset view",onClick:()=>T(p()),children:"⤢"}),x.jsx("div",{"data-testid":`${u}-zoom-readout`,style:{...b,fontSize:11,cursor:"default"},children:Math.round(le(v.scale,i,l)*100)})]})]})},on=[{id:"regular-after-double",name:"Regular after double",description:"Double followed by a two-tile offset run",angle:0,dominoes:[{value1:12,value2:6},{value1:6,value2:6},{value1:6,value2:3},{value1:3,value2:1}],layoutStyles:["linear","offset"]},{id:"double-after-regular",name:"Double after regular",description:"Offset run, a double, then another offset run",angle:0,dominoes:[{value1:12,value2:9},{value1:9,value2:4},{value1:4,value2:4},{value1:4,value2:2},{value1:2,value2:7}],layoutStyles:["linear","offset"]},{id:"double-after-double",name:"Double after double",description:"Offset runs at the head, middle, and tail around two doubles",angle:90,dominoes:[{value1:12,value2:7},{value1:7,value2:8},{value1:8,value2:8},{value1:8,value2:3},{value1:3,value2:5},{value1:5,value2:5},{value1:5,value2:2},{value1:2,value2:1}],layoutStyles:["linear","offset"]},{id:"offset-zigzag",name:"Offset zigzag",description:"Alternating perpendicular tiles without doubles",angle:0,dominoes:[{value1:12,value2:5},{value1:5,value2:9},{value1:9,value2:2},{value1:2,value2:7},{value1:7,value2:1}],layoutStyles:["offset"]},{id:"horizontal-open",name:"Horizontal train",description:"Rightward train: offset head, double, offset tail",angle:0,dominoes:[{value1:5,value2:12},{value1:12,value2:11},{value1:11,value2:11},{value1:11,value2:6},{value1:6,value2:2}],layoutStyles:["linear","offset"]},{id:"vertical-open",name:"Vertical train",description:"Downward train: offset head, double, offset tail",angle:90,dominoes:[{value1:3,value2:12},{value1:12,value2:10},{value1:10,value2:10},{value1:10,value2:4},{value1:4,value2:1}],layoutStyles:["linear","offset"]}],rn=[{id:"single-foot",name:"Single foot",description:"A double fans two angled toes (±45°) while the main line continues straight as the center toe",angle:0,branch:{dominoes:[{value1:12,value2:6},{value1:6,value2:6},{value1:6,value2:3},{value1:3,value2:1}],feet:{1:[{dominoes:[{value1:6,value2:2},{value1:2,value2:5}]},{dominoes:[{value1:6,value2:4},{value1:4,value2:0}]}]}},layoutStyles:["linear","offset"]},{id:"foot-no-center",name:"Foot at the tail",description:"Double ends the main line, so both side toes are present with no straight continuation",angle:0,branch:{dominoes:[{value1:9,value2:7},{value1:7,value2:7}],feet:{1:[{dominoes:[{value1:7,value2:3},{value1:3,value2:8}]},{dominoes:[{value1:7,value2:5},{value1:5,value2:0}]}]}},layoutStyles:["linear","offset"]},{id:"nested-foot",name:"Nested foot",description:"A side toe contains its own double, which sprouts a second-level foot",angle:90,branch:{dominoes:[{value1:12,value2:8},{value1:8,value2:8},{value1:8,value2:3}],feet:{1:[{dominoes:[{value1:8,value2:5},{value1:5,value2:5},{value1:5,value2:2}],feet:{1:[{dominoes:[{value1:5,value2:9},{value1:9,value2:1}]},{dominoes:[{value1:5,value2:4},{value1:4,value2:6}]}]}},{dominoes:[{value1:8,value2:1},{value1:1,value2:7}]}]}},layoutStyles:["linear","offset"]}],W={maxPips:12,engineValue:12,allowConsecutiveDoubles:!1,requireUniqueTiles:!0,requireSequential:!0,doubleObligation:"cover",chickenFoot:{toeCount:3,sideToeAngles:[-45,45]}};function We(e){switch(e.doubleObligation){case"chicken-foot":return Math.max(1,e.chickenFoot.toeCount);case"cover":return 1;default:return 0}}function et(e){return e.doubleObligation==="chicken-foot"?Math.max(0,e.chickenFoot.toeCount-1):0}function ln(e={}){const t=e.maxPips??W.maxPips;return{...W,...e,maxPips:t,engineValue:e.engineValue??t,chickenFoot:{...W.chickenFoot,...e.chickenFoot??{}}}}function tt(e,t){let o=e;for(const n of t)if(o=o?.feet?.[n.doubleIndex]?.[n.toeIndex],!o)return;return o}function se(e,t,o){if(o(e,t),!!e.feet)for(const n of Object.keys(e.feet)){const r=Number(n);e.feet[r].forEach((i,l)=>{se(i,[...t,{doubleIndex:r,toeIndex:l}],o)})}}function sn(e){const t=[];return se(e,[],(o,n)=>{o.dominoes.forEach((r,i)=>{if(r.value1!==r.value2)return;const l=i<o.dominoes.length-1,s=o.feet?.[i]?.length??0;t.push({path:n,doubleIndex:i,value:r.value1,hasCenter:l,sideToes:s,answers:(l?1:0)+s})})}),t}function nt(e,t){const o=We(t);return o<=0?[]:sn(e).filter(n=>n.answers<o)}function ze(e){const t=new Set;return se(e,[],o=>{for(const n of o.dominoes)t.add(Y(n))}),t}function ot(e,t,o){if(e.dominoes.length===0)return[{path:[],attach:"run-tail",value:t,attachToDouble:!0,obligation:!1}];const n=nt(e,o);if(o.doubleObligation!=="none"&&n.length>0){const i=et(o),l=[];for(const s of n){const a=tt(e,s.path);a&&(!s.hasCenter&&s.doubleIndex===a.dominoes.length-1&&l.push({path:s.path,attach:"run-tail",value:s.value,attachToDouble:!0,obligation:!0}),s.sideToes<i&&l.push({path:s.path,attach:"side-toe",value:s.value,doubleIndex:s.doubleIndex,toeSlot:s.sideToes,attachToDouble:!0,obligation:!0}))}return l}const r=[];return se(e,[],(i,l)=>{const s=i.dominoes[i.dominoes.length-1];s&&r.push({path:l,attach:"run-tail",value:s.value2,attachToDouble:we(s),obligation:!1})}),r}function De(e,t,o,n){const r=[],i=re(e,t.value);return n.requireSequential&&!i&&r.push("value-mismatch"),n.requireUniqueTiles&&o.has(Y(e))&&r.push("duplicate-tile"),!n.allowConsecutiveDoubles&&t.attachToDouble&&we(e)&&r.push("consecutive-doubles"),{legal:r.length===0,violations:r}}function rt(e,t,o,n,r){const i=ot(e,t,r),l=[];for(const s of i)for(const a of o)De(a,s,n,r).legal&&l.push({end:s,tile:a});return l}function it(e,t,o){if(t.length===0)return o(e);const[n,...r]=t,l=(e.feet?.[n.doubleIndex]??[]).map((s,a)=>a===n.toeIndex?it(s,r,o):s);return{...e,feet:{...e.feet,[n.doubleIndex]:l}}}function lt(e,t,o){const n=re(t.tile,t.end.value)??{...t.tile};return it(e,t.end.path,r=>{if(t.end.attach==="run-tail")return{...r,dominoes:[...r.dominoes,n]};const i=t.end.doubleIndex??0,l=t.end.toeSlot??r.feet?.[i]?.length??0,s=r.feet?.[i]?[...r.feet[i]]:[];return s[l]={dominoes:[n]},{...r,feet:{...r.feet,[i]:s}}})}function an(e,t,o){const n=De(t.tile,t.end,ze(e),o);return n.legal?{ok:!0,board:lt(e,t),violations:[]}:{ok:!1,board:e,violations:n.violations}}function _(e){return e.kind==="play"}function st(e){const t=[];return e.trains.forEach((o,n)=>{(o.playerId===e.selfPlayerId||o.isPublic)&&t.push(n)}),t}function Me(e){const t=new Set;for(const o of e)for(const n of ze(o))t.add(n);return t}function at(e){const t=Me(e.trains),o=[];for(const n of st(e)){const r=e.trains[n],i=rt(r,e.engineValue,e.hand,t,e.rules);for(const l of i)o.push({kind:"play",trainIndex:n,move:l})}return o}function ut(e={}){return t=>{const o=at(t),n=[...o];return(t.drawPileSize??Number.POSITIVE_INFINITY)>0&&!t.turnDrawUsed&&(o.length===0||e.allowOptionalDraw)&&n.push({kind:"draw"}),n.length===0&&n.push({kind:"pass"}),n}}const ct=ut(),R={preferPlay:"prefer-play",dumpPips:"dump-pips",doublesEarly:"play-doubles-early",ownTrain:"own-train",obligationRelief:"obligation-relief",handFlexibility:"hand-flexibility",defensivePublic:"defensive-public"};function dt(e){const t=re(e.move.tile,e.move.end.value);return t?t.value2:e.move.tile.value2}const un={id:R.preferPlay,score(e){return e.kind==="play"?100:e.kind==="draw"?0:-50}},cn={id:R.dumpPips,score(e){if(!_(e))return 0;const t=e.move.tile;return t.value1+t.value2}},dn={id:R.doublesEarly,score(e,t){if(!_(e))return 0;const o=e.move.tile;return o.value1!==o.value2?0:Math.min(t.obs.hand.length,12)}},fn={id:R.ownTrain,score(e,t){if(!_(e))return 0;const o=t.obs.trains[e.trainIndex];return o&&o.playerId===t.obs.selfPlayerId?8:0}},pn={id:R.obligationRelief,score(e){return _(e)&&e.move.end.obligation?10:0}},hn={id:R.handFlexibility,score(e,t){if(!_(e))return 0;const o=dt(e),n=Y(e.move.tile);let r=!1,i=0;for(const l of t.obs.hand){if(!r&&Y(l)===n){r=!0;continue}Te(l,o)&&i++}return i*3}},xn={id:R.defensivePublic,score(e,t){if(!_(e))return 0;const o=t.obs.trains[e.trainIndex];if(!o||o.playerId===t.obs.selfPlayerId)return 0;const n=dt(e);let r=0;for(const i of t.unseen)Te(i,n)&&r++;return-r}},ft=[un,cn,dn,fn,pn,hn,xn],w=R,pt={beginner:{id:"beginner",temperature:2.5,blunderRate:.25,lookaheadDepth:0,enabled:new Set([w.preferPlay,w.dumpPips]),weights:{[w.preferPlay]:1,[w.dumpPips]:.2}},intermediate:{id:"intermediate",temperature:.6,blunderRate:.05,lookaheadDepth:0,enabled:new Set([w.preferPlay,w.dumpPips,w.doublesEarly,w.ownTrain]),weights:{[w.preferPlay]:1,[w.dumpPips]:1,[w.doublesEarly]:1,[w.ownTrain]:1}},advanced:{id:"advanced",temperature:.15,blunderRate:0,lookaheadDepth:0,enabled:new Set([w.preferPlay,w.dumpPips,w.doublesEarly,w.ownTrain,w.obligationRelief,w.handFlexibility,w.defensivePublic]),weights:{[w.preferPlay]:1,[w.dumpPips]:1.2,[w.doublesEarly]:1.5,[w.ownTrain]:1,[w.obligationRelief]:1.5,[w.handFlexibility]:1,[w.defensivePublic]:1.5}}};function gn(e){return pt[e]}function ht(e,t,o,n){let r=0;for(const i of n.enabled){const l=o.get(i);if(!l)continue;const s=n.weights[i]??1;r+=s*l.score(e,t)}return r}function xt(e,t){let o=Number.NEGATIVE_INFINITY,n=[];return e.forEach((r,i)=>{r>o?(o=r,n=[i]):r===o&&n.push(i)}),n[Math.floor(t()*n.length)]}function gt(e,t,o){const n=Math.max(...e),r=e.map(s=>Math.exp((s-n)/t)),i=r.reduce((s,a)=>s+a,0);let l=o()*i;for(let s=0;s<r.length;s++)if(l-=r[s],l<=0)return s;return r.length-1}function vt(e,t,o){return e.length<=1?0:t.temperature<=0?xt(e,o):gt(e,t.temperature,o)}function bt(e){const{skill:t,generateCandidates:o,buildContext:n,fallback:r}=e,i=e.rng??Math.random,l=new Map(e.heuristics.map(s=>[s.id,s]));return{decide(s){const a=o(s);if(a.length===0)return r(s);if(a.length===1)return a[0];if(t.blunderRate>0&&i()<t.blunderRate)return a[Math.floor(i()*a.length)];const c=n(s,a),d=a.map(u=>ht(u,c,l,t));return a[vt(d,t,i)]}}}function vn(e,t,o){const n=Me(e.trains),r=new Set(n);for(const l of e.hand)r.add(Y(l));const i=Ze(e.rules.maxPips).filter(l=>!r.has(Y(l)));return{obs:e,playedKeys:n,candidates:t,playCandidates:t.filter(_),unseen:i,rng:o}}function bn(e){const t=e.heuristics??ft,o=e.generateCandidates??ct,n=e.rng??Math.random;return bt({skill:e.skill,heuristics:t,generateCandidates:o,buildContext:(r,i)=>vn(r,i,n),fallback:()=>({kind:"pass"}),rng:n})}function yt(e,t,o){let n=t.legalActions(e);return t.orderActions&&(n=t.orderActions(e,n)),Number.isFinite(o)&&n.length>o&&(n=n.slice(0,o)),n}function mt(e,t,o,n,r){if(o<=0||t.isTerminal(e))return t.evaluate(e,n);const i=yt(e,t,r);if(i.length===0)return t.evaluate(e,n);const l=t.currentPlayer(e)===n;let s=l?Number.NEGATIVE_INFINITY:Number.POSITIVE_INFINITY;for(const a of i){const c=mt(t.applyAction(e,a),t,o-1,n,r);s=l?Math.max(s,c):Math.min(s,c)}return s}function yn(e,t,o){const n=o.rng??Math.random,r=Math.max(1,o.determinizations??1),i=o.maxBranch??Number.POSITIVE_INFINITY,l=Math.max(1,o.depth);return yt(e,t,i).map(a=>{let c=0;for(let d=0;d<r;d++){const u=t.determinize?t.determinize(e,o.perspective,n):e;c+=mt(t.applyAction(u,a),t,l-1,o.perspective,i)}return{action:a,value:c/r}})}exports.CHICKEN_FOOT_FIXTURES=rn;exports.CHICKEN_FOOT_TOE_ANGLES=Ne;exports.DEFAULT_DOMINO_THEME=$e;exports.DEFAULT_HEURISTICS=ft;exports.DEFAULT_PIP_COLORS=U;exports.DEFAULT_RULES=W;exports.DefaultPip=Re;exports.DominoHub=He;exports.DominoThemeProvider=Tt;exports.DominoTrain=Ke;exports.DoubleTwelve=ge;exports.HEURISTIC_IDS=R;exports.MexicanTrainGame=Ht;exports.PIP_COLORS=Pt;exports.PIP_LAYOUTS=je;exports.SKILL_PRESETS=pt;exports.TRAIN_FIXTURES=on;exports.TURN_DEGREES=ie;exports.Viewport=nn;exports.applyMove=lt;exports.argmaxIndex=xt;exports.buildBranchTiles=Pe;exports.chooseActionIndex=vt;exports.clampScale=le;exports.collectAllPlayedKeys=Me;exports.collectPlayedKeys=ze;exports.computeTrainLayout=Be;exports.computeTrainTree=ne;exports.createAiPlayer=bn;exports.createCandidateGenerator=ut;exports.createPolicyPlayer=bt;exports.cycleBendAt=Wt;exports.defaultCandidateGenerator=ct;exports.dominoKey=Y;exports.dominoSetSize=Xt;exports.evaluatePlacement=De;exports.fitToBounds=Qe;exports.flattenSegments=me;exports.generateDominoSet=Ze;exports.generatePlayActions=at;exports.generateSampleTrains=Je;exports.getAccessibleTrainIndices=st;exports.getBranchAt=tt;exports.getLegalMoves=rt;exports.getOpenEnds=ot;exports.getPipLayout=_e;exports.getPipStyle=Dt;exports.getSkillProfile=gn;exports.getTrainLayoutBounds=Ot;exports.getUnsatisfiedDoubles=nt;exports.headingAtIndex=Xe;exports.hubTrainStartDistance=qe;exports.isDouble=we;exports.isPlayAction=_;exports.layoutSelfIntersects=ye;exports.layoutsCollide=be;exports.linearDefaultSide=Jt;exports.mergeDominoTheme=pe;exports.mergePipColors=zt;exports.normalizeBends=ve;exports.offsetDefaultSide=Zt;exports.oppositeSide=Ie;exports.orientForConnection=re;exports.otherEnd=Yt;exports.outwardPerpSign=V;exports.playMove=an;exports.requiredDoubleAnswers=We;exports.resolveBend=Qt;exports.resolvePipPosition=Fe;exports.resolvePipStyle=xe;exports.resolveRules=ln;exports.scoreWithHeuristics=ht;exports.screenToContent=en;exports.searchActionValues=yn;exports.sideToTurn=te;exports.sideToeSlots=et;exports.softmaxIndex=gt;exports.stepAlongTrain=A;exports.themeDataAttributes=Oe;exports.tileCorners=de;exports.tileHasValue=Te;exports.tileKey=L;exports.tilesOverlap=H;exports.useDominoTheme=he;exports.validateChickenFootChain=Ge;exports.validateTrainLayout=_t;exports.validateTrainTree=Nt;exports.withBendAt=q;exports.zoomAt=fe;
2
2
  //# sourceMappingURL=index.cjs.map