Puzzle Game: Snek!
This is a puzzle where you have to remove all the arrows from the game board, one by one. Click arrows that have a clear straight path to the edge and are not blocked by other arrows. The puzzle is guaranteed to be solvable, there is always at least one arrow that can be removed. You can make as many errors as you like, there is no time limit or lose condition.
Settings¶
Why?¶
I really like this puzzle game on mobile and started wondering how level generation might work. Generating solvable and interesting puzzles is actually quite a challenge. I wanted to test my theory and just started coding, one thing led to another, and there it is: a pure vanilla JavaScript puzzle game written entirely by hand (no AI) and from scratch (no framework).
The goal here was not to write the perfect game, but to experiment and learn something. It's nice to work on small and useless projects once in a while. Enjoy :)
How are levels generated?¶
Stop reading and think for a moment, how would you tackle this challenge? How to fill this grid with arrows so that the game is both solvable and fun? Take a guess, then read on :)
The core idea is this: Let the level generator build the puzzle in the same order in which a player would later be able to solve it. For each new arrow, we choose a random free cell and direction, and then check the escape route. We know that all existing arrows can be removed before the current one, so we can safely cross occupied cells. But what about empty cells? The minimum arrow length is 2, so isolated empty cells surrounded by arrows are also safe to travel through. If we hit an empty cell with empty neighbor, then we have to be careful. A later arrow may be placed there and block our current arrow, creating a deadlock. We could mark this cell as reserved to prevent a future deadlock, but that would result in a game with lots of empty space. Instead, let's just scrap the current arrow and find a new spot.
Once we found a valid position and direction for a new arrow, we extend it backwards into neighboring free cells using a random walk. We can tweak the probability for bends or endings to control the visual appearance and difficulty of the game. Short arrows and lots of bends make the game harder, and sometimes more annoying.
This will give us a solvable game, but not necessarily an interesting game. Let's add a heuristic for how fun an arrow is, and for each step, generate a bunch of random valid arrows and choose the one that is the most fun.
The current heuristic favours longer arrows with bends (both tunable), but also rewards challenging arrows. Here is the idea: If you are stuck as a player, it's a common tactic to choose a random arrow that annoys you, check which arrow blocks it, and track those arrows until you find one that can be removed. You then backtrack and remove the previous arrow, if possible, or continue your search. This is what makes the game challenging in a good way. An interesting game requires lots of tracking and backtracking.
To calculate the fun score for a possible new arrow, we analyse its escape path. All arrows that block our candidate are tracked and counted as direct dependencies. Those blocking arrows may also have dependencies themselves, so we can think of this as a dependency tree. All arrows in this tree have to be removed before the current arrow can be removed.
A large dependency tree prevents the player from removing the arrow too early. A deep tree means that they have to follow more arrows before they find an exit, making solutions less obvious and the chase more fun. A wide tree means that they cannot immediately remove the previous arrow and have to backtrack more often, as there are more arrows in the way. A balance of those three metrics makes the game more challenging, especially with large game boards.
You made it to the end, you unlocked a special noodle mode! Want to know all the details? Here is the code :)