Pacman Example

The game of Pac-Man implemented by @mathiastiberghien in Vega. To play the game, use the cursor keys to navigate. Move over the gray circles to power up. The game keeps a high score.

You can learn how this Pac-Man game was created in this tutorial.

Vega JSON Specification <>

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "An implementation of the classic video game Pacman.",
  "width":600,
  "height":600,
  "signals":[
    {
      "name": "grid",
      "value": {"width":15, "height":15}
    },
    {
      "name": "rangeWidthDelta",
      "update": "(width - blockSize*grid.width)/2"
    },
    {
      "name": "rangeHeightDelta",
      "update": "(height - blockSize*grid.height)/2"
    },
    {
      "name": "blockSize",
      "init": "min(width/grid.width, height/grid.height)"
    },
    {
      "name": "pacManIsOpen",
      "init": "true",
      "on":[
        {
          "events": {"type": "timer", "throttle": 500}, "update": "!pacManIsOpen"
        }
      ]
    },
    {
        "name": "superPower",
        "init": "0",
        "on": [{"events": {"signal": "pacManX || pacManY"}, "force": true, "update": "superPower > 0 ? superPower - 1 : 0"},
        {"events": {"signal": "testPowerEaten"}, "update": "testPowerEaten? 30 : superPower"}]
    },
    {
      "name": "restart",
      "on":[{"events": "timer:500", "update": "!superPower && ((gRedX === pacManX && gRedY === pacManY) || (gBlueX === pacManX && gBlueY === pacManY) || (gGreenX === pacManX && gGreenY === pacManY) || (gOrangeX === pacManX && gOrangeY === pacManY))"}]
    },
    {
      "name": "testEatenGhost",
      "update": "superPower ? ((gRedX === pacManX && gRedY === pacManY) ? gRed : (gBlueX === pacManX && gBlueY === pacManY) ? gBlue : (gGreenX === pacManX && gGreenY === pacManY) ? gGreen : (gOrangeX === pacManX && gOrangeY === pacManY) ? gOrange : null) : null"
    },
    {
      "name": "testEaten",
      "update": "!restart && (indata('gums', 'key', pacManX+'-'+pacManY) && !indata('eatenGums', 'key', pacManX+'-'+pacManY)) ? {x:pacManX, y:pacManY, key:pacManX+'-'+pacManY} : null"
    },
    {
      "name": "testPowerEaten",
      "update": "!restart && (indata('powerGums', 'key', pacManX+'-'+pacManY) && !indata('eatenGums', 'key', pacManX+'-'+pacManY)) ? {x:pacManX, y:pacManY, key:pacManX+'-'+pacManY} : null"
    },
    {
      "name": "score",
      "init": "0",
      "on": [
        {"events": {"signal": "testEaten"},"update": "testEaten ? score + 10 : score"},
        {"events": {"signal": "testPowerEaten"},"update": "testPowerEaten ? score + 50 : score"},
        {"events": {"signal": "testEatenGhost"},"update": "testEatenGhost ? score + 100 : score"},
        {"events": {"signal": "restart"}, "force":true ,"update": "restart ? 0 : score"}
      ]
    },
    {
      "name": "hiScore",
      "init": "0",
      "on":[
        {"events": {"signal": "score"}, "update": "max(score,hiScore)"}
      ]
    },

    {
      "name": "gRed",
      "init": "data('ghosts')[0]"
    },
    {
      "name": "gRedDelta",
      "update": "{dx:pacManX-gRedX, dy: pacManY-gRedY}"
    },
    {
      "name": "gRedLastDir",
      "update": "gRedDirection"
    },
    {
      "name": "gRedPreferences",
      "update": "setdata('gRedDecisions',[{d:'up', i: (gRedLastDir === 'down' ? 4 : (abs(gRedDelta.dy) > abs(gRedDelta.dx) ? (gRedDelta.dy<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gRedDelta.dy<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'down', i:(gRedLastDir === 'up' ? 4 : (abs(gRedDelta.dy) > abs(gRedDelta.dx) ? (gRedDelta.dy<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gRedDelta.dy<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2))))}, {d:'left', i: (gRedLastDir === 'right' ? 4 : (abs(gRedDelta.dy) < abs(gRedDelta.dx) ? (gRedDelta.dx<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gRedDelta.dx<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'right', i:(gRedLastDir === 'left' ? 4 : abs(gRedDelta.dy) < abs(gRedDelta.dx) ? (gRedDelta.dx<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gRedDelta.dx<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2)))}])"
    },
    {
      "name": "gRedHasWallX",
      "update": "(gRedOffsetX<0 && !gRedCanLeft) || (gRedOffsetX>0 && !gRedCanRight) ? true : false"
    },
    {
      "name": "gRedHasWallY",
      "update": "(gRedOffsetY<0 && !gRedCanUp) || (gRedOffsetY>0 && !gRedCanDown) ? true : false"
    },
    {
      "name": "gRedCanLeft",
      "update": "indata('walls', 'key', gRedX + '-' + gRedY + '-true') ? false : true"
    },
    {
      "name": "gRedCanUp",
      "update": "indata('walls', 'key', gRedX + '-' + gRedY + '-false')? false : true"
    },
    {
      "name": "gRedCanRight",
      "update": "indata('walls', 'key', (gRedX + 1) + '-' + gRedY + '-true') ? false : true"
    },
    {
      "name": "gRedCanDown",
      "update": "indata('walls', 'key', gRedX + '-' + (gRedY+1) + '-false')? false : true"
    },
    {
      "name": "gRedHasPacMan",
      "update": "superPower && ((gRedX + gRedOffsetX === pacManX) && (gRedY + gRedOffsetY === pacManY))"
    },
    {
      "name": "gRedBlocked",
      "on": [
        {
          "events": {"signal": "gRedProposedDirection"}, "force": true, "update": "!gRedProposedDirection ||(gRedProposedDirection === 'up' && !gRedCanUp) || (gRedProposedDirection === 'down' && !gRedCanDown) || (gRedProposedDirection === 'left' && !gRedCanLeft) || (gRedProposedDirection === 'right' && !gRedCanRight) ? true : false"
        }
      ]
    },
    {
      "name": "gRedTry",
      "on": [
        {
          "events": {"type": "timer", "throttle": 400}, "update": "gRedBlocked && gRedTry <3 ? gRedTry+1 : 0"
        }
      ]
    },
    {
      "name": "gRedDecision",
      "update": "data('gRedDecisions')[gRedTry]"
    },
    {
      "name": "gRedProposedDirection",
      "on": [{"events": {"signal": "gRedDecision"}, "force": true, "update": "gRedDecision ? gRedDecision.d : 'none'"}]
    },
    {
      "name": "gRedDirection",
      "update": "gRedProposedDirection && !gRedBlocked ? gRedProposedDirection : gRedDirection"
    },
    {
      "name": "gRedOffsetX",
      "update": "gRedDirection === 'left'? - 1 : gRedDirection === 'right'? 1 : 0"

    },
    {
      "name": "gRedOffsetY",
      "update": "gRedDirection === 'up'? -1 : gRedDirection === 'down' ? 1 : 0"
    },
    {
      "name": "gRedX",
      "init": "gRed.x",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'red') ? 6 : (!restart && !gRedHasWallX && !gRedHasPacMan ? ((gRedOffsetX <0 && gRedX <= 0) ? grid.width - 1 : ((gRedOffsetX > 0 && gRedX >= grid.width -1) ? 0 : (gRedX + gRedOffsetX))) : gRedX)"
        },
        {
          "events": {"signal": "restart"}, "update": "gRed.x", "force": true
        }
      ]
    },
    {
      "name": "gRedY",
      "init": "gRed.y",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'red') ? 7 : (!restart && !gRedHasWallY && !gRedHasPacMan ? ((gRedOffsetY <0 && gRedY <= 0) ? grid.height-1 : (gRedOffsetY > 0 && gRedY >= grid.height - 1)? 0 : gRedY + gRedOffsetY) : gRedY)"
        },
        {
          "events": {"signal": "restart"}, "force":true, "update": "gRed.y"
        }
      ]
    },

    {
      "name": "gBlue",
      "init": "data('ghosts')[1]"
    },
    {
      "name": "gBlueDelta",
      "update": "{dx:pacManX-gBlueX, dy: pacManY-gBlueY}"
    },
    {
      "name": "gBlueLastDir",
      "update": "gBlueDirection"
    },
    {
      "name": "gBluePreferences",
      "update": "setdata('gBlueDecisions',[{d:'up', i: (gBlueLastDir === 'down' ? 4 : (abs(gBlueDelta.dy) > abs(gBlueDelta.dx) ? (gBlueDelta.dy<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gBlueDelta.dy<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'down', i:(gBlueLastDir === 'up' ? 4 : (abs(gBlueDelta.dy) > abs(gBlueDelta.dx) ? (gBlueDelta.dy<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gBlueDelta.dy<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2))))}, {d:'left', i: (gBlueLastDir === 'right' ? 4 : (abs(gBlueDelta.dy) < abs(gBlueDelta.dx) ? (gBlueDelta.dx<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gBlueDelta.dx<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'right', i:(gBlueLastDir === 'left' ? 4 : abs(gBlueDelta.dy) < abs(gBlueDelta.dx) ? (gBlueDelta.dx<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gBlueDelta.dx<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2)))}])"
    },
    {
      "name": "gBlueHasWallX",
      "update": "(gBlueOffsetX<0 && !gBlueCanLeft) || (gBlueOffsetX>0 && !gBlueCanRight) ? true : false"
    },
    {
      "name": "gBlueHasWallY",
      "update": "(gBlueOffsetY<0 && !gBlueCanUp) || (gBlueOffsetY>0 && !gBlueCanDown) ? true : false"
    },
    {
      "name": "gBlueHasGhost",
      "update": "(gBlueX == gRedX && gBlueY === gRedY)"
    },
    {
      "name": "gBlueHasPacMan",
      "update": "superPower && ((gBlueX + gBlueOffsetX === pacManX) && (gBlueY + gBlueOffsetY === pacManY)) "
    },
    {
      "name": "gBlueCanLeft",
      "update": "indata('walls', 'key', gBlueX + '-' + gBlueY + '-true') ? false : true"
    },
    {
      "name": "gBlueCanUp",
      "update": "indata('walls', 'key', gBlueX + '-' + gBlueY + '-false')? false : true"
    },
    {
      "name": "gBlueCanRight",
      "update": "indata('walls', 'key', (gBlueX + 1) + '-' + gBlueY + '-true') ? false : true"
    },
    {
      "name": "gBlueCanDown",
      "update": "indata('walls', 'key', gBlueX + '-' + (gBlueY+1) + '-false')? false : true"
    },
    {
      "name": "gBlueBlocked",
      "on": [
        {
          "events": {"signal": "gBlueProposedDirection"}, "force": true, "update": "!gBlueProposedDirection || (gBlueProposedDirection === 'up' && !gBlueCanUp) || (gBlueProposedDirection === 'down' && !gBlueCanDown) || (gBlueProposedDirection === 'left' && !gBlueCanLeft) || (gBlueProposedDirection === 'right' && !gBlueCanRight) ? true : false"
        }
      ]
    },
    {
      "name": "gBlueTry",
      "on": [
        {
          "events": {"type": "timer", "throttle": 400}, "update": "gBlueBlocked && gBlueTry <=3 ? gBlueTry+1 : 0"
        }
      ]
    },
    {
      "name": "gBlueDecision",
      "update": "data('gBlueDecisions')[gBlueTry]"
    },
    {
      "name": "gBlueProposedDirection",
      "on": [{"events": {"signal": "gBlueDecision"}, "force": true, "update": "gBlueDecision ? gBlueDecision.d : 'none'"}]
    },
    {
      "name": "gBlueDirection",
      "update": "gBlueProposedDirection && !gBlueBlocked ? gBlueProposedDirection : gBlueDirection"
    },
    {
      "name": "gBlueOffsetX",
      "update": "gBlueDirection === 'left'? - 1 : gBlueDirection === 'right'? 1 : 0"

    },
    {
      "name": "gBlueOffsetY",
      "update": "gBlueDirection === 'up' ? -1 : gBlueDirection === 'down' ? 1 : 0"
    },
    {
      "name": "gBlueX",
      "init": "gBlue.x",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'steelblue') ? 7 : (!restart && !gBlueHasWallX && !gBlueHasGhost && !gBlueHasPacMan ? ((gBlueOffsetX <0 && gBlueX <= 0) ? grid.width - 1 : ((gBlueOffsetX > 0 && gBlueX >= grid.width -1) ? 0 : (gBlueX + gBlueOffsetX))) : gBlueX)"
        },
        {
          "events": {"signal": "restart"}, "update": "gBlue.x", "force": true
        }
      ]
    },
    {
      "name": "gBlueY",
      "init": "gBlue.y",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'steelblue') ? 7 : (!restart && !gBlueHasWallY && !gBlueHasGhost && !gBlueHasPacMan ? ((gBlueOffsetY <0 && gBlueY <= 0) ? grid.height-1 : (gBlueOffsetY > 0 && gBlueY >= grid.height - 1)? 0 : gBlueY + gBlueOffsetY) : gBlueY)"
        },
        {
          "events": {"signal": "restart"}, "force":true, "update": "gBlue.y"
        }
      ]
    },

    {
      "name": "gGreen",
      "init": "data('ghosts')[2]"
    },
    {
      "name": "gGreenDelta",
      "update": "{dx:pacManX-gGreenX, dy: pacManY-gGreenY}"
    },
    {
      "name": "gGreenLastDir",
      "update": "gGreenDirection"
    },
    {
      "name": "gGreenPreferences",
      "update": "setdata('gGreenDecisions',[{d:'up', i: (gGreenLastDir === 'down' ? 4 : (abs(gGreenDelta.dy) > abs(gGreenDelta.dx) ? (gGreenDelta.dy<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gGreenDelta.dy<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'down', i:(gGreenLastDir === 'up' ? 4 : (abs(gGreenDelta.dy) > abs(gGreenDelta.dx) ? (gGreenDelta.dy<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gGreenDelta.dy<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2))))}, {d:'left', i: (gGreenLastDir === 'right' ? 4 : (abs(gGreenDelta.dy) < abs(gGreenDelta.dx) ? (gGreenDelta.dx<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gGreenDelta.dx<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'right', i:(gGreenLastDir === 'left' ? 4 : abs(gGreenDelta.dy) < abs(gGreenDelta.dx) ? (gGreenDelta.dx<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gGreenDelta.dx<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2)))}])"
    },
    {
      "name": "gGreenHasWallX",
      "update": "(gGreenOffsetX<0 && !gGreenCanLeft) || (gGreenOffsetX>0 && !gGreenCanRight) ? true : false"
    },
    {
      "name": "gGreenHasWallY",
      "update": "(gGreenOffsetY<0 && !gGreenCanUp) || (gGreenOffsetY>0 && !gGreenCanDown) ? true : false"
    },
    {
      "name": "gGreenHasGhost",
      "update": "(gGreenX === gRedX && gGreenY === gRedY) || (gGreenX === gBlueX && gGreenY+gGreenOffsetY === gBlueY)"
    },
    {
      "name": "gGreenHasPacMan",
      "update": "superPower && ((gGreenX + gGreenOffsetX === pacManX) && (gGreenY + gGreenOffsetY === pacManY)) "
    },
    {
      "name": "gGreenCanLeft",
      "update": "indata('walls', 'key', gGreenX + '-' + gGreenY + '-true') ? false : true"
    },
    {
      "name": "gGreenCanUp",
      "update": "indata('walls', 'key', gGreenX + '-' + gGreenY + '-false')? false : true"
    },
    {
      "name": "gGreenCanRight",
      "update": "indata('walls', 'key', (gGreenX + 1) + '-' + gGreenY + '-true') ? false : true"
    },
    {
      "name": "gGreenCanDown",
      "update": "indata('walls', 'key', gGreenX + '-' + (gGreenY+1) + '-false')? false : true"
    },
    {
      "name": "gGreenBlocked",
      "on": [
        {
          "events": {"signal": "gGreenProposedDirection"}, "force": true, "update": "!gGreenProposedDirection || (gGreenProposedDirection === 'up' && !gGreenCanUp) || (gGreenProposedDirection === 'down' && !gGreenCanDown) || (gGreenProposedDirection === 'left' && !gGreenCanLeft) || (gGreenProposedDirection === 'right' && !gGreenCanRight) ? true : false"
        }
      ]
    },
    {
      "name": "gGreenTry",
      "on": [
        {
          "events": {"type": "timer", "throttle": 400}, "update": "gGreenBlocked && gGreenTry <=3 ? gGreenTry+1 : 0"
        }
      ]
    },
    {
      "name": "gGreenDecision",
      "update": "data('gGreenDecisions')[gGreenTry]"
    },
    {
      "name": "gGreenProposedDirection",
      "on": [{"events": {"signal": "gGreenDecision"}, "force": true, "update": "gGreenDecision ? gGreenDecision.d : 'none'"}]
    },
    {
      "name": "gGreenDirection",
      "update": "gGreenProposedDirection && !gGreenBlocked ? gGreenProposedDirection : gGreenDirection"
    },
    {
      "name": "gGreenOffsetX",
      "update": "gGreenDirection === 'left'? - 1 : gGreenDirection === 'right'? 1 : 0"

    },
    {
      "name": "gGreenOffsetY",
      "update": "gGreenDirection === 'up' ? -1 : gGreenDirection === 'down' ? 1 : 0"
    },
    {
      "name": "gGreenX",
      "init": "gGreen.x",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'green') ? 8 : (!restart && !gGreenHasWallX && !gGreenHasGhost && !gGreenHasPacMan ? ((gGreenOffsetX <0 && gGreenX <= 0) ? grid.width - 1 : ((gGreenOffsetX > 0 && gGreenX >= grid.width -1) ? 0 : (gGreenX + gGreenOffsetX))) : gGreenX)"
        },
        {
          "events": {"signal": "restart"}, "update": "gGreen.x", "force": true
        }
      ]
    },
    {
      "name": "gGreenY",
      "init": "gGreen.y",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'green') ? 7 : (!restart && !gGreenHasWallY && !gGreenHasGhost && !gGreenHasPacMan ? ((gGreenOffsetY <0 && gGreenY <= 0) ? grid.height-1 : (gGreenOffsetY > 0 && gGreenY >= grid.height - 1)? 0 : gGreenY + gGreenOffsetY) : gGreenY)"
        },
        {
          "events": {"signal": "restart"}, "force":true, "update": "gGreen.y"
        }
      ]
    },

    {
      "name": "gOrange",
      "init": "data('ghosts')[3]"
    },
    {
      "name": "gOrangeDelta",
      "update": "{dx:pacManX-gOrangeX, dy: pacManY-gOrangeY}"
    },
    {
      "name": "gOrangeLastDir",
      "update": "gOrangeDirection"
    },
    {
      "name": "gOrangePreferences",
      "update": "setdata('gOrangeDecisions',[{d:'up', i: (gOrangeLastDir === 'down' ? 4 : (abs(gOrangeDelta.dy) > abs(gOrangeDelta.dx) ? (gOrangeDelta.dy<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gOrangeDelta.dy<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'down', i:(gOrangeLastDir === 'up' ? 4 : (abs(gOrangeDelta.dy) > abs(gOrangeDelta.dx) ? (gOrangeDelta.dy<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gOrangeDelta.dy<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2))))}, {d:'left', i: (gOrangeLastDir === 'right' ? 4 : (abs(gOrangeDelta.dy) < abs(gOrangeDelta.dx) ? (gOrangeDelta.dx<0 ? (superPower ? 4 : 1) : (superPower ? 1 : 4)) : (gOrangeDelta.dx<0 ? (superPower ? 3 : 2) : (superPower ? 2 : 3))))}, {d:'right', i:(gOrangeLastDir === 'left' ? 4 : abs(gOrangeDelta.dy) < abs(gOrangeDelta.dx) ? (gOrangeDelta.dx<0 ? (superPower ? 1 : 4) : (superPower ? 4 : 1)) : (gOrangeDelta.dx<0 ? (superPower ? 2 : 3) : (superPower ? 3 : 2)))}])"
    },
    {
      "name": "gOrangeHasWallX",
      "update": "(gOrangeOffsetX<0 && !gOrangeCanLeft) || (gOrangeOffsetX>0 && !gOrangeCanRight) ? true : false"
    },
    {
      "name": "gOrangeHasWallY",
      "update": "(gOrangeOffsetY<0 && !gOrangeCanUp) || (gOrangeOffsetY>0 && !gOrangeCanDown) ? true : false"
    },
    {
      "name": "gOrangeHasGhost",
      "update": "(gOrangeX === gRedX && gOrangeY === gRedY) || (gOrangeX === gBlueX && gOrangeY === gBlueY) || (gOrangeX === gGreenX && gOrangeY === gGreenY)"
    },
    {
      "name": "gOrangeCanLeft",
      "update": "indata('walls', 'key', gOrangeX + '-' + gOrangeY + '-true') ? false : true"
    },
    {
      "name": "gOrangeCanUp",
      "update": "indata('walls', 'key', gOrangeX + '-' + gOrangeY + '-false')? false : true"
    },
    {
      "name": "gOrangeCanRight",
      "update": "indata('walls', 'key', (gOrangeX + 1) + '-' + gOrangeY + '-true') ? false : true"
    },
    {
      "name": "gOrangeCanDown",
      "update": "indata('walls', 'key', gOrangeX + '-' + (gOrangeY+1) + '-false')? false : true"
    },
    {
      "name": "gOrangeBlocked",
      "on": [
        {
          "events": {"signal": "gOrangeProposedDirection"}, "force": true, "update": "!gOrangeProposedDirection || (gOrangeProposedDirection === 'up' && !gOrangeCanUp) || (gOrangeProposedDirection === 'down' && !gOrangeCanDown) || (gOrangeProposedDirection === 'left' && !gOrangeCanLeft) || (gOrangeProposedDirection === 'right' && !gOrangeCanRight) ? true : false"
        }
      ]
    },
    {
      "name": "gOrangeTry",
      "on": [
        {
          "events": {"type": "timer", "throttle": 400}, "update": "gOrangeBlocked && gOrangeTry <=3 ? gOrangeTry+1 : 0"
        }
      ]
    },
    {
      "name": "gOrangeHasPacMan",
      "update": "superPower && ((gOrangeX + gOrangeOffsetX === pacManX) && (gOrangeY + gOrangeOffsetY === pacManY)) "
    },
    {
      "name": "gOrangeDecision",
      "update": "data('gOrangeDecisions')[gOrangeTry]"
    },
    {
      "name": "gOrangeProposedDirection",
      "on": [{"events": {"signal": "gOrangeDecision"}, "force": true, "update": "gOrangeDecision ? gOrangeDecision.d : 'none'"}]
    },
    {
      "name": "gOrangeDirection",
      "update": "gOrangeProposedDirection && !gOrangeBlocked ? gOrangeProposedDirection : gOrangeDirection"
    },
    {
      "name": "gOrangeOffsetX",
      "update": "gOrangeDirection === 'left'? - 1 : gOrangeDirection === 'right'? 1 : 0"

    },
    {
      "name": "gOrangeOffsetY",
      "update": "gOrangeDirection === 'up' ? -1 : gOrangeDirection === 'down' ? 1 : 0"
    },
    {
      "name": "gOrangeX",
      "init": "gOrange.x",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'orange') ? 7 : (!restart && !gOrangeHasWallX && !gOrangeHasGhost && !gOrangeHasPacMan ? ((gOrangeOffsetX <0 && gOrangeX <= 0) ? grid.width - 1 : ((gOrangeOffsetX > 0 && gOrangeX >= grid.width -1) ? 0 : (gOrangeX + gOrangeOffsetX))) : gOrangeX)"
        },
        {
          "events": {"signal": "restart"}, "update": "gOrange.x", "force": true
        }
      ]
    },
    {
      "name": "gOrangeY",
      "init": "gOrange.y",
      "on":[
        {
          "events": "timer:500", "update": "indata('eatenGhosts', 'color', 'orange') ? 7 : (!restart && !gOrangeHasWallY && !gOrangeHasGhost && !gOrangeHasPacMan  ? ((gOrangeOffsetY <0 && gOrangeY <= 0) ? grid.height-1 : (gOrangeOffsetY > 0 && gOrangeY >= grid.height - 1)? 0 : gOrangeY + gOrangeOffsetY) : gOrangeY)"
        },
        {
          "events": {"signal": "restart"}, "force":true, "update": "gOrange.y"
        }
      ]
    },

    {
      "name": "pacManX",
      "init": "7",
      "on":[
        {"events": "timer:500", "update": "!restart && canMoveX ? ((xOffset <0 && pacManX <= 0) ? grid.width - 1 : ((xOffset > 0 && pacManX >= grid.width -1) ? 0 : (pacManX + xOffset))) : pacManX"},
        {
          "events": {"signal": "restart"}, "update": "7", "force": true
        }
      ]
     },
    {
      "name": "pacManY",
      "init": "7",
      "on":[
        {"events": "timer:500", "update": "! restart && canMoveY ? ((yOffset <0 && pacManY <= 0) ? grid.height-1 : (yOffset > 0 && pacManY >= grid.height - 1)? 0 : pacManY + yOffset) : pacManY"},
        {
          "events": {"signal": "restart"}, "force":true, "update": "7"
        }
      ]
    },
    {
      "name": "canMoveX",
      "update": "!hasWallX && !hasGhost"
    },
    {
      "name": "canMoveY",
      "update": "!hasWallY && !hasGhost"
    },
    {
      "name": "hasGhost",
      "update": "!superPower && (((pacManX + xOffset === gRedX) && (pacManY + yOffset === gRedY)) || ((pacManX + xOffset === gBlueX) && (pacManY + yOffset === gBlueY)) || ((pacManX + xOffset === gGreenX) && (pacManY + yOffset === gGreenY)) || ((pacManX + xOffset === gOrangeX) && (pacManY + yOffset === gOrangeY)))"
    },
    {
      "name": "hasWallX",
      "update": "(xOffset<=0 && indata('walls', 'key', pacManX + '-' + pacManY + '-true')) || (xOffset>=0 && indata('walls', 'key', (pacManX + 1) + '-' + pacManY + '-true')) ? true : false"
    },
    {
      "name": "hasWallY",
      "update": "(yOffset<=0 && indata('walls', 'key', pacManX + '-' + pacManY + '-false')) || (yOffset>=0 && indata('walls', 'key', pacManX + '-' + (pacManY+1) + '-false')) ? true : false"
    },
    {
      "name": "xOffset",
      "update": "key === 'ArrowRight' ? 1 : key === 'ArrowLeft' ? -1 : 0"
    },
    {
      "name": "yOffset",
      "update": "key === 'ArrowUp' ? -1 : key === 'ArrowDown' ? 1 : 0"
    },
    {
      "name": "key",
      "on":[
        {
          "events": "window:keydown", "update": "event.code"
        }
      ]
    }

  ],
  "scales":[
    {
      "name": "scaleX",
      "type": "band",
      "domain": {"data": "columns", "field": "data"},
      "range":[{"signal": "rangeWidthDelta"},{"signal": "width-rangeWidthDelta"}],
      "padding":0
    },
    {
      "name": "scaleY",
      "type": "band",
      "domain": {"data": "rows", "field": "data"},
      "range":[{"signal": "rangeHeightDelta"},{"signal": "height-rangeHeightDelta"}]
    }
  ],
  "marks":[
    {
      "type": "rect",
      "encode": {
        "enter": {
          "width": {"signal": "width"},
          "height": {"signal": "height"},
          "fill": {"value": "black"}
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "walls"},
      "encode": {
        "enter": {
          "x": {"signal": "datum.x < grid.width ? datum.x : grid.width-1", "scale": "scaleX", "offset": {"signal": "datum.vertical? (datum.x < grid.width ? datum.x === 0 ? 0 : -1 : blockSize - 2):0"}},
          "y": {"signal": "datum.y < grid.height ? datum.y : grid.height-1", "scale": "scaleY", "offset": {"signal": "datum.vertical? 0: datum.y < grid.height? datum.y === 0 ? 0 : -1 : blockSize -2"}},
          "fill": {"value": "blue"},
          "width": {"signal": "datum.vertical?blockSize/10:blockSize"},
          "height": {"signal": "datum.vertical?blockSize:blockSize/10"}

        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value":7, "scale": "scaleX", "offset": {"signal": "blockSize/2"}},
          "y": {"value":3, "scale": "scaleY", "offset": {"signal": "blockSize/2"}},
          "fill": {"value": "yellow"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "fontSize": {"value":14}
        },
        "update": {
          "text": {"value": "score"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value":7, "scale": "scaleX", "offset": {"signal": "blockSize/2"}},
          "y": {"value":4, "scale": "scaleY"},
          "fill": {"value": "yellow"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "fontSize": {"value":25}
        },
        "update": {
          "text": {"signal": "score"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value":5, "scale": "scaleX", "offset": {"signal": "blockSize/2"}},
          "y": {"value":11, "scale": "scaleY", "offset": {"signal": "blockSize/2"}},
          "fill": {"value": "lightblue"},
          "align": {"value": "left"},
          "baseline": {"value": "middle"},
          "fontSize": {"value":14}
        },
        "update": {
          "opacity": {"signal": "hiScore?1:0"},
          "text": {"value": "high score"}
        }
      }
    },
    {
      "type": "text",
      "encode": {
        "enter": {
          "x": {"value":8, "scale": "scaleX", "offset": {"signal": "blockSize/2"}},
          "y": {"value":11, "scale": "scaleY","offset": {"signal": "blockSize/2"}},
          "fill": {"value": "lightblue"},
          "align": {"value": "center"},
          "baseline": {"value": "middle"},
          "fontSize": {"value":20}
        },
        "update": {
          "opacity": {"signal": "hiScore?1:0"},
          "text": {"signal": "hiScore"}
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "gums"},
      "encode": {
        "enter": {
          "x": {"field": "x", "scale": "scaleX", "offset": {"signal": "blockSize/2"}},
          "y": {"field": "y", "scale": "scaleY", "offset": {"signal": "blockSize/2"}},
          "fill": {"value": "grey"}
        },
        "update": {
          "opacity":[{"test": "indata('eatenGums', 'key', datum.x+'-'+datum.y)", "value": 0}, {"value":1}]
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "powerGums"},
      "encode": {
        "enter": {
          "x": {"field": "x", "scale": "scaleX", "offset": {"signal": "blockSize/2"}},
          "y": {"field": "y", "scale": "scaleY", "offset": {"signal": "blockSize/2"}},
          "size": {"value":200},
          "fill": {"value": "white"}
        },
        "update": {
          "opacity":[{"test": "indata('eatenGums', 'key', datum.x+'-'+datum.y)", "value": 0}, {"value":1}]
        }
      }
    },
    {
      "type": "group",
      "name": "pacMan",
      "encode": {
        "update": {
          "x": {"signal": "pacManX", "scale": "scaleX"},
          "y": {"signal": "pacManY", "scale": "scaleY"},
          "width": {"scale": "scaleX", "band":true},
          "height": {"scale": "scaleY", "band":true}
        }
      },
      "marks":[
        {
          "type": "arc",
          "encode": {
            "enter": {
              "outerRadius": {"signal": "(blockSize/2)*0.8"},
              "stroke": {"value": "black"},
              "x": {"signal": "blockSize/2"},
              "y": {"signal": "blockSize/2"}

            },
            "update": {
              "fill": {"signal": "superPower ? 'red': 'yellow'"},
              "endAngle": {"signal": "(pacManIsOpen? 5*PI/2-PI/6:5*PI/2-0.001)*(xOffset === 0 ? 1: xOffset)"},
              "startAngle": {"signal": "(pacManIsOpen? PI/2+PI/6:PI/2)*(xOffset === 0 ? 1: xOffset)"}
            }
          }
        },
        {
          "type": "symbol",
          "encode": {
            "enter": {
              "y": {"signal": "blockSize/2", "offset": {"signal": "-blockSize/5"}},
              "size": {"signal": "pow(blockSize/10, 2)"},
              "fill": {"value": "black"}
              },
            "update": {
              "x": {"signal": "blockSize/2", "offset": {"signal": "2*(xOffset === 0 ? 1: xOffset)"}}
            }
          }
        }
      ]
     },
    {
      "type": "group",
      "from": {"data": "ghosts"},
      "name": "ghost",
      "encode": {
        "update": {
          "x": {"signal": "datum.color === 'red' ? gRedX : datum.color === 'steelblue'? gBlueX : datum.color === 'green'? gGreenX : gOrangeX", "scale": "scaleX"},
          "y": {"signal": "datum.color === 'red' ? gRedY : datum.color === 'steelblue'? gBlueY : datum.color === 'green'? gGreenY : gOrangeY", "scale": "scaleY"},
          "width": {"scale": "scaleX", "band":true},
          "height": {"scale": "scaleY", "band":true}
        }
      },
      "marks":[
        {
          "type": "symbol",
          "encode": {
            "enter": {
              "size": {"signal": "pow(blockSize/20,2)"},
              "x": {"signal": "blockSize/8"},
              "y": {"signal": "blockSize/9"}
            },
            "update": {
              "shape": {"signal": "indata('eatenGhosts', 'color', parent.color) ? 'M16.459004,11.555034C15.659003,11.555034 15.009003,12.205034 15.009003,13.005036 15.009003,13.804038 15.659003,14.454039 16.459004,14.454039 17.258005,14.454039 17.909006,13.804038 17.909006,13.005036 17.909006,12.205034 17.258005,11.555034 16.459004,11.555034z M7.5419962,11.555034C6.7429954,11.555034 6.0919949,12.205034 6.0919949,13.005036 6.0919949,13.804038 6.7429954,14.454039 7.5419962,14.454039 8.3419973,14.454039 8.9919979,13.804038 8.9919979,13.005036 8.9919979,12.205034 8.3419973,11.555034 7.5419962,11.555034z M16.459004,9.5550298C18.361006,9.5550298 19.909006,11.103033 19.909006,13.005036 19.909006,14.90804 18.361006,16.454042 16.459004,16.454042 14.557002,16.454042 13.009001,14.90804 13.009001,13.005036 13.009001,11.103033 14.557002,9.5550298 16.459004,9.5550298z M7.5419962,9.5550298C9.4439976,9.5550298 10.991999,11.103033 10.991999,13.005036 10.991999,14.90804 9.4439976,16.454042 7.5419962,16.454042 5.6399948,16.454042 4.091993,14.90804 4.091993,13.005036 4.091993,11.103033 5.6399948,9.5550298 7.5419962,9.5550298z M12,1.9999994C6.4860079,1.9999995,2.0000002,6.3300156,2.0000004,11.650998L2.0000004,28.669975 5.102997,27.200983 8.9929964,29.12896 12.742996,27.202997 16.98201,29.106987 19.895004,27.116999 22,28.439995 22,11.650998C22,6.3300156,17.515,1.9999995,12,1.9999994z M12,0C18.617005,0,24,5.2260117,24,11.650998L24,32.056999 19.940003,29.507988 17.184998,31.390007 12.80101,29.421989 9.0080111,31.369011 5.0820009,29.424004 0,31.827995 0,11.650998C1.8747701E-07,5.2260117,5.3840029,0,12,0z' : 'M13.952596,15.068143C13.767538,15.066144 13.583578,15.095151 13.403586,15.157148 12.252587,15.553147 11.725549,17.163162 12.224572,18.753189 12.725547,20.342192 14.062582,21.309212 15.211566,20.914204 16.362564,20.518204 16.889541,18.908188 16.390579,17.318163 15.968584,15.977162 14.95058,15.077146 13.952596,15.068143z M7.7945876,6.1100698C7.2026091,6.0760732 6.4365583,6.7850791 5.9736071,7.8550807 5.4445558,9.0761004 5.5105953,10.302109 6.1215563,10.590106 6.7316013,10.881108 7.65555,10.126112 8.1855779,8.9070922 8.7145686,7.6860881 8.6485896,6.4610711 8.036592,6.1710754 7.9606028,6.1350642 7.8795486,6.1150752 7.7945876,6.1100698z M15.404559,5.9590679C15.383563,5.9580608 15.362566,5.9580608 15.34157,5.960075 14.674579,6.0020671 14.194539,7.1220723 14.275593,8.4590903 14.354573,9.7981063 14.962543,10.848119 15.631547,10.802114 16.300554,10.759113 16.778579,9.6401005 16.700576,8.3020907 16.622573,7.006074 16.049577,5.980064 15.404559,5.9590679z M12.317589,1.4699259E-05C15.527545,0.0050196948 18.757579,1.2870288 21.236579,3.8010436 24.038576,6.6430793 25.533567,12.005127 25.825559,15.861164 26.09155,19.371191 27.844537,19.518194 30.765552,22.228211 31.592515,22.995216 33.904521,25.825243 28.733512,26.053242 26.619564,26.146244 25.60156,25.739243 21.732549,22.850226 21.235542,22.545214 20.664558,22.733219 20.373542,22.885214 20.017526,23.07122 19.741586,23.925232 19.851572,24.215227 20.16456,25.583237 22.25855,25.135235 23.427553,26.313253 24.41156,27.305252 22.795536,29.807287 18.926586,29.29027 18.926586,29.29027 16.343582,28.587277 13.853597,25.258236 11.910547,25.242245 9.6305823,25.258236 9.6305823,25.258236 9.6305823,25.258236 9.6025672,26.705256 9.6425452,27.10626 10.271573,27.256254 10.777553,27.021252 13.298544,27.736271 14.150593,27.978262 16.663589,31.170292 8.7236018,30.424282 7.0135832,30.263287 7.1875944,30.721283 5.2576051,26.025242 4.2626119,23.604229 2.0076115,22.396212 0.6345674,17.082169 -0.27241354,14.207143 -0.21040192,11.068107 0.84159805,8.2280856 0.97556992,7.8450862 1.1235799,7.5130826 1.2786091,7.1980773 1.8406196,6.0020671 2.5815849,4.8720523 3.5156043,3.863056 5.9166007,1.2680314 9.107573,-0.0049901602 12.317589,1.4699259E-05z'"},
              "fill": {"signal": "superPower? 'white' :parent.color"},
              "opacity": {"signal": "superPower?0.7:1"}
            }
          }
        }
      ]
    }
  ],
  "data":[
        {
      "name": "columns",
      "transform": [
        {
          "type": "sequence",
          "start": 0,
          "stop": { "signal": "grid.width" },
          "step": 1
        }
      ]
    },
    {
      "name": "rows",
      "transform": [
        {
          "type": "sequence",
          "start": 0,
          "stop": { "signal": "grid.height" },
          "step": 1
        }
      ]
    },
    {
      "name": "ghosts",
      "values":[
      {
        "x":0,
        "y":0,
        "color": "red"
      },
      {
        "x":14,
        "y":0,
        "color": "steelblue"
      },
       {
        "x":0,
        "y":14,
        "color": "green"
      },
      {
        "x":14,
        "y":14,
        "color": "orange"
      }
      ]
    },
    {
      "name": "eatenGhosts",
      "on": [{
        "trigger": "testEatenGhost",
        "insert": "testEatenGhost"
      },
      {
        "trigger": "!superPower",
        "remove": "!superPower"
      }]
    },
    {
      "name": "gRedDecisions",
      "values":[],
      "transform":[
        {"type": "collect",
          "sort": {"field": "i"}
        }
      ]
    },
    {
      "name": "gBlueDecisions",
      "values":[],
      "transform":[
        {"type": "collect",
         "sort": {"field": "i"}
        }
      ]
    },
    {
      "name": "gGreenDecisions",
      "values":[],
      "transform":[
        {"type": "collect",
          "sort": {"field": "i"}
        }
      ]
    },
    {
      "name": "gOrangeDecisions",
      "values":[],
      "transform":[
        {"type": "collect",
          "sort": {"field": "i"}
        }
      ]
    },
    {
      "name": "gums",
      "values":[
        {"x":0, "y":1},
        {"x":0, "y":2},
        {"x":0, "y":3},
        {"x":0, "y":4},
        {"x":0, "y":7},
        {"x":0, "y":10},
        {"x":0, "y":11},
        {"x":0, "y":12},
        {"x":0, "y":14},

        {"x":1, "y":0},
        {"x":1, "y":2},
        {"x":1, "y":4},
        {"x":1, "y":7},
        {"x":1, "y":10},
        {"x":1, "y":12},
        {"x":1, "y":13},
        {"x":1, "y":14},

        {"x":2, "y":0},
        {"x":2, "y":2},
        {"x":2, "y":3},
        {"x":2, "y":4},
        {"x":2, "y":7},
        {"x":2, "y":10},
        {"x":2, "y":13},
        {"x":2, "y":14},

        {"x":3, "y":0},
        {"x":3, "y":1},
        {"x":3, "y":2},
        {"x":3, "y":4},
        {"x":3, "y":5},
        {"x":3, "y":6},
        {"x":3, "y":7},
        {"x":3, "y":8},
        {"x":3, "y":9},
        {"x":3, "y":10},
        {"x":3, "y":12},
        {"x":3, "y":13},
        {"x":3, "y":14},

        {"x":4, "y":0},
        {"x":4, "y":2},
        {"x":4, "y":6},
        {"x":4, "y":10},
        {"x":4, "y":11},
        {"x":4, "y":12},
        {"x":4, "y":14},

        {"x":5, "y":0},
        {"x":5, "y":2},
        {"x":5, "y":3},
        {"x":5, "y":4},
        {"x":5, "y":5},
        {"x":5, "y":6},
        {"x":5, "y":7},
        {"x":5, "y":8},
        {"x":5, "y":10},
        {"x":5, "y":12},
        {"x":5, "y":14},

        {"x":6, "y":0},
        {"x":6, "y":2},
        {"x":6, "y":5},
        {"x":6, "y":6},
        {"x":6, "y":7},
        {"x":6, "y":8},
        {"x":6, "y":10},
        {"x":6, "y":12},
        {"x":6, "y":13},
        {"x":6, "y":14},

        {"x":7, "y":0},
        {"x":7, "y":1},
        {"x":7, "y":2},
        {"x":7, "y":6},
        {"x":7, "y":8},
        {"x":7, "y":9},
        {"x":7, "y":10},
        {"x":7, "y":14},

        {"x":14, "y":0},
        {"x":14, "y":1},
        {"x":14, "y":2},
        {"x":14, "y":3},
        {"x":14, "y":4},
        {"x":14, "y":7},
        {"x":14, "y":10},
        {"x":14, "y":11},
        {"x":14, "y":12},
        {"x":14, "y":14},

        {"x":13, "y":0},
        {"x":13, "y":2},
        {"x":13, "y":4},
        {"x":13, "y":7},
        {"x":13, "y":10},
        {"x":13, "y":12},
        {"x":13, "y":13},
        {"x":13, "y":14},

        {"x":12, "y":0},
        {"x":12, "y":2},
        {"x":12, "y":3},
        {"x":12, "y":4},
        {"x":12, "y":7},
        {"x":12, "y":10},
        {"x":12, "y":13},
        {"x":12, "y":14},

        {"x":11, "y":0},
        {"x":11, "y":1},
        {"x":11, "y":2},
        {"x":11, "y":4},
        {"x":11, "y":5},
        {"x":11, "y":6},
        {"x":11, "y":7},
        {"x":11, "y":8},
        {"x":11, "y":9},
        {"x":11, "y":10},
        {"x":11, "y":12},
        {"x":11, "y":13},
        {"x":11, "y":14},

        {"x":10, "y":0},
        {"x":10, "y":2},
        {"x":10, "y":6},
        {"x":10, "y":10},
        {"x":10, "y":11},
        {"x":10, "y":12},
        {"x":10, "y":14},

        {"x":9, "y":0},
        {"x":9, "y":2},
        {"x":9, "y":3},
        {"x":9, "y":4},
        {"x":9, "y":5},
        {"x":9, "y":6},
        {"x":9, "y":7},
        {"x":9, "y":8},
        {"x":9, "y":10},
        {"x":9, "y":12},
        {"x":9, "y":14},

        {"x":8, "y":0},
        {"x":8, "y":2},
        {"x":8, "y":5},
        {"x":8, "y":6},
        {"x":8, "y":7},
        {"x":8, "y":8},
        {"x":8, "y":10},
        {"x":8, "y":12},
        {"x":8, "y":13},
        {"x":8, "y":14}

      ],
      "transform":[
        {"type": "formula", "expr": "datum.x+'-'+datum.y", "as": "key"}
      ]
    },
    {
      "name": "powerGums",
      "values":[{"x":0, "y":0},{"x":14, "y":0}, {"x":0, "y":14}, {"x":14, "y":14}],
      "transform":[
        {"type": "formula", "expr": "datum.x+'-'+datum.y", "as": "key"}
      ]
    },
    {
      "name": "eatenGums",
      "on":[
        {
          "trigger": "testEaten",
          "insert": "testEaten"
        },
        {
          "trigger": "testPowerEaten",
          "insert": "testPowerEaten"
        },
        {
          "trigger": "restart",
          "remove": "restart"
        }
      ]
    },
    {
      "name": "walls",
      "values":[
          {"x":0, "y":0, "vertical":false},
          {"x":1, "y":0, "vertical":false},
          {"x":2, "y":0, "vertical":false},
          {"x":3, "y":0, "vertical":false},
          {"x":4, "y":0, "vertical":false},
          {"x":5, "y":0, "vertical":false},
          {"x":6, "y":0, "vertical":false},
          {"x":7, "y":0, "vertical":false},
          {"x":8, "y":0, "vertical":false},
          {"x":9, "y":0, "vertical":false},
          {"x":10, "y":0, "vertical":false},
          {"x":11, "y":0, "vertical":false},
          {"x":12, "y":0, "vertical":false},
          {"x":13, "y":0, "vertical":false},
          {"x":14, "y":0, "vertical":false},
          {"x":0, "y":15, "vertical":false},
          {"x":1, "y":15, "vertical":false},
          {"x":2, "y":15, "vertical":false},
          {"x":3, "y":15, "vertical":false},
          {"x":4, "y":15, "vertical":false},
          {"x":5, "y":15, "vertical":false},
          {"x":6, "y":15, "vertical":false},
          {"x":7, "y":15, "vertical":false},
          {"x":8, "y":15, "vertical":false},
          {"x":9, "y":15, "vertical":false},
          {"x":10, "y":15, "vertical":false},
          {"x":11, "y":15, "vertical":false},
          {"x":12, "y":15, "vertical":false},
          {"x":13, "y":15, "vertical":false},
          {"x":14, "y":15, "vertical":false},

          {"x":0, "y":0, "vertical":true},
          {"x":0, "y":1, "vertical":true},
          {"x":0, "y":2, "vertical":true},
          {"x":0, "y":3, "vertical":true},
          {"x":0, "y":4, "vertical":true},
          {"x":0, "y":5, "vertical":false},
          {"x":1, "y":5, "vertical":false},
          {"x":2, "y":5, "vertical":false},
          {"x":0, "y":7, "vertical":false},
          {"x":1, "y":7, "vertical":false},
          {"x":2, "y":7, "vertical":false},
          {"x":0, "y":8, "vertical":false},
          {"x":1, "y":8, "vertical":false},
          {"x":0, "y":10, "vertical":false},
          {"x":1, "y":10, "vertical":false},
          {"x":2, "y":10, "vertical":false},
          {"x":2, "y":8, "vertical":false},
          {"x":3, "y":5, "vertical":true},
          {"x":3, "y":6, "vertical":true},
          {"x":3, "y":8, "vertical":true},
          {"x":3, "y":9, "vertical":true},
          {"x":0, "y":10, "vertical":true},
          {"x":0, "y":11, "vertical":true},
          {"x":0, "y":12, "vertical":true},
          {"x":0, "y":13, "vertical":true},
          {"x":0, "y":14, "vertical":true},

          {"x":15, "y":0, "vertical":true},
          {"x":15, "y":1, "vertical":true},
          {"x":15, "y":2, "vertical":true},
          {"x":15, "y":3, "vertical":true},
          {"x":15, "y":4, "vertical":true},
          {"x":12, "y":5, "vertical":true},
          {"x":14, "y":5, "vertical":false},
          {"x":13, "y":5, "vertical":false},
          {"x":14, "y":7, "vertical":false},
          {"x":13, "y":7, "vertical":false},
          {"x":12, "y":7, "vertical":false},
          {"x":12, "y":5, "vertical":false},
          {"x":12, "y":6, "vertical":true},
          {"x":14, "y":8, "vertical":false},
          {"x":13, "y":8, "vertical":false},
          {"x":14, "y":10, "vertical":false},
          {"x":13, "y":10, "vertical":false},
          {"x":12, "y":10, "vertical":false},
          {"x":12, "y":8, "vertical":false},
          {"x":12, "y":8, "vertical":true},
          {"x":12, "y":9, "vertical":true},
          {"x":15, "y":10, "vertical":true},
          {"x":15, "y":11, "vertical":true},
          {"x":15, "y":12, "vertical":true},
          {"x":15, "y":13, "vertical":true},
          {"x":15, "y":14, "vertical":true},

          {"x":6, "y":5, "vertical":false},
          {"x":7, "y":5, "vertical":true},
          {"x":7, "y":6, "vertical":false},
          {"x":8, "y":5, "vertical":true},
          {"x":8, "y":5, "vertical":false},
          {"x":7, "y":5, "vertical":true},
          {"x":7, "y":3, "vertical":false},
          {"x":8, "y":3, "vertical":false},
          {"x":6, "y":3, "vertical":false},
          {"x":6, "y":4, "vertical":true},
          {"x":9, "y":3, "vertical":true},
          {"x":9, "y":4, "vertical":true},
          {"x":6, "y":3, "vertical":true},

          {"x":3, "y":3, "vertical":false},
          {"x":4, "y":3, "vertical":false},
          {"x":3, "y":3, "vertical":true},
          {"x":5, "y":3, "vertical":true},
          {"x":3, "y":4, "vertical":false},
          {"x":4, "y":4, "vertical":true},
          {"x":5, "y":4, "vertical":true},
          {"x":4, "y":5, "vertical":true},
          {"x":5, "y":5, "vertical":true},
          {"x":4, "y":6, "vertical":false},

          {"x":10, "y":3, "vertical":false},
          {"x":11, "y":3, "vertical":false},
          {"x":10, "y":3, "vertical":true},
          {"x":12, "y":3, "vertical":true},
          {"x":11, "y":4, "vertical":false},
          {"x":10, "y":4, "vertical":true},
          {"x":11, "y":4, "vertical":true},
          {"x":10, "y":5, "vertical":true},
          {"x":11, "y":5, "vertical":true},
          {"x":10, "y":6, "vertical":false},

          {"x":1, "y":1, "vertical":true},
          {"x":1, "y":1, "vertical":false},
          {"x":2, "y":1, "vertical":false},
          {"x":1, "y":2, "vertical":false},
          {"x":2, "y":2, "vertical":false},
          {"x":3, "y":1, "vertical":true},
          {"x":4, "y":1, "vertical":true},
          {"x":4, "y":1, "vertical":false},
          {"x":4, "y":2, "vertical":false},
          {"x":5, "y":1, "vertical":false},
          {"x":6, "y":1, "vertical":false},
          {"x":5, "y":2, "vertical":false},
          {"x":6, "y":2, "vertical":false},
          {"x":7, "y":1, "vertical":true},
          {"x":8, "y":1, "vertical":true},
          {"x":8, "y":1, "vertical":false},
          {"x":9, "y":1, "vertical":false},
          {"x":8, "y":2, "vertical":false},
          {"x":9, "y":2, "vertical":false},
          {"x":11, "y":1, "vertical":true},
          {"x":10, "y":1, "vertical":false},
          {"x":10, "y":2, "vertical":false},
          {"x":12, "y":1, "vertical":true},
          {"x":12, "y":1, "vertical":false},
          {"x":13, "y":1, "vertical":false},
          {"x":12, "y":2, "vertical":false},
          {"x":13, "y":2, "vertical":false},
          {"x":14, "y":1, "vertical":true},
          {"x":1, "y":3, "vertical":true},
          {"x":2, "y":3, "vertical":true},
          {"x":1, "y":3, "vertical":false},
          {"x":13, "y":4, "vertical":false},
          {"x":13, "y":3, "vertical":true},
          {"x":14, "y":3, "vertical":true},
          {"x":13, "y":3, "vertical":false},
          {"x":1, "y":4, "vertical":false},

          {"x":6, "y":7, "vertical":true},
          {"x":9, "y":7, "vertical":true},
          {"x":7, "y":8, "vertical":false},
          {"x":6, "y":8, "vertical":false},
          {"x":8, "y":8, "vertical":false},

          {"x":4, "y":7, "vertical":false},
          {"x":4, "y":10, "vertical":false},
          {"x":5, "y":10, "vertical":false},
          {"x":6, "y":10, "vertical":false},
          {"x":4, "y":7, "vertical":true},
          {"x":4, "y":8, "vertical":true},
          {"x":4, "y":9, "vertical":true},
          {"x":5, "y":7, "vertical":true},
          {"x":5, "y":8, "vertical":true},
          {"x":7, "y":9, "vertical":true},
          {"x":6, "y":10, "vertical":false},
          {"x":6, "y":9, "vertical":false},
          {"x":5, "y":9, "vertical":false},

          {"x":10, "y":7, "vertical":false},
          {"x":10, "y":10, "vertical":false},
          {"x":9, "y":10, "vertical":false},
          {"x":8, "y":10, "vertical":false},
          {"x":10, "y":7, "vertical":true},
          {"x":10, "y":8, "vertical":true},
          {"x":11, "y":9, "vertical":true},
          {"x":11, "y":7, "vertical":true},
          {"x":11, "y":8, "vertical":true},
          {"x":8, "y":9, "vertical":true},
          {"x":6, "y":10, "vertical":false},
          {"x":9, "y":9, "vertical":false},
          {"x":8, "y":9, "vertical":false},

          {"x":5, "y":11, "vertical":false},
          {"x":6, "y":11, "vertical":false},
          {"x":7, "y":11, "vertical":false},
          {"x":8, "y":11, "vertical":false},
          {"x":9, "y":11, "vertical":false},
          {"x":10, "y":11, "vertical":true},
          {"x":5, "y":11, "vertical":true},
          {"x":5, "y":12, "vertical":false},
          {"x":6, "y":12, "vertical":false},
          {"x":8, "y":12, "vertical":false},
          {"x":9, "y":12, "vertical":false},
          {"x":7, "y":12, "vertical":true},
          {"x":8, "y":12, "vertical":true},
          {"x":7, "y":13, "vertical":true},
          {"x":8, "y":13, "vertical":true},
          {"x":7, "y":14, "vertical":false},

          {"x":1, "y":13, "vertical":true},
          {"x":0, "y":13, "vertical":false},
          {"x":0, "y":14, "vertical":false},

          {"x":2, "y":11, "vertical":false},
          {"x":3, "y":11, "vertical":false},
          {"x":1, "y":12, "vertical":false},
          {"x":1, "y":11, "vertical":false},
          {"x":2, "y":13, "vertical":false},
          {"x":3, "y":12, "vertical":false},
          {"x":4, "y":11, "vertical":true},
          {"x":2, "y":12, "vertical":true},
          {"x":1, "y":11, "vertical":true},
          {"x":3, "y":12, "vertical":true},

          {"x":4, "y":13, "vertical":false},
          {"x":5, "y":13, "vertical":false},
          {"x":4, "y":14, "vertical":false},
          {"x":5, "y":14, "vertical":false},
          {"x":4, "y":13, "vertical":true},
          {"x":6, "y":13, "vertical":true},

          {"x":9, "y":13, "vertical":false},
          {"x":10, "y":13, "vertical":false},
          {"x":9, "y":14, "vertical":false},
          {"x":10, "y":14, "vertical":false},
          {"x":9, "y":13, "vertical":true},
          {"x":11, "y":13, "vertical":true},

          {"x":14, "y":13, "vertical":true},
          {"x":14, "y":13, "vertical":false},
          {"x":14, "y":14, "vertical":false},

          {"x":11, "y":11, "vertical":false},
          {"x":13, "y":11, "vertical":false},
          {"x":11, "y":12, "vertical":false},
          {"x":12, "y":11, "vertical":false},
          {"x":12, "y":13, "vertical":false},
          {"x":13, "y":12, "vertical":false},
          {"x":14, "y":11, "vertical":true},
          {"x":12, "y":12, "vertical":true},
          {"x":11, "y":11, "vertical":true},
          {"x":13, "y":12, "vertical":true}
      ],
      "transform":[
        {
          "type": "formula",
          "expr": "datum.x+'-'+datum.y+'-'+datum.vertical",
          "as": "key"
        }
      ]
    }
  ]
}