{"id":60,"date":"2026-06-10T17:13:44","date_gmt":"2026-06-10T17:13:44","guid":{"rendered":"https:\/\/wahpro.com\/?page_id=60"},"modified":"2026-06-10T17:19:36","modified_gmt":"2026-06-10T17:19:36","slug":"game","status":"publish","type":"page","link":"https:\/\/wahpro.com\/?page_id=60","title":{"rendered":"Game"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\nbody{font-family:sans-serif;background:#1a1a2e;color:#eee;min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:16px}\nh1{font-size:1.6rem;font-weight:700;color:#f9ca24;text-shadow:0 0 10px #f9ca2466;margin-bottom:12px}\n#setup{background:#16213e;border-radius:12px;padding:20px;width:100%;max-width:500px}\n#setup h2{font-size:1.1rem;margin-bottom:12px;color:#f9ca24}\n.player-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}\n.player-row input{flex:1;padding:7px 10px;border-radius:8px;border:1px solid #444;background:#0f3460;color:#fff;font-size:14px}\n.color-dot{width:24px;height:24px;border-radius:50%;border:2px solid #fff;flex-shrink:0}\n.btn{padding:8px 18px;border-radius:8px;border:none;cursor:pointer;font-weight:600;font-size:14px;transition:transform .1s}\n.btn:active{transform:scale(.97)}\n.btn-add{background:#0f3460;color:#7ec8e3;border:1px solid #7ec8e366}\n.btn-start{background:#f9ca24;color:#1a1a2e;margin-top:12px;width:100%;padding:11px}\n.btn-roll{background:#e84393;color:#fff;font-size:1.1rem;padding:12px 32px;border-radius:12px}\n.btn-roll:disabled{background:#555;cursor:not-allowed}\n.btn-new{background:#0f3460;color:#7ec8e3;border:1px solid #7ec8e366;margin-top:8px}\n#game{display:none;width:100%;max-width:700px;flex-direction:column;align-items:center;gap:12px}\n#board-wrap{position:relative;width:100%;max-width:600px}\ncanvas{border-radius:12px;width:100%;display:block}\n#info{background:#16213e;border-radius:10px;padding:12px 16px;width:100%;max-width:600px}\n#turn-label{font-size:1rem;font-weight:600;margin-bottom:6px}\n#log{font-size:13px;color:#aaa;max-height:80px;overflow-y:auto;line-height:1.6}\n#dice-area{display:flex;gap:12px;align-items:center}\n#dice-face{font-size:2.8rem;line-height:1;transition:transform .3s}\n.scores{display:flex;flex-wrap:wrap;gap:8px;width:100%;max-width:600px}\n.score-card{background:#16213e;border-radius:8px;padding:8px 12px;display:flex;align-items:center;gap:8px;font-size:13px}\n.score-dot{width:14px;height:14px;border-radius:50%;flex-shrink:0}\n#winner-screen{display:none;flex-direction:column;align-items:center;gap:12px;padding:20px;background:#16213e;border-radius:16px;width:100%;max-width:500px;text-align:center}\n#winner-screen h2{font-size:2rem;color:#f9ca24}\n<\/style>\n<\/head>\n<body>\n<h1>\ud83d\udc0d Snakes &#038; Ladders \ud83e\ude9c<\/h1>\n\n<div id=\"setup\">\n  <h2>Add Players<\/h2>\n  <div id=\"players-list\"><\/div>\n  <button class=\"btn btn-add\" onclick=\"addPlayer()\">+ Add Player<\/button>\n  <br>\n  <button class=\"btn btn-start\" onclick=\"startGame()\">Start Game<\/button>\n<\/div>\n\n<div id=\"game\">\n  <div class=\"scores\" id=\"scores\"><\/div>\n  <div id=\"board-wrap\"><canvas id=\"board\" width=\"600\" height=\"600\"><\/canvas><\/div>\n  <div id=\"info\">\n    <div id=\"turn-label\"><\/div>\n    <div id=\"log\"><\/div>\n  <\/div>\n  <div id=\"dice-area\">\n    <div id=\"dice-face\">\ud83c\udfb2<\/div>\n    <button class=\"btn btn-roll\" id=\"roll-btn\" onclick=\"rollDice()\">Roll Dice<\/button>\n  <\/div>\n  <button class=\"btn btn-new\" onclick=\"newGame()\">New Game<\/button>\n<\/div>\n\n<div id=\"winner-screen\">\n  <div style=\"font-size:3rem\">\ud83c\udfc6<\/div>\n  <h2 id=\"winner-name\"><\/h2>\n  <p style=\"color:#aaa\">reached square 100 first!<\/p>\n  <button class=\"btn btn-start\" onclick=\"newGame()\" style=\"max-width:200px;margin-top:8px\">Play Again<\/button>\n<\/div>\n\n<script>\nconst COLORS = ['#e84393','#f9ca24','#7ec8e3','#6bcb77','#ff6b6b','#c77dff','#ff9f43','#48dbfb','#fd79a8','#badc58'];\nconst DICE_FACES = ['\u2680','\u2681','\u2682','\u2683','\u2684','\u2685'];\n\nconst SNAKES = {97:78,95:56,88:24,76:37,74:53,62:19,54:34,17:7};\nconst LADDERS = {3:38,8:30,28:84,40:59,51:67,63:81,71:91};\n\nlet players = [];\nlet playerCount = 0;\nlet current = 0;\nlet rolling = false;\nlet animFrame = null;\nlet snakeAnimState = null;\n\nfunction addPlayer(){\n  if(playerCount >= 10) return;\n  const idx = playerCount;\n  const div = document.createElement('div');\n  div.className = 'player-row';\n  div.id = 'pr-'+idx;\n  div.innerHTML = `<span class=\"color-dot\" style=\"background:${COLORS[idx]}\"><\/span><input type=\"text\" placeholder=\"Player ${idx+1} name\" id=\"pname-${idx}\" value=\"Player ${idx+1}\">`;\n  document.getElementById('players-list').appendChild(div);\n  playerCount++;\n}\n\nfunction newGame(){\n  document.getElementById('winner-screen').style.display='none';\n  document.getElementById('game').style.display='none';\n  document.getElementById('setup').style.display='block';\n  document.getElementById('players-list').innerHTML='';\n  playerCount=0;\n  players=[];\n  addPlayer(); addPlayer();\n}\n\nfunction startGame(){\n  players=[];\n  for(let i=0;i<playerCount;i++){\n    const nm = document.getElementById('pname-'+i)?.value.trim()||'Player '+(i+1);\n    players.push({name:nm,color:COLORS[i],pos:0,animPos:0});\n  }\n  if(players.length<1){alert('Add at least 1 player');return;}\n  current=0;\n  rolling=false;\n  snakeAnimState=null;\n  document.getElementById('setup').style.display='none';\n  document.getElementById('winner-screen').style.display='none';\n  document.getElementById('game').style.display='flex';\n  document.getElementById('roll-btn').disabled=false;\n  document.getElementById('log').innerHTML='';\n  updateScores();\n  updateTurnLabel();\n  drawBoard();\n}\n\nfunction squareToXY(sq){\n  const s=sq-1, row=Math.floor(s\/10), col=s%10;\n  const r=9-row;\n  const c=(row%2===0)?col:9-col;\n  const sz=60;\n  return {x:c*sz+sz\/2, y:r*sz+sz\/2};\n}\n\nfunction drawBoard(){\n  const canvas=document.getElementById('board');\n  const ctx=canvas.getContext('2d');\n  const sz=60;\n  ctx.clearRect(0,0,600,600);\n\n  \/\/ Draw squares\n  for(let sq=1;sq<=100;sq++){\n    const s=sq-1, row=Math.floor(s\/10), col=s%10;\n    const r=9-row, c=(row%2===0)?col:9-col;\n    const x=c*sz, y=r*sz;\n    const isSnake=SNAKES[sq]!==undefined, isLadder=LADDERS[sq]!==undefined;\n    ctx.fillStyle=isSnake?'#2d0a0a':isLadder?'#0a2d0a':((r+c)%2===0?'#1e2d55':'#243368');\n    ctx.fillRect(x,y,sz,sz);\n    ctx.strokeStyle='#ffffff22';ctx.lineWidth=1;\n    ctx.strokeRect(x,y,sz,sz);\n    ctx.fillStyle='#ffffff99';ctx.font='10px sans-serif';ctx.textAlign='center';\n    ctx.fillText(sq,x+sz\/2,y+12);\n  }\n\n  \/\/ Draw ladders\n  Object.entries(LADDERS).forEach(([from,to])=>{\n    const f=squareToXY(+from), t=squareToXY(+to);\n    ctx.save();\n    const dx=t.x-f.x, dy=t.y-f.y, len=Math.sqrt(dx*dx+dy*dy);\n    const angle=Math.atan2(dy,dx);\n    ctx.translate(f.x,f.y);ctx.rotate(angle);\n    ctx.strokeStyle='#f9ca24';ctx.lineWidth=4;ctx.setLineDash([]);\n    ctx.beginPath();ctx.moveTo(8,0);ctx.lineTo(len-8,0);ctx.stroke();\n    \/\/ rungs\n    for(let i=14;i<len-8;i+=14){\n      ctx.beginPath();ctx.moveTo(i,-6);ctx.lineTo(i,6);ctx.stroke();\n    }\n    ctx.restore();\n  });\n\n  \/\/ Draw snakes\n  Object.entries(SNAKES).forEach(([from,to])=>{\n    const head=squareToXY(+from), tail=squareToXY(+to);\n    drawSnake(ctx,head,tail,null,1);\n  });\n\n  \/\/ Animate snake if needed\n  if(snakeAnimState){\n    const {head,tail,t,playerIdx}=snakeAnimState;\n    drawSnake(ctx,head,tail,players[playerIdx].color,t);\n  }\n\n  \/\/ Draw players\n  const groups={};\n  players.forEach((p,i)=>{\n    const sq=p.animPos||p.pos||1;\n    if(!groups[sq])groups[sq]=[];\n    groups[sq].push(i);\n  });\n  Object.entries(groups).forEach(([sq,idxs])=>{\n    const {x,y}=squareToXY(+sq);\n    const n=idxs.length;\n    idxs.forEach((pi,k)=>{\n      const off=n>1?[-8,8,0,-8,8][k]:0;\n      const offy=n>2?[-6,-6,6][k]:0;\n      ctx.beginPath();\n      ctx.arc(x+off+(n>1?0:0),y+(offy||0),9,0,Math.PI*2);\n      ctx.fillStyle=players[pi].color;\n      ctx.fill();\n      ctx.strokeStyle='#fff';ctx.lineWidth=2;\n      ctx.stroke();\n    });\n  });\n}\n\nfunction drawSnake(ctx,head,tail,highlightColor,t){\n  const cx1=head.x+(tail.x-head.x)*0.2+40, cy1=head.y+(tail.y-head.y)*0.2-40;\n  const cx2=head.x+(tail.x-head.x)*0.7-40, cy2=head.y+(tail.y-head.y)*0.7+40;\n  ctx.save();\n  ctx.strokeStyle=highlightColor||'#c0392b';\n  ctx.lineWidth=highlightColor?7:5;\n  ctx.globalAlpha=highlightColor?0.9:0.7;\n  ctx.setLineDash(highlightColor?[16,8]:[12,6]);\n  ctx.lineDashOffset=highlightColor?-(t*80):0;\n  ctx.beginPath();\n  ctx.moveTo(head.x,head.y);\n  ctx.bezierCurveTo(cx1,cy1,cx2,cy2,tail.x,tail.y);\n  ctx.stroke();\n  \/\/ Head circle\n  ctx.setLineDash([]);\n  ctx.beginPath();\n  ctx.arc(head.x,head.y,10,0,Math.PI*2);\n  ctx.fillStyle=highlightColor||'#e74c3c';\n  ctx.globalAlpha=0.9;\n  ctx.fill();\n  \/\/ Eyes\n  ctx.fillStyle='#fff';\n  ctx.globalAlpha=1;\n  const eyeAngle=Math.atan2(tail.y-head.y,tail.x-head.x)+Math.PI;\n  ctx.beginPath();ctx.arc(head.x+Math.cos(eyeAngle-0.5)*5,head.y+Math.sin(eyeAngle-0.5)*5,2.5,0,Math.PI*2);ctx.fill();\n  ctx.beginPath();ctx.arc(head.x+Math.cos(eyeAngle+0.5)*5,head.y+Math.sin(eyeAngle+0.5)*5,2.5,0,Math.PI*2);ctx.fill();\n  ctx.fillStyle='#000';\n  ctx.beginPath();ctx.arc(head.x+Math.cos(eyeAngle-0.5)*5,head.y+Math.sin(eyeAngle-0.5)*5,1.2,0,Math.PI*2);ctx.fill();\n  ctx.beginPath();ctx.arc(head.x+Math.cos(eyeAngle+0.5)*5,head.y+Math.sin(eyeAngle+0.5)*5,1.2,0,Math.PI*2);ctx.fill();\n  ctx.restore();\n}\n\nfunction rollDice(){\n  if(rolling)return;\n  rolling=true;\n  document.getElementById('roll-btn').disabled=true;\n  const p=players[current];\n  let shakes=0;\n  const rollInterval=setInterval(()=>{\n    document.getElementById('dice-face').textContent=DICE_FACES[Math.floor(Math.random()*6)];\n    shakes++;\n    if(shakes>10){\n      clearInterval(rollInterval);\n      const val=Math.floor(Math.random()*6)+1;\n      document.getElementById('dice-face').textContent=DICE_FACES[val-1];\n      movePlayer(current,val);\n    }\n  },80);\n}\n\nfunction log(msg){\n  const el=document.getElementById('log');\n  el.innerHTML=`<span>${msg}<\/span><br>`+el.innerHTML;\n}\n\nfunction movePlayer(pi,steps){\n  const p=players[pi];\n  const start=p.pos||1;\n  let target=start+steps;\n  if(target>100)target=start;\n  \/\/ Animate token sliding\n  let frame=0, total=20;\n  p.animPos=start;\n  const anim=setInterval(()=>{\n    frame++;\n    const t=frame\/total;\n    p.animPos=Math.round(start+(target-start)*t);\n    drawBoard();\n    if(frame>=total){\n      clearInterval(anim);\n      p.pos=target;\n      p.animPos=target;\n      log(`${p.name} rolled ${steps} \u2192 square ${target}`);\n      if(target===100){\n        drawBoard();\n        showWinner(pi);\n        return;\n      }\n      if(SNAKES[target]!==undefined){\n        const dest=SNAKES[target];\n        const head=squareToXY(target), tail=squareToXY(dest);\n        log(`\ud83d\udc0d Oh no! ${p.name} hit a snake! Sliding from ${target} to ${dest}`);\n        animateSnake(pi,head,tail,dest,()=>afterMove());\n      } else if(LADDERS[target]!==undefined){\n        const dest=LADDERS[target];\n        log(`\ud83e\ude9c Lucky! ${p.name} climbed a ladder from ${target} to ${dest}`);\n        const lstart=target;\n        let lframe=0,ltotal=25;\n        const lanim=setInterval(()=>{\n          lframe++;\n          const lt=lframe\/ltotal;\n          p.animPos=Math.round(lstart+(dest-lstart)*lt);\n          drawBoard();\n          if(lframe>=ltotal){\n            clearInterval(lanim);\n            p.pos=dest;p.animPos=dest;\n            updateScores();drawBoard();\n            if(dest===100){showWinner(pi);return;}\n            afterMove();\n          }\n        },30);\n      } else {\n        updateScores();drawBoard();\n        afterMove();\n      }\n    }\n  },30);\n}\n\nfunction animateSnake(pi,head,tail,dest,cb){\n  let t=0;\n  snakeAnimState={head,tail,t,playerIdx:pi};\n  const anim=setInterval(()=>{\n    t+=0.04;\n    snakeAnimState.t=t;\n    drawBoard();\n    if(t>=1){\n      clearInterval(anim);\n      snakeAnimState=null;\n      players[pi].pos=dest;players[pi].animPos=dest;\n      updateScores();drawBoard();\n      cb();\n    }\n  },40);\n}\n\nfunction afterMove(){\n  current=(current+1)%players.length;\n  updateTurnLabel();\n  updateScores();\n  rolling=false;\n  document.getElementById('roll-btn').disabled=false;\n}\n\nfunction updateTurnLabel(){\n  const p=players[current];\n  document.getElementById('turn-label').innerHTML=`<span style=\"color:${p.color};font-weight:700\">${p.name}<\/span>'s turn`;\n}\n\nfunction updateScores(){\n  const s=document.getElementById('scores');\n  s.innerHTML=players.map((p,i)=>`<div class=\"score-card\"${i===current?' style=\"border:1px solid '+p.color+'\"':''}>\n    <div class=\"score-dot\" style=\"background:${p.color}\"><\/div>\n    <span>${p.name}<\/span>\n    <span style=\"color:#aaa;margin-left:4px\">sq.${p.pos||1}<\/span>\n  <\/div>`).join('');\n}\n\nfunction showWinner(pi){\n  rolling=false;\n  document.getElementById('game').style.display='none';\n  const ws=document.getElementById('winner-screen');\n  ws.style.display='flex';\n  document.getElementById('winner-name').style.color=players[pi].color;\n  document.getElementById('winner-name').textContent='\ud83c\udf89 '+players[pi].name+' wins!';\n}\n\n\/\/ Init\naddPlayer(); addPlayer();\n<\/script>\n<\/body>\n<\/html>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\nbody{background:#0d0d1a;color:#eee;font-family:'Segoe UI',sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;padding:10px}\nh1{font-size:1.5rem;color:#f9ca24;margin-bottom:8px;letter-spacing:2px}\n#container{display:flex;gap:16px;align-items:flex-start}\n#game-wrap{display:flex;flex-direction:column;align-items:center;gap:8px}\ncanvas{border:2px solid #333;border-radius:4px;background:#111}\n#side{width:160px;display:flex;flex-direction:column;gap:10px}\n.panel{background:#16213e;border-radius:10px;padding:12px;font-size:13px}\n.panel h3{color:#f9ca24;font-size:12px;letter-spacing:1px;margin-bottom:8px;text-transform:uppercase}\n#score-val{font-size:2rem;font-weight:700;color:#f9ca24}\n#level-val{font-size:1.2rem;font-weight:600;color:#7ec8e3}\n#next-letter{font-size:2.5rem;font-weight:900;text-align:center;color:#fff;text-shadow:0 0 12px #f9ca24}\n#word-display{font-size:1rem;letter-spacing:3px;color:#6bcb77;word-break:break-all;min-height:22px;font-weight:700}\n#found-words{font-size:12px;color:#aaa;max-height:100px;overflow-y:auto;line-height:1.8}\n.word-tag{display:inline-block;background:#1e3a2e;color:#6bcb77;border-radius:4px;padding:1px 6px;margin:1px;font-size:11px}\n#controls{font-size:11px;color:#666;line-height:2}\n#controls span{color:#aaa}\n#overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:#000a;display:flex;flex-direction:column;align-items:center;justify-content:center;border-radius:4px;gap:12px;z-index:10}\n#overlay h2{font-size:1.8rem;color:#f9ca24}\n#overlay p{color:#aaa;font-size:13px;text-align:center}\n.start-btn{padding:10px 28px;background:#f9ca24;color:#0d0d1a;font-weight:700;font-size:15px;border:none;border-radius:8px;cursor:pointer}\n.start-btn:hover{background:#ffd43b}\n#game-canvas-wrap{position:relative}\n#flash{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;border-radius:4px;opacity:0;background:#6bcb7744;transition:opacity .15s}\n<\/style>\n<\/head>\n<body>\n<h1>\u2b1b WORD DROP<\/h1>\n<div id=\"container\">\n  <div id=\"game-wrap\">\n    <div id=\"game-canvas-wrap\">\n      <canvas id=\"canvas\" width=\"300\" height=\"540\"><\/canvas>\n      <div id=\"overlay\">\n        <h2>WORD DROP<\/h2>\n        <p>Drop letters to spell words!<br>Longer words = more points<br><b style=\"color:#f9ca24\">3+ letter words<\/b> count<\/p>\n        <button class=\"start-btn\" onclick=\"startGame()\">Start Game<\/button>\n      <\/div>\n      <div id=\"flash\"><\/div>\n    <\/div>\n    <div style=\"font-size:11px;color:#555\">\u2190 \u2192 Move &nbsp;|&nbsp; \u2193 Fast drop &nbsp;|&nbsp; Space = Hard drop<\/div>\n  <\/div>\n\n  <div id=\"side\">\n    <div class=\"panel\">\n      <h3>Score<\/h3>\n      <div id=\"score-val\">0<\/div>\n      <div style=\"margin-top:4px;font-size:12px;color:#aaa\">Level <span id=\"level-val\">1<\/span><\/div>\n    <\/div>\n    <div class=\"panel\">\n      <h3>Next<\/h3>\n      <div id=\"next-letter\">A<\/div>\n    <\/div>\n    <div class=\"panel\">\n      <h3>Board Letters<\/h3>\n      <div id=\"word-display\"><\/div>\n    <\/div>\n    <div class=\"panel\">\n      <h3>Words Found<\/h3>\n      <div id=\"found-words\"><span style=\"color:#555\">None yet&#8230;<\/span><\/div>\n    <\/div>\n    <div class=\"panel\" id=\"controls\">\n      <h3>Controls<\/h3>\n      <span>\u2190 \u2192<\/span> Move<br>\n      <span>\u2193<\/span> Speed up<br>\n      <span>Space<\/span> Drop<br>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\nconst COLS=10,ROWS=18,SZ=30;\nconst canvas=document.getElementById('canvas');\nconst ctx=canvas.getContext('2d');\nconst COLORS={\n  A:'#e74c3c',B:'#e67e22',C:'#f1c40f',D:'#2ecc71',E:'#1abc9c',\n  F:'#3498db',G:'#9b59b6',H:'#e91e63',I:'#ff5722',J:'#795548',\n  K:'#607d8b',L:'#00bcd4',M:'#8bc34a',N:'#ff9800',O:'#673ab7',\n  P:'#f06292',Q:'#4db6ac',R:'#ff7043',S:'#66bb6a',T:'#29b6f6',\n  U:'#ab47bc',V:'#26a69a',W:'#d4e157',X:'#ef5350',Y:'#42a5f5',Z:'#ec407a'\n};\n\n\/\/ Weighted letter distribution (common letters more frequent)\nconst LETTER_POOL='AAAAAAABBCCDDDEEEEEEEEEFFGGGHHIIIIIIIJKLLLLMMNNNNNNOOOOOOPPQRRRRRRSSSSTTTTTTTUUUVVWWXYYZ'.split('');\n\n\/\/ Massive word list for checking\nconst WORDS = new Set(`the and for are but not you all can had her was one our out day get has him his how man new now old see two way who boy did its let put say she too use able acid aged also area army away baby back ball band bank base bath bear beat been belt best bird bite blow blue boat body bomb bone book born both bread brick broad burn call came care cart case cash cast cave cell chat chin chip city clam clap clay clip club coat code coil cold come cope copy cord core corn cost coup crew crop curl cute dark data date dawn dead deal dean dear debt deep deny desk dice diet dirt disk dock does dome door dose down draw drew drop drug drum duck dull dumb dump dusk dust duty each earn ease east easy edge else emit epic even ever evil exam fact fade fail fair fall fame farm fast fate fear feed feel feet fell felt fill film find fire firm fish fist five flag flat flea flew flip flow foam fold folk fond font food foot fore fork form fort foul four free from frog fuel full fund fury fuse fuss gain gale game gang gate gave gear gene gift girl give glad glow glue goal gold golf good grab gram gray grew grid grim grip grow gulf guru gust hack hair half hall halt hand hang hard harm harp hash hate have heal heap heat heel held hell help herb hide high hill hint hire hold hole holy home hook hope horn host hour huge hull hump hung hunt hurt idea idle inch into iron item jack jail jest join joke jump just keel keen keep kind king knee knew knit knob know lack lake lamb lamp land lane lark lash last late lead leak lean leap left lend lens levy liar life lift like lime limp line link lion list live load loan lock loft lore lorn lose loss loud love luck lung lure lurk made maid mail make mane many mark mare mask mass mast mate maze meal mean meat meet melt memo mesh mild mile milk mill mime mind mine mint mire miss mist mode mole monk mood moon more move much musk must myth nail name navy near neck need nest next nice nick nine node none noon norm nose note nova nude null oath obey odor omen once only open oral orb owed pace pack page paid pain pair pale palm pane park part pass past path pave peak peal pear peat peel peer pelt pent perk pest pick pine pink pipe pity plan play plea plot plow ploy plum plus poem poet poll polo pond poor pope pork port pose post pour pray prey prod prom prop pull pump pure push quit race rack rage raid rail rain rake rang rank rant rape rare rash rate rave read real ream reap rear rely rent rend rest ride rift ring rise risk roam roar robe rock rode role roll roof room rope rose rota rout rude rule rung rush rust sack safe sage sail sake sale sand sane sang sank save scan scar seal seam sear seed seek seem seen seep self sell semi send sent shed shin ship shoe shop shot show shut sick sigh silk sill silt sink siren size skid skin skip skis slab slap slat slew slim slip slop slow slug slum slur smug snap snip soar soak sock soft soil sold sole some song soon sort sour span spar spit spin spot stab stag star stay stem step stew stir stop stub stun such suit sulk sung sunk surf swap swat swim tail tale talk tall tame tank tape tare tarn taut team tear teem tell tend tent term than that them then they thin tide tilt time tire toll tone tong torn toss tour town trap tray tree trim trio trip trot true tube tuck tug tune turf turn tusk twin type ugly undo unit upon urge used vain vale vary vase vast vats veer veil vein vent very vest veto view vine visa void wade wail wait wake walk wall wane wary wash wave weal wean weed weep weld well went were whet whim whip wick wide wile will wilt wind wine wing wink wire wise wish wisp woke wolf word wore work worm worn wove wrap wren writ yawn yell yoga your zero zone`.split(' '));\n\nlet board,piece,nextLetter,score,level,gameLoop,falling,speed,paused,gameOver;\n\nfunction initBoard(){return Array.from({length:ROWS},()=>Array(COLS).fill(null));}\n\nfunction randLetter(){return LETTER_POOL[Math.floor(Math.random()*LETTER_POOL.length)];}\n\nfunction newPiece(letter){\n  return {x:Math.floor(COLS\/2),y:0,letter:letter||randLetter()};\n}\n\nfunction startGame(){\n  document.getElementById('overlay').style.display='none';\n  board=initBoard();\n  score=0;level=1;speed=800;falling=false;paused=false;gameOver=false;\n  nextLetter=randLetter();\n  piece=newPiece();\n  nextLetter=randLetter();\n  document.getElementById('found-words').innerHTML='<span style=\"color:#555\">None yet...<\/span>';\n  document.getElementById('score-val').textContent='0';\n  document.getElementById('level-val').textContent='1';\n  updateNextDisplay();\n  if(gameLoop)clearInterval(gameLoop);\n  gameLoop=setInterval(tick,speed);\n  render();\n}\n\nfunction tick(){\n  if(paused||gameOver)return;\n  if(!movePiece(0,1)){\n    placePiece();\n  }\n}\n\nfunction movePiece(dx,dy){\n  const nx=piece.x+dx, ny=piece.y+dy;\n  if(nx<0||nx>=COLS||ny>=ROWS)return false;\n  if(ny>=0&&board[ny][nx])return false;\n  piece.x=nx;piece.y=ny;\n  render();return true;\n}\n\nfunction hardDrop(){\n  while(movePiece(0,1)){}\n  placePiece();\n}\n\nfunction placePiece(){\n  if(piece.y<0){endGame();return;}\n  board[piece.y][piece.x]=piece.letter;\n  checkWords();\n  piece=newPiece(nextLetter);\n  nextLetter=randLetter();\n  updateNextDisplay();\n  updateBoardDisplay();\n  render();\n}\n\nfunction getBottomRow(){\n  \/\/ Find the lowest row that has letters\n  for(let r=ROWS-1;r>=0;r--){\n    for(let c=0;c<COLS;c++){\n      if(board[r][c])return r;\n    }\n  }\n  return -1;\n}\n\nfunction checkWords(){\n  \/\/ Collect all letters row by row from bottom, check each row and horizontal sequences\n  let found=false;\n  for(let r=ROWS-1;r>=0;r--){\n    const row=board[r];\n    \/\/ Find all contiguous sequences in this row\n    let seqs=[];\n    let cur=[];\n    for(let c=0;c<COLS;c++){\n      if(row[c]){cur.push({c,l:row[c]});}\n      else{if(cur.length>0){seqs.push([...cur]);cur=[];}}\n    }\n    if(cur.length>0)seqs.push(cur);\n\n    seqs.forEach(seq=>{\n      \/\/ Try all substrings of length 3+\n      for(let start=0;start<seq.length;start++){\n        for(let end=start+3;end<=seq.length;end++){\n          const sub=seq.slice(start,end);\n          const word=sub.map(x=>x.l).join('').toLowerCase();\n          if(WORDS.has(word)){\n            \/\/ Remove these letters\n            const pts=word.length*word.length*10;\n            score+=pts;\n            found=true;\n            addFoundWord(word,pts);\n            sub.forEach(x=>{board[r][x.c]=null;});\n            flashBoard();\n            \/\/ Apply gravity\n            applyGravity();\n          }\n        }\n      }\n    });\n  }\n  if(found){\n    score+=0;\n    level=Math.floor(score\/500)+1;\n    clearInterval(gameLoop);\n    speed=Math.max(150,800-level*60);\n    gameLoop=setInterval(tick,speed);\n    document.getElementById('score-val').textContent=score;\n    document.getElementById('level-val').textContent=level;\n  }\n  updateBoardDisplay();\n}\n\nfunction applyGravity(){\n  for(let c=0;c<COLS;c++){\n    let col=[];\n    for(let r=0;r<ROWS;r++){if(board[r][c])col.push(board[r][c]);}\n    for(let r=0;r<ROWS;r++){board[r][c]=null;}\n    for(let i=0;i<col.length;i++){board[ROWS-1-i][c]=col[col.length-1-i];}\n  }\n}\n\nfunction addFoundWord(word,pts){\n  const el=document.getElementById('found-words');\n  if(el.innerHTML.includes('None yet'))el.innerHTML='';\n  el.innerHTML=`<span class=\"word-tag\">${word} +${pts}<\/span>`+el.innerHTML;\n}\n\nfunction flashBoard(){\n  const f=document.getElementById('flash');\n  f.style.opacity='1';\n  setTimeout(()=>f.style.opacity='0',150);\n}\n\nfunction updateNextDisplay(){\n  const el=document.getElementById('next-letter');\n  el.textContent=nextLetter;\n  el.style.color=COLORS[nextLetter]||'#fff';\n  el.style.textShadow=`0 0 16px ${COLORS[nextLetter]}88`;\n}\n\nfunction updateBoardDisplay(){\n  \/\/ Show all letters currently on board (bottom row to top)\n  let letters=[];\n  for(let r=ROWS-1;r>=0;r--){\n    for(let c=0;c<COLS;c++){\n      if(board[r][c])letters.push(board[r][c]);\n    }\n  }\n  document.getElementById('word-display').textContent=letters.slice(0,30).join(' ');\n}\n\nfunction render(){\n  ctx.clearRect(0,0,canvas.width,canvas.height);\n  \/\/ Grid lines\n  ctx.strokeStyle='#1a1a2e';ctx.lineWidth=1;\n  for(let r=0;r<=ROWS;r++){ctx.beginPath();ctx.moveTo(0,r*SZ);ctx.lineTo(COLS*SZ,r*SZ);ctx.stroke();}\n  for(let c=0;c<=COLS;c++){ctx.beginPath();ctx.moveTo(c*SZ,0);ctx.lineTo(c*SZ,ROWS*SZ);ctx.stroke();}\n\n  \/\/ Board pieces\n  for(let r=0;r<ROWS;r++){\n    for(let c=0;c<COLS;c++){\n      if(board[r][c])drawBlock(c,r,board[r][c],0.85);\n    }\n  }\n\n  \/\/ Ghost piece\n  if(piece){\n    let gy=piece.y;\n    while(gy+1<ROWS&#038;&#038;!board[gy+1][piece.x])gy++;\n    if(gy!==piece.y)drawBlock(piece.x,gy,piece.letter,0.2);\n  }\n\n  \/\/ Active piece\n  if(piece&#038;&#038;piece.y>=0)drawBlock(piece.x,piece.y,piece.letter,1);\n}\n\nfunction drawBlock(c,r,letter,alpha){\n  const x=c*SZ,y=r*SZ,pad=2;\n  const col=COLORS[letter]||'#888';\n  ctx.save();\n  ctx.globalAlpha=alpha;\n  \/\/ Block bg\n  ctx.fillStyle=col+'33';\n  ctx.fillRect(x+pad,y+pad,SZ-pad*2,SZ-pad*2);\n  \/\/ Border\n  ctx.strokeStyle=col;\n  ctx.lineWidth=2;\n  ctx.strokeRect(x+pad,y+pad,SZ-pad*2,SZ-pad*2);\n  \/\/ Letter\n  ctx.fillStyle=col;\n  ctx.font=`bold ${SZ*0.55}px 'Segoe UI',monospace`;\n  ctx.textAlign='center';ctx.textBaseline='middle';\n  ctx.shadowColor=col;ctx.shadowBlur=6;\n  ctx.fillText(letter,x+SZ\/2,y+SZ\/2);\n  ctx.restore();\n}\n\nfunction endGame(){\n  gameOver=true;\n  clearInterval(gameLoop);\n  const ov=document.getElementById('overlay');\n  ov.style.display='flex';\n  ov.innerHTML=`<h2 style=\"color:#e74c3c\">GAME OVER<\/h2>\n    <p style=\"color:#aaa\">Final Score: <b style=\"color:#f9ca24;font-size:1.4rem\">${score}<\/b><br>Level ${level}<\/p>\n    <button class=\"start-btn\" onclick=\"startGame()\">Play Again<\/button>`;\n}\n\ndocument.addEventListener('keydown',e=>{\n  if(gameOver||!piece)return;\n  if(e.key==='ArrowLeft'){e.preventDefault();movePiece(-1,0);}\n  else if(e.key==='ArrowRight'){e.preventDefault();movePiece(1,0);}\n  else if(e.key==='ArrowDown'){e.preventDefault();movePiece(0,1);}\n  else if(e.key===' '){e.preventDefault();hardDrop();}\n});\n\n\/\/ Touch support\nlet touchX=null;\ncanvas.addEventListener('touchstart',e=>{touchX=e.touches[0].clientX;},{passive:true});\ncanvas.addEventListener('touchmove',e=>{\n  if(!touchX)return;\n  const dx=e.touches[0].clientX-touchX;\n  if(Math.abs(dx)>20){movePiece(dx>0?1:-1,0);touchX=e.touches[0].clientX;}\n},{passive:true});\ncanvas.addEventListener('touchend',e=>{touchX=null;},{passive:true});\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc0d Snakes &#038; Ladders \ud83e\ude9c Add Players + Add Player Start Game \ud83c\udfb2 Roll Dice New Game \ud83c\udfc6 reached square 100 first! Play Again \u2b1b WORD DROP WORD DROP Drop letters to spell words!Longer words = more points3+ letter words count Start Game \u2190 \u2192 Move &nbsp;|&nbsp; \u2193 Fast drop &nbsp;|&nbsp; Space = Hard drop [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-60","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/pages\/60","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wahpro.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=60"}],"version-history":[{"count":3,"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/pages\/60\/revisions"}],"predecessor-version":[{"id":64,"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/pages\/60\/revisions\/64"}],"wp:attachment":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=60"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}