knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "man/figures/README-", out.width = "100%" )
r badger::badge_devel("hrj21/nonogram", "blue")
r badger::badge_lifecycle("stable")
R package for solving black and white nonogram puzzles.
You can install the nonogram package from GitHub with:
# install.packages("devtools") devtools::install_github("hrj21/nonogram")
The package defines an S3 class nonogram
used for representing nonogram puzzles. The nonogram()
constructor function allows you to define a nonogram by hand. The rows
and columns
arguments should be lists of numeric vectors representing the patterns in each row and column (starting with the top row and leftmost column).
library(nonogram) non_3x3 <- nonogram( rows = list(c(1, 1), 1, 3), columns = list(c(1, 1), 2, c(1, 1)) ) non_3x3
There are currently two algorithms implemented for solving nonograms: a brute force algorithm, and a permutation elimination algorithm. The brute force algorithm calculates the porportion of filled squares in the nonogram, and keeps proposing random matrices of 0s and 1s until the row and column patterns of a proposal match those of the nonogram in question.
To solve our nono_3x3 nonogram using the brute force algorithm, we simply call the solve()
method, specifying algorithm = "force"
. You will need to set the max_iter
argument higher than the default (10).
non_3x3 <- solve(non_3x3, algorithm = "force", max_iter = Inf) # ?solve.nonogram
You can plot the solved nongram by passing it to the plot()
method.
You can also embed plots, for example:
plot(non_3x3) # ?plot.nonogram
As the number of possible solutions is 2^(n x m), the brute force algorithm is rarely useful beyond very small nonograms. Technically the number of solutions is smaller than 2^(n x m) when we constrain the proposal matrix to have the same proportion of 1s as the nonogram to be solved, but still the number of possible solutions becomes quickly intractable with even moderately-sized nonograms.
The other algorithm is the permutation elimination algorithm, which proceeds as follows:
Unless you have a specific nonogram you wish to solve or create, defining nonograms by hand can become a little tedious. Thankfully, the package comes with the examples
dataset, which is a list of many predefined nonograms of various sizes. Let's pick one to solve.
data(examples) ex36 <- examples[[36]] ex36 plot(ex36, with_solution = FALSE)
Let's solve our 15 x 15 nonogram using the permutation elimination algorithm (the default). In fact, all the arguments in the call to solve
below are the defaults, except the nonogram itself.
ex36 <- solve(ex36, algorithm = "perm_elim", verbose = TRUE, parallel = TRUE) plot(ex36)
The main issue with this algorithm is that all possible permutations of 0s and 1s for the row and column lengths must be calculated up front. This quickly consumes a lot of memory and processing power and so this algorithm doesn't scale to puzzles larger than 30 x 30.
Even if solving smaller puzzles, if you are solving multiple puzzles of the same size, it is more efficient to calculate these full permutation matrices once, rather than separately for each puzzle. Let's say we wish to solve puzzles that are 15 x 15, we can calculate the matrix of all possible permutations of 0 and 1 of length 15 using the function make_full_perm_set()
.
perms15 <- make_full_perm_set(15) perms15[1:10, ] # just the first 10 rows dim(perms15)
ex36 <- ex36 |> solve(row_permutations = perms15, column_permutations = perms15) |> plot()
This package does not implement the best or most efficient nonogram solving algorithms, but you are very welcome to create a pull request to improve the existing package and implement better algorithms (such as those discussed at https://webpbn.com/survey/). This also isn't the only R package for solving nonograms. Please also check out https://github.com/coolbutuseless/nonogram.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.