I was playing around using transitive closures (at least my definition of it) and got better than expected results which puzzle me a bit. I’ll start with a diagram of the graph before showing the code:
This is a game tree in which red circles are a loss, blue victory, and black draw.
My Prolog version of the above looks like so:
move(a, 1, b).
move(a, 2, c).
move(a, 3, d).
move(b, 4, e).
move(b, 5, f).
% move(b, 9, a). % cycle
move(c, 4, g).
move(c, 5, h).
move(d, 4, i).
move(d, 5, j).
move(e, 6, k).
move(g, 6, l).
move(i, 7, m).
move(j, 8, n).
win(k).
win(n).
draw(l).
draw(m).
lose(f).
lose(h).
transitive_closure(Start, Path) :-
transitive_closure_(Start, [], RPath),
reverse(RPath, Path).
transitive_closure_(S0, Ms, [lose,M|Ms]) :-
move(S0, M, S1),
lose(S1), !.
transitive_closure_(S0, Ms, [draw,M|Ms]) :-
move(S0, M, S1),
draw(S1), !.
transitive_closure_(S0, Ms, [win,M|Ms]) :-
move(S0, M, S1),
win(S1), !.
transitive_closure_(S0, Ms, Acc) :-
move(S0, M, S1),
transitive_closure_(S1, [M|Ms], Acc).
Running transitive_closure(a, Path)
yields:
?- transitive_closure(a, Path).
Path = [1, 5, lose] ;
Path = [2, 5, lose] ;
Path = [3, 4, 7, draw] ;
Path = [3, 5, 8, win].
This surprised me, since I though Prolog would default to a depth first search, in which case the first result would be Path = [1, 4, 6, win]
.
I also tried to hang the above code by making the game tree look like this:
by uncommenting move(b, 9, a).
, but it still gave the correct solution.
Is SWI Prolog’s default search breadth first or iterative deepening rather than depth first? (It’s nice that’s it’s smarter than expected, but I’m confused as to why).