Last active
November 29, 2023 11:20
-
-
Save rdivyanshu/ea7d280767c7c68cffb94f1421813efa to your computer and use it in GitHub Desktop.
Haunted puzzles (by KrazyDad) solved in ASP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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). | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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. | |
Author
rdivyanshu
commented
Nov 9, 2023
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment