오픈 소스 모험: 에피소드 68: 스위치 퍼즐용 Crystal Z3 솔버
퍼즐은 다음과 같습니다.
연결의 모양과 시각적 표현은 게임마다 다릅니다. 때로는 모든 것을 켜고, 때로는 끄고, 때로는 특정 패턴을 형성하기를 원합니다.
원칙적으로 스위치는 전구와 독립적일 수 있고 연결은 일방적일 수 있지만 일반적으로 수행되지 않으므로 여기서 다루지 않겠습니다.
스위치를 두 번 뒤집는 것은 뒤집지 않는 것과 같고 뒤집는 순서는 중요하지 않으므로 솔루션은 단순히 뒤집는 데 필요한 스위치 목록입니다.
예시
다음은 이 퍼즐 인스턴스의 시각적 예입니다.
o -- o -- * -- *
| | | |
* -- o -- o -- o
| | | |
o -- * -- o -- o
12개의 전구가 있고 그 중 4개는 이미 켜져 있습니다. 왼쪽 상단을 클릭하면 다음과 같이 바뀝니다.
* -- * -- * -- *
| | | |
o -- o -- o -- o
| | | |
o -- * -- o -- o
일반 텍스트 형식
우리는 멋진 ASCII 아트 파서를 만들고 싶지 않으므로 이것을 훨씬 더 간단한 형식으로 바꿀 것입니다.
이를 인코딩하기 위해 모든 노드에 이름을 지정해 보겠습니다.
a -- b -- c -- d
| | | |
e -- f -- g -- h
| | | |
i -- j -- k -- l
이 퍼즐을 인코딩하는 방법은 다음과 같습니다.
a b c d e f g h i j k l
a b f g h i k l
a b
b c
c d
e f
f g
g h
i j
j k
k l
a e
b f
c g
d h
e i
f j
g k
h l
첫 번째 줄은 모든 이름의 목록입니다(이는 기술적으로 선택 사항입니다). 두 번째 줄은 뒤집어야 하는 모든 이름의 목록입니다. 다음 줄은 모두 연결입니다. 가장 컴팩트한 형식은 아니지만 가능합니다.
변수
스위치당 하나와 전구당 하나의 두 가지 변수 세트가 있습니다.
모든 전구 변수에 대해 반전 여부를 지정합니다. 연결된 모든 스위치의 XOR과 같습니다.
스위치를 뒤집어야 하는 경우 스위치 변수는 true입니다. 그러한 변수의 목록이 해결책입니다.
솔버
#!/usr/bin/env crystal
require "z3"
require "set"
class SwitchesSolver
@nodes : Array(String)
@flipme : Set(String)
def initialize(path)
lines = File.read_lines(path).map(&.split)
@nodes = lines.shift
@flipme = lines.shift.to_set
@connections = {} of String => Set(String)
@nodes.each do |n|
@connections[n] = Set{n}
end
lines.each do |(a, b)|
@connections[a] << b
@connections[b] << a
end
@solver = Z3::Solver.new
@switches = {} of String => Z3::BoolExpr
@lights = {} of String => Z3::BoolExpr
end
def call
# Initialize variables
@nodes.each do |n|
@lights[n] = Z3.bool("light #{n}")
@switches[n] = Z3.bool("switch #{n}")
end
# Set which lights should or should not be flipped
@nodes.each do |n|
if @flipme.includes?(n)
@solver.assert @lights[n]
else
@solver.assert ~@lights[n]
end
end
# Which switches affect which light
@nodes.each do |n|
@solver.assert @lights[n] == @connections[n].map{|nn| @switches[nn]}.reduce{|a,b| a^b}
end
if @solver.check
model = @solver.model
puts @nodes.select{|n| model[@switches[n]].to_s == "true" }.sort.join(" ")
else
puts "No solution"
end
end
end
SwitchesSolver.new(ARGV[0]).call
솔버는 매우 간단합니다. 우리가 하는 덜 일반적인 일은 다음과 같습니다.
^
xor 연산자 Set{n}
- 요소로 리터럴 설정n
- Ruby에서는 Set[n]
결과
$ ./switches 1.txt
a b e f g i j k
수동으로 쉽게 확인할 수 있습니다.
지금까지의 이야기
All the code is on GitHub .
다음에 온다
다음 몇 개의 에피소드에 걸쳐 Crystal 및 Z3에서 몇 가지 더 많은 퍼즐 게임 해결사를 코딩할 것입니다.
Reference
이 문제에 관하여(오픈 소스 모험: 에피소드 68: 스위치 퍼즐용 Crystal Z3 솔버), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/taw/open-source-adventures-episode-68-crystal-z3-solver-for-switches-puzzle-4poh텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)