Skip to content

Instantly share code, notes, and snippets.

@rdivyanshu
Last active November 29, 2023 11:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rdivyanshu/ea7d280767c7c68cffb94f1421813efa to your computer and use it in GitHub Desktop.
Save rdivyanshu/ea7d280767c7c68cffb94f1421813efa to your computer and use it in GitHub Desktop.
Haunted puzzles (by KrazyDad) solved in ASP
#const n = 4.
% count(X, Y, N) - Number of ghoulish fiends (N) visible from X, Y.
% Grid is extended to include 0th and (n + 1)th row as well as 0th and (n + 1)th column
count(0, 1, 0). count(0, 2, 2). count(0, 3, 2). count(0, 4, 3).
count(1, 0, 2). count(2, 0, 1). count(3, 0, 2). count(4, 0, 0).
count(5, 1, 1). count(5, 2, 1). count(5, 3, 1). count(5, 4, 2).
count(1, 5, 1). count(2, 5, 1). count(3, 5, 2). count(4, 5, 1).
% mirror(X, Y, T) - There is mirror of type T on X, Y.
mirror(1, 4, "/").
mirror(2, 1, "\\"). mirror(2, 4, "/").
mirror(3, 2, "/"). mirror(3, 3, "/").
mirror(4, 2, "\\"). mirror(4, 3, "\\").
% number of ghosts, draculas and zombies on grid.
ghost(2).
dracula(4).
zombie(3).
#script(python)
import clingo.ast
import clingo.symbol
import clingox.ast
import itertools
def get_direction(r, c, n):
if r == 0: return "S"
if r == n + 1: return "N"
if c == 0: return "E"
if c == n + 1: return "W"
def get_displacement(dir):
if dir == "N": return (-1, 0)
if dir == "E": return (0, 1)
if dir == "S": return (1, 0)
if dir == "W": return (0, -1)
def reflect_direction(dir, mirror):
if mirror == "/":
return { "S" : "W", "E" : "N", "N" : "E", "W" : "S" }[dir]
if mirror == "\\":
return { "S" : "E", "N" : "W", "E" : "S", "W" : "N" }[dir]
def visibility(n, r, c, mirror_loc):
cr, cc, dir = r, c, get_direction(r, c, n)
dr, dc = get_displacement(dir)
cr, cc = cr + dr, cc + dc
direct, mirror, through_mirror = [], [], False
while 1 <= cr <= n and 1 <= cc <= n:
if through_mirror:
mirror.append((cr, cc, dir))
else:
direct.append((cr, cc, dir))
if (cr, cc) in mirror_loc:
through_mirror = True
dir = reflect_direction(dir, mirror_loc[(cr, cc)])
dr, dc = get_displacement(dir)
cr, cc = cr + dr, cc + dc
return direct, mirror
def build_rule_ast(name, arguments):
position = clingo.ast.Position("<generated>", 0, 0)
location = clingo.ast.Location(position, position)
arguments = list(map(lambda x : clingo.ast.SymbolicTerm(location, x), arguments))
function = clingo.ast.Function(location, name, arguments, False)
atom = clingo.ast.SymbolicAtom(function)
literal = clingo.ast.Literal(location, clingo.ast.Sign.NoSign, atom)
return clingo.ast.Rule(location, literal, [])
def main(ctl):
statements = []
clingo.ast.parse_string(
open("instance.lp").read(),
lambda st: statements.append(st)
)
with clingo.ast.ProgramBuilder(ctl) as b:
for st in statements:
b.add(st)
mirror_loc = []
for st in statements:
if st.ast_type != clingo.ast.ASTType.Rule:
continue
ast_dict = clingox.ast.ast_to_dict(st)
if ast_dict["head"]["atom"]["symbol"]["name"] != "mirror":
continue
arguments = ast_dict["head"]["atom"]["symbol"]["arguments"]
arguments = map(lambda x: clingox.ast.dict_to_ast(x), arguments)
r, c, t = map(lambda x: dict(x.items())["symbol"], arguments)
mirror_loc.append(((r.number, c.number), t.string))
n = ctl.get_const("n").number
lookouts = itertools.chain.from_iterable(
[[(0, i), (n+1, i), (i, 0), (i, n+1)] for i in range(1, n+1)])
for i, j in lookouts:
direct, mirror = visibility(n, i, j, dict(mirror_loc))
with clingo.ast.ProgramBuilder(ctl) as b:
for (r, c, d) in direct:
b.add(build_rule_ast(
"direct_visible",
[clingo.symbol.Number(i), clingo.symbol.Number(j),
clingo.symbol.Number(r), clingo.symbol.Number(c),
clingo.symbol.String(d)]))
for (r, c, d) in mirror:
b.add(build_rule_ast(
"mirror_visible",
[clingo.symbol.Number(i), clingo.symbol.Number(j),
clingo.symbol.Number(r), clingo.symbol.Number(c),
clingo.symbol.String(d)]))
ctl.ground([("base", [])])
ctl.solve()
#end.
grid(X, Y) :- X = 1..n, Y = 1..n.
{ ghost(X, Y) : grid(X, Y) } = N :- ghost(N).
{ dracula(X, Y) : grid(X, Y) } = N :- dracula(N).
{ zombie(X, Y) : grid(X, Y) } = N :- zombie(N).
:- ghost(X, Y), mirror(X, Y, T).
:- dracula(X, Y), mirror(X, Y, T).
:- zombie(X, Y), mirror(X, Y, T).
:- ghost(X, Y), dracula(X, Y).
:- ghost(X, Y), zombie(X, Y).
:- dracula(X, Y), zombie(X, Y).
ghost(X, Y, N) :- count(X, Y, TN),
N = #count { GX, GY, D : mirror_visible(X, Y, GX, GY, D), ghost(GX, GY) }.
dracula(X, Y, N) :- count(X, Y, TN),
N = #count { DX, DY, D : direct_visible(X, Y, DX, DY, D), dracula(DX, DY) }.
zombie(X, Y, N1 + N2) :- count(X, Y, TN),
N1 = #count { ZX, ZY, D : mirror_visible(X, Y, ZX, ZY, D), zombie(ZX, ZY) },
N2 = #count { ZX, ZY, D : direct_visible(X, Y, ZX, ZY, D), zombie(ZX, ZY) }.
:- count(X, Y, N), ghost(X, Y, G), dracula(X, Y, D), zombie(X, Y, Z), N != G + D + Z.
#show ghost/2.
#show dracula/2.
#show zombie/2.
@rdivyanshu
Copy link
Author

rdivyanshu commented Nov 9, 2023

haunted

clingo version 5.6.2
Reading from solution.lp
Solving...
Answer: 1
zombie(1,2) zombie(1,3) zombie(4,4) dracula(2,2) dracula(2,3) dracula(3,1) dracula(3,4) ghost(1,1) ghost(4,1)
SATISFIABLE

Models       : 1+
Calls        : 1
Time         : 0.071s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
CPU Time     : 0.071s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment