Skip to content

Snake Example ​

Github Link (npm i && npm run dev to run, hosted demo coming soon)

The Snake example implements a game, using the useTick() hook. There are three actions:

  • newGame resets the state of the game.
  • turn is called whenever a player wants to turn the snake.
  • tick is called automatically by a shared timer, and runs the main game loop, checking for collisions, moving the snake, and updating the score.
ts
export const maxX = 30
export const maxY = 30

const models = {
  state: {
    key: "primary", // "0" since this is a singleton
    direction: "string",
    tickCount: "integer",
    tiles: "string",
    gameOver: "string",
  },
}

const actions = {
  newGame: (db) => {
    const centerX = Math.floor(maxX / 2)
    const centerY = Math.floor(maxY / 2)
    const tiles = JSON.stringify([
      [centerX, centerY - 1],
      [centerX, centerY],
      [centerX, centerY + 1],
    ])
    db.set("state", {
      key: "0",
      direction: "n",
      tickCount: 0,
      tiles,
      gameOver: "",
    })
  },
  turn: async (db, { direction }) => {
    if (["n", "e", "w", "s"].indexOf(direction) === -1) {
      throw new Error()
    }
    const { direction: currentDirection, tickCount, tiles, gameOver } = await db.state.get("0")
    if (
      (direction === "n" && currentDirection === "s") ||
      (direction === "s" && currentDirection === "n") ||
      (direction === "e" && currentDirection === "w") ||
      (direction === "w" && currentDirection === "e")
    ) {
      throw new Error()
    }
    await db.set("state", { key: "0", direction, tickCount, tiles, gameOver })
  },
  tick: async (db) => {
    const { direction, tickCount, tiles, gameOver } = await db.get("state", "0")
    if (gameOver) throw new Error()

    const tilesList = JSON.parse(tiles)
    const [headX, headY] = tilesList[tilesList.length - 1]

    let next
    if (direction === "n") {
      next = [headX, headY + 1]
    } else if (direction === "e") {
      next = [headX + 1, headY]
    } else if (direction === "s") {
      next = [headX, headY - 1]
    } else if (direction === "w") {
      next = [headX - 1, headY]
    }

    if (
      next[0] < 0 ||
      next[0] > maxX ||
      next[1] < 0 ||
      next[1] > maxY ||
      tilesList.some(([tx, ty]) => tx === next[0] && ty === next[1])
    ) {
      await db.set("state", { key: "0", gameOver: "true", direction, tickCount, tiles })
      return
    }

    tilesList.push(next)
    if (tickCount % 5 !== 0) {
      tilesList.shift()
    }

    await db.set("state", {
      key: "0",
      tickCount: tickCount + 1,
      tiles: JSON.stringify(tilesList),
      direction,
      gameOver,
    })
  },
}