오픈 소스 모험: 에피소드 69: 수족관 퍼즐용 Crystal Z3 솔버
퍼즐에 익숙해지려면 몇 번 플레이하는 것이 좋습니다. 그러면 게시물을 더 쉽게 따라갈 수 있습니다.
수입자
늘 그렇듯이 큰 퍼즐의 모든 문자를 수동으로 입력하는 데 인내심이 없고 오류가 발생하기 쉬울 것이므로 Puzzle Team의 내부 형식에서 변환하는 임포터를 작성했습니다.
#!/usr/bin/env crystal
hints, board = ARGV[0].split(";", 2)
hints = hints.split("_")
size = hints.size // 2
top = hints[0, size]
left = hints[size, size]
board = board.split(",").map{|c| (96+c.to_i).chr}.join.scan(/.{#{size}}/).map(&.[0])
puts [" #{top.join}\n\n", left.zip(board).map{|a,b| "#{a} #{b}\n"}.join].join
$ ./import_puzzle '5_3_1_3_3_5_5_3_4_4_2_3_4_4_4_6_3_2_2_6;1,1,1,1,2,3,3,4,4,5,1,6,6,6,2,7,4,4,5,5,1,8,8,2,2,7,4,4,5,9,10,10,10,11,2,7,12,12,9,9,13,13,10,11,11,11,12,14,14,9,15,13,10,10,16,12,12,12,12,12,15,13,10,10,16,12,17,17,17,17,15,13,10,10,16,18,18,18,18,17,19,10,10,16,16,20,18,17,17,17,19,10,10,16,20,20,18,18,18,17' > 1.txt
일반 텍스트 입력
결과는 다음과 같습니다.
5313355344
2 aaaabccdde
3 afffbgddee
4 ahhbbgddei
4 jjjkbgllii
4 mmjkkklnni
6 omjjplllll
3 omjjplqqqq
2 omjjprrrrq
2 sjjpptrqqq
6 sjjpttrrrq
조금 덜 바쁘게 하기 위해 힌트와 보드 사이에 여분의 열과 행을 추가했습니다.
솔버
솔버는 다음과 같습니다.
#!/usr/bin/env crystal
require "z3"
class AquariumSolver
@col_hints : Array(Int32)
@row_hints : Array(Int32)
@board : Array(Array(Char))
@containers : Set(Char)
def initialize(path)
lines = File.read_lines(path)
@col_hints = lines[0][2..].chars.map(&.to_i)
@xsize = @col_hints.size
@row_hints = lines[2..].map(&.[0].to_i)
@ysize = @row_hints.size
@board = lines[2..].map{|l| l[2..].chars}
@containers = @board.flatten.to_set
@solver = Z3::Solver.new
@cells = {} of Tuple(Int32, Int32) => Z3::BoolExpr
@water_levels = {} of Char => Z3::IntExpr
end
def each_coord
@xsize.times do |x|
@ysize.times do |y|
yield(x, y)
end
end
end
def row_cell_count(y)
(0...@xsize).map{|x| @cells[{x,y}].ite(1, 0)}.reduce{|a,b| a+b}
end
def col_cell_count(x)
(0...@ysize).map{|y| @cells[{x,y}].ite(1, 0)}.reduce{|a,b| a+b}
end
def call
# Initialize cell variables
each_coord do |x, y|
@cells[{x,y}] = Z3.bool("cell #{x},#{y}")
end
# Initialize water level variables
# We could restrict it more tightly to make solution unambiguous
# Right now container at levels 4-6 can have any level 0-10
# even though 0-4 means same (all full), and 6-10 means same (all empty)
@containers.each do |c|
@water_levels[c] = v = Z3.int("level #{c}")
@solver.assert v >= 0
@solver.assert v <= @ysize
end
# All cells in each container have correct water level
each_coord do |x, y|
c = @board[y][x]
@solver.assert @cells[{x,y}] == (y >= @water_levels[c])
end
# Row hints are correct
@ysize.times do |y|
@solver.assert row_cell_count(y) == @row_hints[y]
end
# Col hints are correct
@xsize.times do |x|
@solver.assert col_cell_count(x) == @col_hints[x]
end
# Print the solution
if @solver.satisfiable?
model = @solver.model
@ysize.times do |y|
@xsize.times do |x|
v = (model[@cells[{x, y}]].to_s == "true")
if v
print "#"
else
print "."
end
end
print "\n"
end
else
puts "No solution"
end
end
end
AquariumSolver.new(ARGV[0]).call
솔버는 일반적으로 이전 솔버에서 이미 다룬 기술을 사용합니다.
결과
$ ./aquarium 1.txt
.....##...
.###......
......####
...##...##
...###...#
#....#####
##...#....
##........
#.....#...
#...#####.
지금까지의 이야기
All the code is on GitHub .
다음에 온다
다음 몇 개의 에피소드에 걸쳐 Crystal 및 Z3에서 몇 가지 더 많은 퍼즐 게임 해결사를 코딩할 것입니다.
Reference
이 문제에 관하여(오픈 소스 모험: 에피소드 69: 수족관 퍼즐용 Crystal Z3 솔버), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/taw/open-source-adventures-episode-69-crystal-z3-solver-for-aquarium-puzzle-224j텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)