{"id":58,"date":"2026-06-10T17:12:30","date_gmt":"2026-06-10T17:12:30","guid":{"rendered":"https:\/\/wahpro.com\/?p=58"},"modified":"2026-06-10T17:12:30","modified_gmt":"2026-06-10T17:12:30","slug":"snakes-and-ladder","status":"publish","type":"post","link":"https:\/\/wahpro.com\/?p=58","title":{"rendered":"Snakes and Ladder"},"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","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<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-58","post","type-post","status-publish","format-standard","hentry","category-games"],"_links":{"self":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/posts\/58","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"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=58"}],"version-history":[{"count":1,"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/posts\/58\/revisions"}],"predecessor-version":[{"id":59,"href":"https:\/\/wahpro.com\/index.php?rest_route=\/wp\/v2\/posts\/58\/revisions\/59"}],"wp:attachment":[{"href":"https:\/\/wahpro.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=58"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wahpro.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=58"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wahpro.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=58"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}