Here is some proof of the pudding. One can use aggregate_all/3 for min/max, here is a little adaptation of the code here. But the code below does not yet return a winning strategy. It only returns a first move and a score:
% best(+Board, +Player, -Move, -Score)
best(X, P, N, W) :-
move(X, P, Y, N),
(win(Y, P) -> W = 1;
other(P, Q),
(tie(Y, Q) -> W = 0;
aggregate_all(max(V), best(Y, Q, _, V), H),
W is -H)).
We can check the moves and scores we get for a position:
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W).
N = 2,
W = -1 ;
N = 5,
W = 1 ;
N = 6,
W = -1 ;
N = 8,
W = -1 ;
N = 9,
W = -1
Now how would we go about and store a winning strategy and choose a winning strategy? One idea here is now to replace the aggregate_all/3 by a findall/3. This should give us the multi branches of a winning strategy:
% best(+Board, +Player, -Tree, -Score)
best(X, P, N-R, W) :-
move(X, P, Y, N),
(win(Y, P) -> R = [], W = 1;
other(P, Q),
(tie(Y, Q) -> R = [], W = 0;
findall(M-V, best(Y, Q, M, V), L),
max_value(L, -1, H),
(Q == x ->
filter_value(L, H, J),
random_member(U, J),
R = [U];
strip_value(L, R)),
W is -H)).
We use random_member/2 for the single branches. Rerun to get different correct results:
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W), W = 1.
N = 5-[2-[6-[]], 6-[9-[]], 8-[9-[]], 9-[6-[]]],
W = 1 ;
No
?- best([[x, -, o], [x, -, -], [o, -, -]], x, N, W), W = 1.
N = 5-[2-[8-[6-[9-[]], 9-[6-[]]]], 6-[9-[]], 8-[6-[]], 9-[6-[]]],
W = 1 ;
No
Open source:
Prolog code for the tic-tac-toe game
score via aggregate, return first move
https://gist.github.com/jburse/928f060331ed7d5307a0d3fcd6d4aae9#file-tictac2-pl
Prolog code for the tic-tac-toe game
score via findall, return random winning strategy
https://gist.github.com/jburse/928f060331ed7d5307a0d3fcd6d4aae9#file-tictac3-pl