/* Hex Bridgecraft. Adapted from PlayHex; AGPL-3.0. See LICENSE.txt. PlayHex Board.ts supplies adjacency, connection detection and winning paths. This offline shell and shortest-path opponent are additions. */ import Board from './Board.ts'; const $=(s)=>document.querySelector(s); let board,turn=0,winner=null,history=[],last=null,busy=false,version=0; const mode=()=>$('#mode').value; function resize(){const n=board.getSize();const w=Math.min(840,window.innerWidth-42);$('#board').style.setProperty('--cell',Math.min(56,(w-12)/(n*1.5-.5))+'px');} function reset(){version++;board=new Board(Number($('#size').value));turn=0;winner=null;history=[];last=null;busy=false;render();resize();$('#message').textContent='Select any empty hex to place your first stone.';} function render(){const n=board.getSize(),grid=board.getCells(),path=board.getShortestWinningPath()||[],wins=new Set(path.map(c=>c.row+','+c.col));const root=$('#board');root.style.setProperty('--n',n);root.replaceChildren();for(let r=0;r'+name+'';b.disabled=p!==null||busy||winner!==null;b.addEventListener('click',()=>play(r,c));row.appendChild(b);}root.appendChild(row);}$('#turn').textContent=winner!==null?(winner===0?'Red':'Blue')+' connected the shores!':busy?'Blue is planning a bridge…':(turn===0?'Red · connect top to bottom':'Blue · connect left to right');$('#turn').style.color=winner===1||turn===1?'#8ccafa':'#ff9690';$('#moves').textContent=history.length+' stones played';$('#undo').disabled=history.length===0||busy;} function place(r,c){history.push({grid:board.getCellsClone(),turn,last});board.setCell({row:r,col:c},turn);last={row:r,col:c};winner=board.calculateWinner();turn=1-turn;} function play(r,c){if(busy||winner!==null||!board.isEmpty({row:r,col:c}))return;place(r,c);$('#message').textContent=winner!==null?'A complete bridge! Start a new game or Undo to explore another line.':'Stone placed at '+String.fromCharCode(65+c)+(r+1)+'.';if(mode()==='ai'&&turn===1&&winner===null){busy=true;render();const v=version;setTimeout(()=>{if(v!==version)return;const m=chooseMove();place(m.row,m.col);busy=false;render();$('#message').textContent=winner!==null?'Blue completed a bridge. Try again, or Undo to find a defense.':'Blue played '+String.fromCharCode(65+m.col)+(m.row+1)+'. Your move.';},250);}else render();} function distance(p){const n=board.getSize(),dist=Array(n*n).fill(Infinity),seen=new Set(),grid=board.getCells();const cost=(r,c)=>grid[r][c]===p?0:grid[r][c]===null?1:Infinity;for(const x of board.getSideCells(p,0))dist[x.row*n+x.col]=cost(x.row,x.col);for(let t=0;tvalue){value=v;best=a;}}return best;} $('#new').addEventListener('click',reset);$('#size').addEventListener('change',reset);$('#mode').addEventListener('change',reset);$('#undo').addEventListener('click',()=>{if(busy||!history.length)return;version++;let prev=history.pop();if(mode()==='ai'&&prev.turn===1&&history.length)prev=history.pop();board=Board.createFromGrid(prev.grid);turn=prev.turn;last=prev.last;winner=null;render();$('#message').textContent='Move taken back. Try a different bridge.';});window.addEventListener('resize',resize);reset();