<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Game on Mike&#39;s Blog</title>
        <link>https://mikerossiter.tech/tags/game/</link>
        <description>Recent content in Game on Mike&#39;s Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-gb</language>
        <lastBuildDate>Fri, 31 Jul 2026 09:00:09 +0100</lastBuildDate><atom:link href="https://mikerossiter.tech/tags/game/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Cat and Mouse Game!</title>
        <link>https://mikerossiter.tech/posts/cat-and-mouse-game/</link>
        <pubDate>Thu, 03 Apr 2025 09:40:44 +0100</pubDate>
        
        <guid>https://mikerossiter.tech/posts/cat-and-mouse-game/</guid>
        <description>&lt;img src="https://mikerossiter.tech/catandmouse.png" alt="Featured image of post Cat and Mouse Game!" /&gt;&lt;h1 id=&#34;cat-and-mouse-game&#34;&gt;Cat and Mouse Game
&lt;/h1&gt;&lt;p&gt;A fun game I discovered on YouTube. It has elements of graph theory and algorithmic thinking but in essence its a &lt;strong&gt;chase and evade&lt;/strong&gt; type game that is super easy to grasp. I played this with my kids using cushions on the floor and they loved it. For all of ten minutes.&lt;/p&gt;
&lt;p&gt;Give it a go! I&amp;rsquo;ve added a score for the mouse so see how high you can get it. Sadly, there is no winning/prize involved!&lt;/p&gt;
&lt;div class=&#34;cat-mouse-game&#34;&gt;
&lt;style&gt;
  .cat-mouse-game {
    margin: 0;
    padding: 20px;
    font-family: sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .cat-mouse-game canvas {
    display: block;
    width: 100%;
    max-width: 600px;
    height: auto;
    touch-action: manipulation;
  }
  .cat-mouse-game .instructions {
    text-align: center;
    max-width: 500px;
    font-size: 14px;
    padding: 0 20px;
  }
  .cat-mouse-game .score {
    margin-bottom: 10px;
    font-size: 16px;
  }
&lt;/style&gt;
&lt;div class=&#34;score&#34; id=&#34;scoreDisplay&#34;&gt;Score: 0&lt;/div&gt;
&lt;canvas id=&#34;gameCanvas&#34; width=&#34;600&#34; height=&#34;600&#34;&gt;&lt;/canvas&gt;
&lt;div class=&#34;instructions&#34;&gt;
  &lt;p&gt;&lt;strong&gt;Instructions:&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;You are the mouse (blue). Click on a connected node to move.&lt;/p&gt;
  &lt;p&gt;The cat (red) will move after you and try to catch you.&lt;/p&gt;
  &lt;p&gt;If the cat lands on your node, the game resets and your score returns to 0.&lt;/p&gt;
  &lt;p&gt;You gain 1 point for every move you make without getting caught!&lt;/p&gt;
&lt;/div&gt;
&lt;script&gt;
const canvas = document.getElementById(&#39;gameCanvas&#39;);
canvas.width = 600;
canvas.height = 600;
const ctx = canvas.getContext(&#39;2d&#39;);
const scoreDisplay = document.getElementById(&#39;scoreDisplay&#39;);

const nodes = {
  center: { x: 300, y: 300 },
  top: { x: 300, y: 150 },
  bottomLeft: { x: 200, y: 450 },
  bottomRight: { x: 400, y: 450 },
  left: { x: 100, y: 300 },
  right: { x: 500, y: 300 }
};

const connectionLines = [
  [&#39;center&#39;, &#39;top&#39;],
  [&#39;center&#39;, &#39;bottomLeft&#39;],
  [&#39;center&#39;, &#39;bottomRight&#39;],
  [&#39;top&#39;, &#39;right&#39;],
  [&#39;right&#39;, &#39;bottomRight&#39;],
  [&#39;bottomRight&#39;, &#39;bottomLeft&#39;],
  [&#39;bottomLeft&#39;, &#39;left&#39;],
  [&#39;left&#39;, &#39;top&#39;]
];

let mouse = { node: &#39;right&#39; };
let cat = { node: &#39;center&#39; };
let score = 0;

function updateScoreDisplay() {
  scoreDisplay.textContent = `Score: ${score}`;
}

function resetGame() {
  resizeCanvasForDPR();

  const outerNodes = [&#39;top&#39;, &#39;bottomLeft&#39;, &#39;bottomRight&#39;, &#39;left&#39;, &#39;right&#39;];

  mouse.node = outerNodes[Math.floor(Math.random() * outerNodes.length)];

  const firstMoves = getValidMoves(mouse.node);
  const secondMoves = firstMoves.flatMap(n =&gt; getValidMoves(n));
  const possibleCats = secondMoves.filter(n =&gt; n !== mouse.node &amp;&amp; !firstMoves.includes(n));

  cat.node = possibleCats[Math.floor(Math.random() * possibleCats.length)] || &#39;center&#39;;

  score = 0;
  updateScoreDisplay();
  drawNodes();

  setTimeout(() =&gt; {
    moveCatTowardMouse();
    updateGame();
  }, 500);
}

function resizeCanvasForDPR() {
  const dpr = window.devicePixelRatio || 1;

  // Pick actual pixel size based on canvas container (max 600)
  const containerWidth = Math.min(canvas.parentElement.clientWidth, 600);
  const size = containerWidth;

  canvas.style.width = size + &#39;px&#39;;
  canvas.style.height = size + &#39;px&#39;;

  canvas.width = size * dpr;
  canvas.height = size * dpr;

  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.scale(dpr, dpr);

  updateNodePositions(size);
}


function getClickRadius() {
  return &#39;ontouchstart&#39; in window ? 25 : 15;
}

function updateNodePositions(size) {
  nodes.center = { x: size / 2, y: size / 2 };
  nodes.top = { x: size / 2, y: size * 0.25 };
  nodes.bottomLeft = { x: size * 0.33, y: size * 0.75 };
  nodes.bottomRight = { x: size * 0.66, y: size * 0.75 };
  nodes.left = { x: size * 0.15, y: size / 2 };
  nodes.right = { x: size * 0.85, y: size / 2 };
}

function drawNodes() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  connectionLines.forEach(([a, b]) =&gt; {
    const posA = nodes[a];
    const posB = nodes[b];
    ctx.beginPath();
    ctx.moveTo(posA.x, posA.y);
    ctx.lineTo(posB.x, posB.y);
    ctx.strokeStyle = &#39;#ccc&#39;;
    ctx.stroke();
  });

  Object.entries(nodes).forEach(([key, pos]) =&gt; {
    ctx.beginPath();
    ctx.arc(pos.x, pos.y, 10, 0, 2 * Math.PI);
    ctx.fillStyle = &#39;gray&#39;;
    ctx.fill();
  });

  const mousePos = nodes[mouse.node];
  ctx.beginPath();
  ctx.arc(mousePos.x, mousePos.y, 12, 0, 2 * Math.PI);
  ctx.fillStyle = &#39;blue&#39;;
  ctx.fill();

  const catPos = nodes[cat.node];
  ctx.beginPath();
  ctx.arc(catPos.x, catPos.y, 12, 0, 2 * Math.PI);
  ctx.fillStyle = &#39;red&#39;;
  ctx.fill();
}

function getValidMoves(node) {
  const moves = [];
  connectionLines.forEach(([a, b]) =&gt; {
    if (a === node) moves.push(b);
    else if (b === node) moves.push(a);
  });
  return moves;
}

function handleClick(e) {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  const clickedNode = Object.entries(nodes).find(([name, pos]) =&gt; {
    const dx = x - pos.x;
    const dy = y - pos.y;
    return Math.sqrt(dx * dx + dy * dy) &lt; getClickRadius();  
  });

  if (!clickedNode) return;

  const [clickedName] = clickedNode;
  const valid = getValidMoves(mouse.node);

  if (valid.includes(clickedName)) {
    mouse.node = clickedName;
    score++;
    updateScoreDisplay();
    updateGame(true);
  }
}

function moveCatTowardMouse() {
  const moves = getValidMoves(cat.node);

  if (moves.includes(mouse.node)) {
    cat.node = mouse.node;
  } else if (moves.length &gt; 0) {
    cat.node = moves[Math.floor(Math.random() * moves.length)];
  }
}

function isMouseTrapped() {
  const moves = getValidMoves(mouse.node);
  return moves.length === 1 &amp;&amp; moves[0] === &#39;center&#39; &amp;&amp; cat.node === &#39;center&#39;;
}

function updateGame(mouseMoved = false) {
  drawNodes();
  if (mouse.node === cat.node) {
    alert(`Final score: ${score}. Cat caught the mouse! Restarting game.`);
    resetGame();
  } else if (isMouseTrapped()) {
    alert(&#39;Mouse is trapped! Cat wins! Restarting game.&#39;);
    resetGame();
  } else if (mouseMoved) {
    setTimeout(() =&gt; {
      moveCatTowardMouse();
      updateGame();
    }, 500);
  }
}

canvas.addEventListener(&#39;click&#39;, handleClick);
canvas.addEventListener(&#39;touchstart&#39;, function(e) {
  // Prevent simulated mouse event
  e.preventDefault();
  const touch = e.touches[0];
  handleClick({ clientX: touch.clientX, clientY: touch.clientY });
}, { passive: false });

window.addEventListener(&#39;resize&#39;, () =&gt; {
  resetGame();
});

document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; {
  resetGame(); // ✅ wait for layout before sizing canvas
});

&lt;/script&gt;
&lt;/div&gt;
</description>
        </item>
        
    </channel>
</rss>
