char(X, L, C0, L, C) --> [X0], {X0 \= 0'\n, char_code(X, X0), C is C0 + 1}, !. digit(D, L0, C0, L, C) --> char(D, L0, C0, L, C), {char_type(D, digit)}. digits([D|Ds], L0, C0, L, C) --> digit(D, L0, C0, L1, C1), digits0(Ds, L1, C1, L, C). digits0(Ds, L0, C0, L, C) --> digits(Ds, L0, C0, L, C). digits0([], L, C, L, C) --> []. newline(L0, _, L, 0) --> "\n", {L is L0 + 1}. num(n(N, L-C0-C1), L, C0, L, C) --> digits(Ds, L, C0, L, C), !, {number_chars(N, Ds), C1 is C - 1}. dot(L0, C0, L, C) --> char('.', L0, C0, L, C). sym(s(X, L-C0), L, C0, L, C) --> char(X, L, C0, L, C), {\+ char_type(X, digit), X \= '.'}. thing(X, L0, C0, L, C) --> num(X, L0, C0, L, C). thing(X, L0, C0, L, C) --> sym(X, L0, C0, L, C). nonthing(L0, C0, L, C) --> dot(L0, C0, L, C). nonthing(L0, C0, L, C) --> newline(L0, C0, L, C). input([], L, C, L, C) --> []. input([I|Is], L0, C0, L, C) --> thing(I, L0, C0, L1, C1), !, input(Is, L1, C1, L, C). input(Is, L0, C0, L, C) --> nonthing(L0, C0, L1, C1), !, input(Is, L1, C1, L, C). input(Is) --> input(Is, 0, 0, _, _). adjacent(LN-CN0-CN1, LS-CS) :- LLo is LN - 1, LHi is LN + 1, LS >= LLo, LS =< LHi, CLo is CN0 - 1, CHi is CN1 + 1, CS >= CLo, CS =< CHi. part_number(N) :- n(N, NLoc), s(_, SLoc), adjacent(NLoc, SLoc). number_next_to(N, SLoc) :- n(N, NLoc), adjacent(NLoc, SLoc). gear(Ratio) :- s('*', SLoc), bagof(N, number_next_to(N, SLoc), [A, B]), Ratio is A * B. parse_setup(File) :- parse(File, Items), setup(Items). parse(File, Items) :- phrase_from_file(input(Items), File), !. setup(Items) :- transaction((abolish(n/2), abolish(s/2), maplist(assert, Items))). :- dynamic n/2, s/2. sum(Xs, X) :- foldl(plus, Xs, 0, X). part1(File) :- parse_setup(File), bagof(N, part_number(N), Ns), sum(Ns, Total), writeln(Total). part2(File) :- parse_setup(File), bagof(R, gear(R), Rs), sum(Rs, Total), writeln(Total). % vim: set ft=prolog :