{"id":11,"date":"2026-07-15T22:26:49","date_gmt":"2026-07-15T22:26:49","guid":{"rendered":"https:\/\/demo.jfxhosted.com\/?p=11"},"modified":"2026-07-15T22:38:47","modified_gmt":"2026-07-15T22:38:47","slug":"retro-maze-chase","status":"publish","type":"post","link":"https:\/\/demo.jfxhosted.com\/?p=11","title":{"rendered":"Retro Maze Chase"},"content":{"rendered":"\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n\n<h1 class=\"wp-block-heading\">Retro Maze Chase<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A standalone WordPress-native canvas demo: collect every signal dot, trigger power nodes, capture vulnerable chasers, and keep the hosting route online.<\/p>\n\n\n\n<section id=\"jfx-retro-chase-wrap\" style=\"border:1px solid #d8d8d8;background:#f7f8fa;padding:22px;margin:28px 0\">\n  <div style=\"display:flex;flex-wrap:wrap;gap:24px;align-items:flex-start\">\n    <canvas id=\"jfxRetroChaseCanvas\" width=\"408\" height=\"408\" style=\"width:min(408px,100%);height:auto;background:#070b16;border:3px solid #101522;box-shadow:0 8px 28px rgba(0,0,0,.18)\"><\/canvas>\n    <div style=\"min-width:240px;max-width:380px\">\n      <h2 style=\"margin-top:0\">Route Runner<\/h2>\n      <p id=\"jfxRetroChaseStatus\" style=\"font-weight:700\">Status: ready.<\/p>\n      <p id=\"jfxRetroChaseScore\" style=\"font-family:monospace;font-size:18px\">Score 0 | Lives 3 | Dots 0<\/p>\n      <p style=\"margin-bottom:10px\">Move with arrow keys or WASD. Collect signal dots, grab cyan power nodes, then catch vulnerable chasers for bonus points.<\/p>\n      <div style=\"display:grid;grid-template-columns:repeat(3,52px);gap:7px;width:170px;margin:14px 0\">\n        <span><\/span><button type=\"button\" data-chase-dir=\"up\">Up<\/button><span><\/span>\n        <button type=\"button\" data-chase-dir=\"left\">Left<\/button><button type=\"button\" data-chase-dir=\"down\">Down<\/button><button type=\"button\" data-chase-dir=\"right\">Right<\/button>\n      <\/div>\n      <button type=\"button\" id=\"jfxRetroChaseReset\">Restart<\/button>\n      <p style=\"font-size:14px;margin-top:16px\">This post is intentionally unlinked from the site navigation. It is stored in WordPress content and uses no external scripts, images, or trackers.<\/p>\n    <\/div>\n  <\/div>\n<\/section>\n<script id=\"jfxRetroMazeChaseScript\">\n(function(){\n  var canvas = document.getElementById('jfxRetroChaseCanvas');\n  if (!canvas) { return; }\n  var ctx = canvas.getContext('2d');\n  var statusEl = document.getElementById('jfxRetroChaseStatus');\n  var scoreEl = document.getElementById('jfxRetroChaseScore');\n  var tile = 24;\n  var grid = [\n    '#################',\n    '#........#......#',\n    '#.###.##.#.##.#.#',\n    '#.#.....#....#..#',\n    '#.#.###.###.###.#',\n    '#...#.......#...#',\n    '###.#.#####.#.###',\n    '#.....#...#.....#',\n    '#.###.#.#.#.###.#',\n    '#.....#...#.....#',\n    '###.#.#####.#.###',\n    '#...#.......#...#',\n    '#.#.###.###.###.#',\n    '#.#.....#....#..#',\n    '#.###.##.#.##.#.#',\n    '#......#........#',\n    '#################'\n  ];\n  var startPlayer = {x:1,y:1};\n  var startChasers = [\n    {x:15,y:1,color:'#ff4d6d',homeX:15,homeY:1},\n    {x:1,y:15,color:'#ffb703',homeX:1,homeY:15},\n    {x:15,y:15,color:'#8b5cf6',homeX:15,homeY:15},\n    {x:8,y:8,color:'#2dd4bf',homeX:8,homeY:8}\n  ];\n  var powerNodes = [\n    {x:1,y:5},\n    {x:15,y:5},\n    {x:1,y:11},\n    {x:15,y:11}\n  ];\n  var player;\n  var chasers;\n  var dots;\n  var score;\n  var lives;\n  var captureTicks;\n  var captureCombo;\n  var won;\n  var lost;\n  var tickTimer = null;\n\n  function cloneDots(){\n    var rows = [];\n    for (var y = 0; y < grid.length; y = y + 1) {\n      rows[y] = grid[y].split('');\n    }\n    for (var i = 0; i < powerNodes.length; i = i + 1) {\n      rows[powerNodes[i].y][powerNodes[i].x] = 'o';\n    }\n    return rows;\n  }\n\n  function dotCount(){\n    var count = 0;\n    for (var y = 0; y < dots.length; y = y + 1) {\n      for (var x = 0; x < dots[y].length; x = x + 1) {\n        if (dots[y][x] === '.') { count = count + 1; }\n        if (dots[y][x] === 'o') { count = count + 1; }\n      }\n    }\n    return count;\n  }\n\n  function isWall(x,y){\n    if (y < 0) { return true; }\n    if (y >= grid.length) { return true; }\n    if (x < 0) { return true; }\n    if (x >= grid[y].length) { return true; }\n    return grid[y][x] === '#';\n  }\n\n  function resetPositions(){\n    player = {x:startPlayer.x,y:startPlayer.y};\n    chasers = [];\n    for (var i = 0; i < startChasers.length; i = i + 1) {\n      chasers.push({\n        x:startChasers[i].homeX,\n        y:startChasers[i].homeY,\n        color:startChasers[i].color,\n        homeX:startChasers[i].homeX,\n        homeY:startChasers[i].homeY\n      });\n    }\n  }\n\n  function restart(){\n    dots = cloneDots();\n    score = 0;\n    lives = 3;\n    captureTicks = 0;\n    captureCombo = 0;\n    won = false;\n    lost = false;\n    resetPositions();\n    if (tickTimer) { clearInterval(tickTimer); }\n    tickTimer = setInterval(gameTick, 230);\n    draw();\n    updateStatus('Status: chase started.');\n  }\n\n  function updateScore(){\n    scoreEl.textContent = 'Score ' + score + ' | Lives ' + lives + ' | Dots ' + dotCount() + ' | Power ' + captureTicks;\n  }\n\n  function updateStatus(text){\n    statusEl.textContent = text;\n    updateScore();\n  }\n\n  function drawCell(x,y,color,pad){\n    ctx.fillStyle = color;\n    ctx.fillRect(x * tile + pad, y * tile + pad, tile - pad * 2, tile - pad * 2);\n  }\n\n  function draw(){\n    ctx.clearRect(0,0,canvas.width,canvas.height);\n    ctx.fillStyle = '#070b16';\n    ctx.fillRect(0,0,canvas.width,canvas.height);\n    for (var y = 0; y < grid.length; y = y + 1) {\n      for (var x = 0; x < grid[y].length; x = x + 1) {\n        if (grid[y][x] === '#') {\n          drawCell(x,y,'#1d4ed8',1);\n        } else {\n          drawCell(x,y,'#09111f',1);\n          if (dots[y][x] === '.') {\n            ctx.fillStyle = '#f9fafb';\n            ctx.beginPath();\n            ctx.arc(x * tile + tile \/ 2, y * tile + tile \/ 2, 3, 0, Math.PI * 2);\n            ctx.fill();\n          }\n          if (dots[y][x] === 'o') {\n            ctx.fillStyle = '#67e8f9';\n            ctx.beginPath();\n            ctx.arc(x * tile + tile \/ 2, y * tile + tile \/ 2, 7, 0, Math.PI * 2);\n            ctx.fill();\n            ctx.fillStyle = '#ffffff';\n            ctx.beginPath();\n            ctx.arc(x * tile + tile \/ 2, y * tile + tile \/ 2, 3, 0, Math.PI * 2);\n            ctx.fill();\n          }\n        }\n      }\n    }\n    ctx.fillStyle = '#facc15';\n    ctx.beginPath();\n    ctx.arc(player.x * tile + tile \/ 2, player.y * tile + tile \/ 2, 9, 0, Math.PI * 2);\n    ctx.fill();\n    ctx.fillStyle = '#111827';\n    ctx.fillRect(player.x * tile + 13, player.y * tile + 7, 3, 3);\n    for (var i = 0; i < chasers.length; i = i + 1) {\n      if (captureTicks > 0) {\n        ctx.fillStyle = '#38bdf8';\n      } else {\n        ctx.fillStyle = chasers[i].color;\n      }\n      ctx.beginPath();\n      ctx.arc(chasers[i].x * tile + tile \/ 2, chasers[i].y * tile + tile \/ 2, 9, Math.PI, 0);\n      ctx.lineTo(chasers[i].x * tile + tile - 3, chasers[i].y * tile + tile - 4);\n      ctx.lineTo(chasers[i].x * tile + 3, chasers[i].y * tile + tile - 4);\n      ctx.closePath();\n      ctx.fill();\n      ctx.fillStyle = '#ffffff';\n      ctx.fillRect(chasers[i].x * tile + 7, chasers[i].y * tile + 9, 3, 3);\n      ctx.fillRect(chasers[i].x * tile + 14, chasers[i].y * tile + 9, 3, 3);\n      if (captureTicks > 0) {\n        ctx.fillStyle = '#0f172a';\n        ctx.fillRect(chasers[i].x * tile + 7, chasers[i].y * tile + 14, 10, 2);\n      }\n    }\n    updateScore();\n  }\n\n  function tryMove(dx,dy){\n    if (won) { return; }\n    if (lost) { return; }\n    var nx = player.x + dx;\n    var ny = player.y + dy;\n    if (isWall(nx,ny)) { return; }\n    player.x = nx;\n    player.y = ny;\n    if (dots[ny][nx] === '.') {\n      dots[ny][nx] = ' ';\n      score = score + 10;\n    }\n    if (dots[ny][nx] === 'o') {\n      dots[ny][nx] = ' ';\n      score = score + 50;\n      captureTicks = 45;\n      captureCombo = 0;\n      updateStatus('Status: power node active. Chasers are vulnerable.');\n    }\n    checkCollisions();\n    if (dotCount() === 0) {\n      won = true;\n      clearInterval(tickTimer);\n      updateStatus('Status: all signals collected. You win.');\n    }\n    draw();\n  }\n\n  function validMoves(entity){\n    var moves = [\n      {dx:0,dy:-1,name:'up'},\n      {dx:0,dy:1,name:'down'},\n      {dx:-1,dy:0,name:'left'},\n      {dx:1,dy:0,name:'right'}\n    ];\n    var valid = [];\n    for (var i = 0; i < moves.length; i = i + 1) {\n      if (!isWall(entity.x + moves[i].dx, entity.y + moves[i].dy)) {\n        valid.push(moves[i]);\n      }\n    }\n    return valid;\n  }\n\n  function moveChaser(chaser,index){\n    var valid = validMoves(chaser);\n    if (valid.length === 0) { return; }\n    var best = valid[0];\n    var bestScore = 9999;\n    for (var i = 0; i < valid.length; i = i + 1) {\n      var tx = chaser.x + valid[i].dx;\n      var ty = chaser.y + valid[i].dy;\n      var distance = Math.abs(tx - player.x) + Math.abs(ty - player.y);\n      if (captureTicks > 0) { distance = 0 - distance; }\n      if (index === 1) { distance = distance + Math.abs(tx - startPlayer.x); }\n      if (index === 2) {\n        if ((score + i) % 4 === 0) { distance = distance + 3; }\n      }\n      if (distance < bestScore) {\n        bestScore = distance;\n        best = valid[i];\n      }\n    }\n    if (index === 3) {\n      var choice = (score + chaser.x + chaser.y) % valid.length;\n      best = valid[choice];\n    }\n    chaser.x = chaser.x + best.dx;\n    chaser.y = chaser.y + best.dy;\n  }\n\n  function resetChaser(chaser){\n    chaser.x = chaser.homeX;\n    chaser.y = chaser.homeY;\n  }\n\n  function sameCell(a,b){\n    if (a.x !== b.x) { return false; }\n    if (a.y !== b.y) { return false; }\n    return true;\n  }\n\n  function loseLife(){\n    lives = lives - 1;\n    if (lives <= 0) {\n      lost = true;\n      clearInterval(tickTimer);\n      updateStatus('Status: route failed. Restart to try again.');\n      return;\n    }\n    resetPositions();\n    updateStatus('Status: caught. Rerouting with ' + lives + ' lives left.');\n  }\n\n  function checkCollisions(){\n    for (var i = 0; i < chasers.length; i = i + 1) {\n      if (sameCell(player, chasers[i])) {\n        if (captureTicks > 0) {\n          captureCombo = captureCombo + 1;\n          score = score + 200 * captureCombo;\n          resetChaser(chasers[i]);\n          updateStatus('Status: chaser captured for bonus points.');\n          draw();\n          return;\n        }\n        loseLife();\n        return;\n      }\n    }\n  }\n\n  function gameTick(){\n    if (won) { return; }\n    if (lost) { return; }\n    for (var i = 0; i < chasers.length; i = i + 1) {\n      moveChaser(chasers[i], i);\n      checkCollisions();\n      if (lost) { break; }\n    }\n    if (captureTicks > 0) {\n      captureTicks = captureTicks - 1;\n      if (captureTicks === 0) {\n        captureCombo = 0;\n        updateStatus('Status: power expired. Keep routing.');\n      }\n    }\n    draw();\n  }\n\n  document.addEventListener('keydown', function(e){\n    var k = e.key.toLowerCase();\n    if (k === 'arrowup') { e.preventDefault(); tryMove(0,-1); }\n    if (k === 'w') { e.preventDefault(); tryMove(0,-1); }\n    if (k === 'arrowdown') { e.preventDefault(); tryMove(0,1); }\n    if (k === 's') { e.preventDefault(); tryMove(0,1); }\n    if (k === 'arrowleft') { e.preventDefault(); tryMove(-1,0); }\n    if (k === 'a') { e.preventDefault(); tryMove(-1,0); }\n    if (k === 'arrowright') { e.preventDefault(); tryMove(1,0); }\n    if (k === 'd') { e.preventDefault(); tryMove(1,0); }\n  });\n\n  document.querySelectorAll('[data-chase-dir]').forEach(function(btn){\n    btn.addEventListener('click', function(){\n      var dir = btn.getAttribute('data-chase-dir');\n      if (dir === 'up') { tryMove(0,-1); }\n      if (dir === 'down') { tryMove(0,1); }\n      if (dir === 'left') { tryMove(-1,0); }\n      if (dir === 'right') { tryMove(1,0); }\n    });\n  });\n  document.getElementById('jfxRetroChaseReset').addEventListener('click', restart);\n  restart();\n})();\n<\/script>\n\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Retro Maze Chase A standalone WordPress-native canvas demo: collect every signal dot, trigger power nodes, capture vulnerable chasers, and keep the hosting route online. Route Runner Status: ready. Score 0 | Lives 3 | Dots 0 Move with arrow keys or WASD. Collect signal dots, grab cyan power nodes, then catch vulnerable chasers for bonus [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-11","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"acf":[],"_links":{"self":[{"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=\/wp\/v2\/posts\/11","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11"}],"version-history":[{"count":1,"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=\/wp\/v2\/posts\/11\/revisions"}],"predecessor-version":[{"id":12,"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=\/wp\/v2\/posts\/11\/revisions\/12"}],"wp:attachment":[{"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demo.jfxhosted.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}