clpz:monotonic.

line(L)  --> seq(L_), "\n", { maplist(\Ch^B^nth0(B,".@",Ch), L_, L) }.
input(I) :- phrase_from_file(sequence(line,"",I),"i/04.in"). % input needs trailing newline

shift(W,I,E)           :- append(W,[_],[0|I]), append(I,[0],[_|E]).
shift_horiz(W,I,E)     :- maplist(shift,W,I,E).
shift_vert(N,I,S)      :- transpose(I,I_), shift_horiz(N_,I_,S_), maplist(transpose,[N_,S_],[N,S]).
group_neighbours(I,Ns) :-
	shift_horiz(W,I,E), maplist(shift_vert,[NW,N,NE],[W,I,E],[SW,S,SE]),
	transpose([I,NW,N,NE,W,E,SW,S,SE],Ts), maplist(transpose,Ts,Ns).

step_cell([0|_], 0).
step_cell([1|Ns],0) :- sum_list(Ns,Score), #Score #<  4. % ideally we would use sum/3 or clpb's card/2, but they are too slow.
step_cell([1|Ns],1) :- sum_list(Ns,Score), #Score #>= 4.
step                --> group_neighbours, maplist(maplist(step_cell)).

inaccessible      --> maplist(sum_list), sum_list.
remove(S0,[N|A_]) :- inaccessible(S0,N), step(S0,S1), if_(S0 = S1, A_ = [], remove(S1,A_)).

solve(P1,P2) :- input(I), remove(I,Ns), phrase(([N0,N1],...,[N]),Ns), #P1 #= #N0 - #N1, #P2 #= #N0 - #N.
test         :- make_test(day(4),solve,1372,7922).


It took me a long time to come up with a nice solution to this one, because I find 2d array manipulation to be difficult in Prolog.


~/aoc25pl/04