Andrea Horvath

Software Engineer, Problem Solver


snakesy

This little minigame was made in one week for Bitsy Jam #88 (theme: snakes). I saw the theme and immediately thought: what if I remade the classic Snake game in Bitsy? Is that even possible?

Bitsy is primarily designed for small narrative adventures, so recreating a real-time arcade game meant building several systems that the engine doesn't normally support.

Core Features

  • Recreated classic Snake gameplay in Bitsy
  • Implemented dynamic food spawning and snake growth systems
  • Manipulated Bitsy tile data at runtime to simulate moving entities

Food Spawning

Another feature of classic Snake is that a food item will spawn in empty space, and will continue to respawn somewhere else after the snake eats it.

I represented the game board as a 16×16 array where occupied tiles (walls and snake segments) were marked as true and empty tiles as false.

My first approach was to repeatedly select random tiles until an empty one was found. This worked early on, but became increasingly inefficient as the snake grew. When most of the board was occupied, the code might need dozens or even hundreds of attempts before finding a valid location for the next food item. Instead of repeatedly generating new random coordinates, I generated a starting position and then searched through the grid until I found an empty tile. This guaranteed that a valid position would eventually be found while avoiding the performance issues caused by repeatedly rerolling random locations.

function moveFood() {
    foodX = (foodX + 1) % 15;
    if (foodX === 1) {
        foodY = (foodY + 1) % 15;
    }
    if (foodX === initFoodX && foodY === initFoodY) {
        collides = false;
        return true;
    }
    return false;
}

do {
    if (playerX === foodX && playerY === foodY) {
        collides = true;
        tilemapFull = moveFood();
    }
    else if (tilemap[foodX][foodY]) {
        collides = true;
        tilemapFull = moveFood();
    }
    else {
        collides = false;
    }

} while (collides)

This solution is much faster than randomly selecting a tile until an empty one is found!

Snake Body

A tricky issue that quickly became apparent is that every time the player ate food, the body would grow slightly larger, until the game became too difficult to navigate. Bitsy has built-in support to move an avatar sprite around the map, but nothing for moving a collection of tiles like a snake body.

What I ended up doing was storing a list of bodyTiles, so that I could track the id and each position of the tiles in the snake body. Every time the user moved, a new body sprite would be placed in the head's previous position, and the direction it faced would be determined by user input data. The snake body effectively behaves like a queue: new segments are added at the front while old segments are removed from the back. This allowed the body to appear as though it was moving across the map.

var bodyTiles = [];
var updateTilemap = (itemsList, tileX, tileY) => {
    var newTile = undefined
    if (prevDir === currentDir) {
        newTile = { id: BODY_TILES[currentDir], x: tileX, y: tileY }
    }
    else {
        if ((prevDir === "right" && currentDir === "up") || (prevDir === "down" && currentDir === "left")) {
            newTile = { id: TILES.cornerBottomRight, x: tileX, y: tileY }
        }
        else if ((prevDir === "right" && currentDir === "down") || (prevDir === "up" && currentDir === "left")) {
            newTile = { id: TILES.cornerTopRight, x: tileX, y: tileY }
        }
        else if ((prevDir === "left" && currentDir === "up") || (prevDir === "down" && currentDir === "right")) {
            newTile = { id: TILES.cornerBottomLeft, x: tileX, y: tileY }
        }
        else if ((prevDir === "left" && currentDir === "down") || (prevDir === "up" && currentDir === "right")) {
            newTile = { id: TILES.cornerTopLeft, x: tileX, y: tileY }
        }
    }

    if (newTile) {
        itemsList.push(newTile)
        bodyTiles.unshift(newTile);
        tilemap[newTile.x][newTile.y] = true;
    }
}

After this, I simply would pop the last tile in bodyTiles to move the tail's position. Otherwise, the snake would grow without eating any food every time it moved!

Collisions

In classic Snake, colliding with your own snake body results in a game over. In some versions, as well, colliding with the surrounding walls also results in a game over. I wanted both of these features in my Bitsy version, but I needed a way to detect collisions between the player, the snake body, and the walls.

Fortunately, collision handling was much simpler than the food spawning and body movement systems. Since Bitsy already supports triggering events when the player interacts with items, I could represent walls and snake body segments as item tiles and attach a game-over ending to them through the editor. Once those tiles were being updated correctly, collision detection came almost for free.

Closing Thoughts

Despite the challenges of working within Bitsy's limitations, this ended up being a surprisingly straightforward project to build. Rather than managing a large game system, most of the work involved configuring the Bitsy editor and manipulating tile data through code. Snakesy reinforced how flexible Bitsy can be when treated as a framework rather than a traditional game engine. Building systems like dynamic food spawning, collision detection, and a growing snake body required directly manipulating tile data and thinking creatively about how to represent game state.

The project also showed me how much can be accomplished with a relatively small amount of code when the underlying systems are designed carefully. Despite being a simple game, recreating Snake in Bitsy was a fun exercise in adapting a familiar mechanic to an engine that was never designed for it.