Independent-learning super-curriculum projects: reading, research and ideas shared by Perse students

NumberBlocks : designing a numbers puzzle game

I decided to make a puzzle in the Japanese style (simple rules, no language requirements) and I came up with the below. I then wrote a program to solve these puzzles and also generate new ones.

The rules

The grid given has numbers and some boundaries between them. The goal is to add more boundaries to enclose blocks of numbers, such that each block contains the numbers from 1 to the size of the block: e.g. a size-1 block contains the number 1, a size-2 block contains the numbers 1 and 2, etc. Blocks may be of any size or shape.

Example puzzle The solution

Writing the program

Despite the simplicity of the rules, the code was not so simple. Once I had a function to draw the grid, the next step was to make a program that creates solved grids of a given size. The program creates an empty grid, then chooses cells at random in the grid. For each cell, it grows that cell in random directions until it decides the block is big enough; it then puts borders around the block and populates it with numbers. It repeats until the entire grid is full.

The next function is the solve function. This is necessary in order to be certain that a given grid has a unique solution, which will be needed later. The solve function looks at each cell in turn, and determines the blocks that that cell may be in. It checks that each block is valid – it must be continuously connected and contain the right numbers. The solve function then branches off for each possible block the cell can be in, or terminates if there are no possible blocks. When all the cells are assigned a block, the function returns the possible solved states.

Finally, the last function sets a puzzle. It first generates a grid, then looks at each boundary in turn. If removing the boundary results in a grid with one unique solution (this is where the solve function is needed), then the function removes that boundary. Otherwise, it moves on. Once all the boundaries have been looked at, the puzzle is drawn.

Using the program

The program takes about 20 seconds to create a 5×5 puzzle (default), a few minutes for a 7×7, and a while longer for anything larger.

At the bottom of the code is the line draw(bruteSet(5,5,”fiendish”))​. This is where the puzzle is made. The first 5 defines the height, and the second the width. “Fiendish” is the difficulty – replacing this with a number will have the program try to remove that many boundaries – a low number is an easy puzzle, and fiendish is the hardest.

​First look at the 4 in the bottom-right. It is boxed in on all sides apart from up, so must be connected to the 3 above it. They must also be connected to the 5 by the same logic.

That same block, since it contains a five, must be of at least size 5. Therefore it needs more cells – specifically a 1 and a 2. The block cannot contain any other cells than are highlighted in red, because it cannot contain two 5s or two 2s. It also may not contain the boldface 2, because then the 2 to its right would be boxed in and unable to connect and become a legal cell. Thus we get a size-5 block (including the only 1 available).

Now consider the 5 in red. It is boxed in on all sides except down, so must extend down. It must also be in a block with a 4; as there is only one unassigned 4, they must share a block. That block must also contain a 3; there are two of these left, but reaching the one on the top row would require absorbing both the violet 1s – this is impossible, so the block must contain the 3 on the bottom row.

The block now needs only a 1 – there are two of these available (in red) but absorbing the one on the bottom row would box in the violet 2, making it unable to expand into a valid block. Therefore the red 1 must be absorbed, finishing the block. The violet 2 now also expands to the 1 on the bottom row, completing a second block.

There are only 2 remaining blocks to create. The red 1 is boxed in, unable to expand to the 1 above it because no valid block contains two 1s. Therefore the 1 is on its own. The remaining three numbers form a valid block with each other.