{"id":6,"date":"2026-02-05T23:03:31","date_gmt":"2026-02-05T23:03:31","guid":{"rendered":"https:\/\/daan.joshuabink.nl\/?page_id=6"},"modified":"2026-02-06T00:10:18","modified_gmt":"2026-02-06T00:10:18","slug":"thuis","status":"publish","type":"page","link":"https:\/\/daan.joshuabink.nl\/","title":{"rendered":"Thuis"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"6\" class=\"elementor elementor-6\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c6dd272 e-flex e-con-boxed e-con e-parent\" data-id=\"c6dd272\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1859e68 elementor-widget elementor-widget-html\" data-id=\"1859e68\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"bbGame\">\n  <header class=\"top\">\n    <h1>Bier en Bitterbal<\/h1>\n    <p class=\"legend\">\n      <span class=\"chip chip--beer\" aria-hidden=\"true\"><\/span> Bier\n      <span class=\"sep\">|<\/span>\n      <span class=\"chip chip--ball\" aria-hidden=\"true\"><\/span> Bitterbal\n    <\/p>\n  <\/header>\n\n  <section class=\"card\" aria-label=\"Spel\">\n    <div class=\"hud\">\n      <div class=\"badge\" aria-live=\"polite\">\n        <span class=\"dot dot--beer\" id=\"bbTurnDot\" aria-hidden=\"true\"><\/span>\n        <span id=\"bbTurnText\">Aan zet: Bier<\/span>\n      <\/div>\n\n      <div class=\"controls\">\n        <div class=\"selectWrap\">\n          <select class=\"select\" id=\"bbModeSelect\" aria-label=\"Speelmodus\">\n            <option value=\"pvp\">Tegen elkaar<\/option>\n            <option value=\"bot\">Tegen bot<\/option>\n          <\/select>\n        <\/div>\n        <button class=\"btn\" id=\"bbNewGameBtn\" type=\"button\">Nieuw spel<\/button>\n        <button class=\"btn\" id=\"bbUndoBtn\" type=\"button\" disabled>Undo<\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"board\" id=\"bbBoard\" role=\"grid\" aria-label=\"Bord\">\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 1\"><\/button>\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 2\"><\/button>\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 3\"><\/button>\n\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 4\"><\/button>\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 5\"><\/button>\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 6\"><\/button>\n\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 7\"><\/button>\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 8\"><\/button>\n      <button class=\"cell\" type=\"button\" role=\"gridcell\" aria-label=\"Vak 9\"><\/button>\n    <\/div>\n\n    <div class=\"status\">\n      <span id=\"bbMessage\">Klik op een vakje om te beginnen.<\/span>\n      <span id=\"bbScore\" aria-label=\"Score\">Bier 0 \u00b7 0 Bitterbal<\/span>\n    <\/div>\n\n    <div class=\"overlay\" id=\"bbOverlay\" aria-hidden=\"true\">\n      <canvas id=\"bbConfetti\"><\/canvas>\n      <div class=\"modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"bbWinTitle\">\n        <div class=\"trophy\" aria-hidden=\"true\"><\/div>\n        <h2 id=\"bbWinTitle\">Gewonnen<\/h2>\n        <p id=\"bbWinText\">Proost<\/p>\n        <div class=\"row\">\n          <button class=\"btn\" id=\"bbPlayAgainBtn\" type=\"button\">Nog een potje<\/button>\n          <button class=\"btn\" id=\"bbCloseOverlayBtn\" type=\"button\">Sluiten<\/button>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/section>\n<\/div>\n\n<script>\n(() => {\n  const root = document.currentScript.previousElementSibling; \/\/ .bbGame\n  if (!root || !root.classList || !root.classList.contains('bbGame')) return;\n\n  \/\/ Imgur \"page\" links werken niet altijd als image src\n  \/\/ Gebruik directe i.imgur.com links. Als je bestand geen png is, verander .png naar .jpg\n  const BEER_IMG = \"https:\/\/i.imgur.com\/WsNvXeH.png\";\n  const BALL_IMG = \"https:\/\/i.imgur.com\/qE7b8qD.png\";\n\n  const boardEl = root.querySelector('#bbBoard');\n  const cells = Array.from(boardEl.querySelectorAll('.cell'));\n\n  const turnText = root.querySelector('#bbTurnText');\n  const turnDot = root.querySelector('#bbTurnDot');\n  const msgEl = root.querySelector('#bbMessage');\n  const scoreEl = root.querySelector('#bbScore');\n\n  const newGameBtn = root.querySelector('#bbNewGameBtn');\n  const undoBtn = root.querySelector('#bbUndoBtn');\n  const modeSelect = root.querySelector('#bbModeSelect');\n\n  const overlay = root.querySelector('#bbOverlay');\n  const winTitle = root.querySelector('#bbWinTitle');\n  const winText = root.querySelector('#bbWinText');\n  const playAgainBtn = root.querySelector('#bbPlayAgainBtn');\n  const closeOverlayBtn = root.querySelector('#bbCloseOverlayBtn');\n\n  const confettiCanvas = root.querySelector('#bbConfetti');\n  const ctx = confettiCanvas.getContext('2d');\n\n  const Player = { BEER:'beer', BALL:'ball' };\n\n  let board = Array(9).fill(null);\n  let turn = Player.BEER;\n  let gameOver = false;\n\n  let history = [];\n  let score = { beer: 0, ball: 0 };\n\n  let mode = 'pvp';\n  const botPlays = Player.BALL;\n\n  const wins = [\n    [0,1,2],[3,4,5],[6,7,8],\n    [0,3,6],[1,4,7],[2,5,8],\n    [0,4,8],[2,4,6]\n  ];\n\n  function setStatus(t){ msgEl.textContent = t; }\n\n  function updateTurnUI(){\n    const isBeer = turn === Player.BEER;\n    turnText.textContent = 'Aan zet: ' + (isBeer ? 'Bier' : 'Bitterbal');\n    turnDot.classList.toggle('dot--beer', isBeer);\n    turnDot.classList.toggle('dot--ball', !isBeer);\n  }\n\n  function updateScoreUI(){\n    scoreEl.textContent = `Bier ${score.beer} \u00b7 ${score.ball} Bitterbal`;\n  }\n\n  function cellTokenHTML(kind){\n    if (kind === Player.BEER){\n      return `\n        <div class=\"token3d\">\n          <img decoding=\"async\" class=\"tokenImg tokenImg--beer\" src=\"${BEER_IMG}\" alt=\"Bier\" draggable=\"false\" loading=\"lazy\">\n        <\/div>`;\n    }\n    if (kind === Player.BALL){\n      return `\n        <div class=\"token3d\">\n          <img decoding=\"async\" class=\"tokenImg tokenImg--ball\" src=\"${BALL_IMG}\" alt=\"Bitterbal\" draggable=\"false\" loading=\"lazy\">\n        <\/div>`;\n    }\n    return '';\n  }\n\n  function render(){\n    cells.forEach((btn, i) => {\n      const v = board[i];\n      btn.innerHTML = v ? cellTokenHTML(v) : '';\n      btn.setAttribute('aria-label',\n        `Vak ${i+1}: ` + (v ? (v === Player.BEER ? 'Bier' : 'Bitterbal') : 'Leeg')\n      );\n    });\n    undoBtn.disabled = (mode === 'bot') ? (history.length < 2 || gameOver) : (history.length === 0 || gameOver);\n    updateTurnUI();\n    updateScoreUI();\n  }\n\n  function winnerFor(state){\n    for (const [a,b,c] of wins){\n      if (state[a] && state[a] === state[b] && state[a] === state[c]) return state[a];\n    }\n    return null;\n  }\n\n  function isDraw(){ return board.every(v => v !== null); }\n\n  function setGameEnabled(enabled){\n    cells.forEach(c => c.disabled = !enabled);\n  }\n\n  function showOverlay(title, text){\n    winTitle.textContent = title;\n    winText.textContent = text;\n    overlay.classList.add('show');\n    overlay.setAttribute('aria-hidden','false');\n    startConfetti();\n  }\n\n  function hideOverlay(){\n    overlay.classList.remove('show');\n    overlay.setAttribute('aria-hidden','true');\n    stopConfetti();\n  }\n\n  function endGame(winner){\n    gameOver = true;\n    setGameEnabled(false);\n\n    if (winner){\n      score[winner] += 1;\n      const who = winner === Player.BEER ? 'Bier' : 'Bitterbal';\n      setStatus(who + ' wint');\n      showOverlay('Gewonnen', who + ' wint. Proost');\n    } else {\n      setStatus('Gelijkspel');\n      showOverlay('Gelijkspel', 'Tijd voor een rondje');\n    }\n    render();\n  }\n\n  function resetBoard(keepScore=true){\n    board = Array(9).fill(null);\n    history = [];\n    gameOver = false;\n    turn = Player.BEER;\n    setGameEnabled(true);\n    hideOverlay();\n    setStatus(mode === 'bot' ? 'Je bent Bier. Bot is Bitterbal.' : 'Tegen elkaar. Bier begint.');\n    if (!keepScore) score = { beer: 0, ball: 0 };\n    render();\n  }\n\n  function toggleTurn(){ turn = (turn === Player.BEER) ? Player.BALL : Player.BEER; }\n\n  function playAt(idx, byBot=false){\n    if (gameOver) return;\n    if (board[idx] !== null) return;\n\n    board[idx] = turn;\n    history.push({ idx, prevTurn: turn });\n\n    const w = winnerFor(board);\n    if (w){ endGame(w); return; }\n    if (isDraw()){ endGame(null); return; }\n\n    toggleTurn();\n    setStatus('Aan zet: ' + (turn === Player.BEER ? 'Bier' : 'Bitterbal') + '.');\n    render();\n\n    if (!byBot && mode === 'bot' && turn === botPlays){\n      setTimeout(botMove, 240);\n    }\n  }\n\n  function undo(){\n    if (history.length === 0) return;\n    if (gameOver) return;\n\n    if (mode === 'bot'){\n      if (history.length >= 1){ const last = history.pop(); board[last.idx] = null; }\n      if (history.length >= 1){\n        const last2 = history.pop();\n        board[last2.idx] = null;\n        turn = last2.prevTurn;\n      } else {\n        turn = Player.BEER;\n      }\n      setStatus('Zet teruggedraaid. Aan zet: Bier.');\n    } else {\n      const last = history.pop();\n      board[last.idx] = null;\n      turn = last.prevTurn;\n      setStatus('Zet teruggedraaid. Aan zet: ' + (turn === Player.BEER ? 'Bier' : 'Bitterbal') + '.');\n    }\n\n    setGameEnabled(true);\n    gameOver = false;\n    render();\n  }\n\n  function availableMoves(state){\n    const moves = [];\n    for (let i=0;i<9;i++) if (state[i] === null) moves.push(i);\n    return moves;\n  }\n\n  function minimax(state, current, maximizing, alpha, beta){\n    const w = winnerFor(state);\n    if (w === botPlays) return { score: 10 };\n    if (w && w !== botPlays) return { score: -10 };\n    if (state.every(v => v !== null)) return { score: 0 };\n\n    const moves = [];\n    for (let i=0;i<9;i++){\n      if (state[i] !== null) continue;\n      const next = state.slice();\n      next[i] = current;\n      const nextPlayer = (current === Player.BEER) ? Player.BALL : Player.BEER;\n      const res = minimax(next, nextPlayer, !maximizing, alpha, beta);\n      moves.push({ idx:i, score: res.score });\n\n      if (maximizing) alpha = Math.max(alpha, res.score);\n      else beta = Math.min(beta, res.score);\n\n      if (beta <= alpha) break;\n    }\n\n    if (maximizing){\n      let best = moves[0];\n      for (const m of moves) if (m.score > best.score) best = m;\n      return best;\n    } else {\n      let best = moves[0];\n      for (const m of moves) if (m.score < best.score) best = m;\n      return best;\n    }\n  }\n\n  function botMove(){\n    if (gameOver) return;\n    if (turn !== botPlays) return;\n\n    const state = board.slice();\n    const best = minimax(state, botPlays, true, -Infinity, Infinity);\n    const idx = (best && typeof best.idx === 'number') ? best.idx : availableMoves(state)[0];\n    playAt(idx, true);\n  }\n\n  \/* confetti in thema kleuren *\/\n  let confettiAnim = null;\n  let pieces = [];\n  let startTs = 0;\n\n  const themeColors = [\n    { hue: 38, sat: 95, light: 60 },\n    { hue: 45, sat: 90, light: 74 },\n    { hue: 28, sat: 70, light: 54 },\n    { hue: 2,  sat: 90, light: 60 }\n  ];\n\n  function resizeCanvas(){\n    const rect = overlay.getBoundingClientRect();\n    confettiCanvas.width = Math.max(1, Math.floor(rect.width * devicePixelRatio));\n    confettiCanvas.height = Math.max(1, Math.floor(rect.height * devicePixelRatio));\n    confettiCanvas.style.width = rect.width + 'px';\n    confettiCanvas.style.height = rect.height + 'px';\n    ctx.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0);\n  }\n\n  function makePieces(count){\n    const rect = overlay.getBoundingClientRect();\n    const w = rect.width, h = rect.height;\n    const arr = [];\n    for (let i=0;i<count;i++){\n      const pick = themeColors[Math.floor(Math.random()*themeColors.length)];\n      arr.push({\n        x: Math.random()*w,\n        y: -20 - Math.random()*h*0.2,\n        vx: (Math.random()-0.5)*2.2,\n        vy: 2.5 + Math.random()*3.2,\n        rot: Math.random()*Math.PI,\n        vr: (Math.random()-0.5)*0.25,\n        size: 6 + Math.random()*8,\n        shape: Math.random() < 0.5 ? 'rect' : 'tri',\n        color: `hsl(${pick.hue} ${pick.sat}% ${pick.light}%)`,\n        alpha: 0.92\n      });\n    }\n    return arr;\n  }\n\n  function drawPiece(p){\n    ctx.save();\n    ctx.translate(p.x, p.y);\n    ctx.rotate(p.rot);\n    ctx.globalAlpha = p.alpha;\n    ctx.fillStyle = p.color;\n\n    if (p.shape === 'rect'){\n      ctx.fillRect(-p.size\/2, -p.size\/2, p.size, p.size*0.65);\n    } else {\n      ctx.beginPath();\n      ctx.moveTo(-p.size\/2, p.size\/2);\n      ctx.lineTo(0, -p.size\/2);\n      ctx.lineTo(p.size\/2, p.size\/2);\n      ctx.closePath();\n      ctx.fill();\n    }\n    ctx.restore();\n  }\n\n  function step(ts){\n    if (!startTs) startTs = ts;\n    const rect = overlay.getBoundingClientRect();\n    const w = rect.width, h = rect.height;\n\n    ctx.clearRect(0,0,w,h);\n\n    for (const p of pieces){\n      p.x += p.vx;\n      p.y += p.vy;\n      p.rot += p.vr;\n      p.vy += 0.02;\n\n      const t = (ts - startTs) \/ 2200;\n      if (t > 0.7) p.alpha = Math.max(0, 1 - (t-0.7)\/0.3);\n\n      if (p.y > h + 40){\n        p.y = -30;\n        p.x = Math.random()*w;\n        p.vy = 2.5 + Math.random()*3.2;\n        p.alpha = 0.92;\n      }\n      drawPiece(p);\n    }\n\n    if (ts - startTs < 2200){\n      confettiAnim = requestAnimationFrame(step);\n    } else {\n      stopConfetti();\n    }\n  }\n\n  function startConfetti(){\n    resizeCanvas();\n    pieces = makePieces(170);\n    startTs = 0;\n    cancelAnimationFrame(confettiAnim);\n    confettiAnim = requestAnimationFrame(step);\n    window.addEventListener('resize', resizeCanvas, { passive:true });\n  }\n\n  function stopConfetti(){\n    cancelAnimationFrame(confettiAnim);\n    confettiAnim = null;\n    pieces = [];\n    startTs = 0;\n    const rect = overlay.getBoundingClientRect();\n    ctx.clearRect(0,0,rect.width,rect.height);\n    window.removeEventListener('resize', resizeCanvas);\n  }\n\n  \/* UI wiring *\/\n  cells.forEach((btn, i) => btn.addEventListener('click', () => {\n    if (mode === 'bot' && turn === botPlays) return;\n    playAt(i, false);\n  }));\n\n  newGameBtn.addEventListener('click', () => resetBoard(true));\n  undoBtn.addEventListener('click', undo);\n\n  modeSelect.addEventListener('change', () => {\n    mode = modeSelect.value;\n    resetBoard(true);\n  });\n\n  playAgainBtn.addEventListener('click', () => resetBoard(true));\n  closeOverlayBtn.addEventListener('click', hideOverlay);\n\n  overlay.addEventListener('click', (e) => {\n    if (e.target === overlay) hideOverlay();\n  });\n\n  window.addEventListener('keydown', (e) => {\n    const k = e.key;\n    if (k >= '1' && k <= '9'){\n      if (gameOver) return;\n      const idx = Number(k) - 1;\n      if (mode === 'bot' && turn === botPlays) return;\n      playAt(idx, false);\n    }\n    if (k === 'Escape' && overlay.classList.contains('show')) hideOverlay();\n  });\n\n  mode = modeSelect.value;\n  resetBoard(true);\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Bier en Bitterbal Bier | Bitterbal Aan zet: Bier Tegen elkaarTegen bot Nieuw spel Undo Klik op een vakje om te beginnen. Bier 0 \u00b7 0 Bitterbal Gewonnen Proost Nog een potje Sluiten<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-6","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=\/wp\/v2\/pages\/6","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6"}],"version-history":[{"count":32,"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=\/wp\/v2\/pages\/6\/revisions"}],"predecessor-version":[{"id":55,"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=\/wp\/v2\/pages\/6\/revisions\/55"}],"wp:attachment":[{"href":"https:\/\/daan.joshuabink.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}