Mosaic Puzzle is de facto near Minesweeper Puzzle we did within the earlier episode.
Logically the one distinction is that trace cells may comprise mines. And for that motive, as a substitute of “mines” it talks about black and white cells.
The problem we’ll have will probably be with the output, not with the solver.
shard.yml
First we have to load Z3:
identify: mosaic-solver
model: 0.0.1
dependencies:
z3:
github: taw/crystal-z3
Solver
The solver is nearly an identical to the one for Minesweeper we did within the earlier episode. The one distinction within the solver half is that @vars[{x, y}]?
is included in neighbourhood(x, y)
, and we not assert that trace cells should be empty.
#!/usr/bin/env crystal
require "z3"
class MosaicSolver
@knowledge : Array(Array(Nil | Int32))
@ysize : Int32
@xsize : Int32
def initialize(path)
@knowledge = File.read_lines(path).map l.chars.map c.ascii_number? ? c.to_i : nil
@ysize = @knowledge.dimension
@xsize = @knowledge[0].dimension
@solver = Z3::Solver.new
@vars = {} of Tuple(Int32, Int32) => Z3::BoolExpr
finish
def each_coord
@ysize.occasions do |y|
@xsize.occasions do |x|
yield(x, y)
finish
finish
finish
def neighbourhood(x, y)
[
@vars[{x, y+1}]?,
@vars[{x, y}]?,
@vars[{x, y-1}]?,
@vars[{x+1, y+1}]?,
@vars[{x+1, y}]?,
@vars[{x+1, y-1}]?,
@vars[{x-1, y+1}]?,
@vars[{x-1, y}]?,
@vars[{x-1, y-1}]?,
].compact
finish
def neighbourhood_count(x, y)
neighbourhood(x, y).map v.ite(1, 0).scale back a+b
finish
def name
each_coord do |x, y|
@vars[{x, y}] = Z3.bool("#{x},#{y}")
finish
each_coord do |x, y|
c = @knowledge[y][x]
if c
@solver.assert neighbourhood_count(x, y) == c
finish
finish
...
finish
finish
solver = MosaicSolver.new(ARGV[0])
solver.name
Printing answer
OK, let’s get to the difficult half, printing the answer. It is difficult as a result of we have to use completely different colours and nonetheless print the quantity hints.
We might use a flowery library for it, however terminal codes aren’t all that difficult, so I simply hardcoded them.
if @solver.verify
mannequin = @solver.mannequin
@ysize.occasions do |y|
@xsize.occasions do |x|
v = (mannequin[@vars[{x, y}]].to_s == "true")
c = @knowledge[y][x] || " "
if v
print "e[30;104m"
else
print "e[30;102m"
end
print c
end
print "e[0m"
print "n"
end
else
puts "No solution"
end
Result
Here’s what it looks like:
Story so far
Coming next
Over the next few episodes I’ll code a few more puzzle game solvers in Crystal and Z3.