clpz:monotonic.
instruction((right,N)) --> "R", number(N).
instruction((left, N)) --> "L", number(N_), { #N #= - #N_ }.
input(I) :- phrase_from_file(sequence(instruction,"\n",I), "i/01.in").
box(right,N,Box) :- #Box #= #N div 100.
box(left, N,Box) :- #N_ #= #N - 1, #Box #= #N_ div 100.
turn((Dir,N),(Dial0,P1_0,P2_0),(Dial1,P1_1,P2_1)) :-
#Dial1 #= #Dial0 + #N, if_(clpz_t(#Dial1 mod 100 #= 0), #P1_1 #= #P1_0 + 1, #P1_1 #= #P1_0),
box(Dir, Dial0, Box1), box(Dir, Dial1, Box2), #P2_1 #= #P2_0 + abs(#Box1 - #Box2).
solve(P1,P2) :- input(I), foldl(turn,I,(50,0,0),(_,P1,P2)).
test :- make_test(day(1),solve,1158,6860).
Well, this was certainly harder than your average day 1. I use a DCG to parse the input, then use a left fold to simulate turning the dial and keeping track of zeros.
For part two, I imagine that each multiple of 100 is in its own "box". For example, -5 is in box -1, and 123 is in box 1. If you turn from -5 to 123, you cross from box -1 -> box 0 -> box 1, so you pass over two 0s. The only tricky thing is that you need to define boxes differently for turning left/right to get this to work.
~/aoc25pl/01