Prolog: Tick-tack-toe game - algorithm

I have basic Tick-tack-toe game, where 2 players both make moves on a grid of 9 cells. The problem is that after the frist player makes the last and winnig move, the game doesnt stop and player 2 still can play. And if second player, somehow, makes winning move too, he will be the winner, despite player 1 actually getting the win first. It doesnt make same error if second player wins frist. Draw works fine. Here`s the code:
:- dynamic o/1.
:- dynamic x/1.
/* the various combinations of a successful horizontal, vertical
or diagonal line */
ordered_line(1,2,3).
ordered_line(4,5,6).
ordered_line(7,8,9).
ordered_line(1,4,7).
ordered_line(2,5,8).
ordered_line(3,6,9).
ordered_line(1,5,9).
ordered_line(3,5,7).
/*line predicate to complete lines
line(A,B,C) :- ordered_line(A,B,C).
line(A,B,C) :- ordered_line(A,C,B).
line(A,B,C) :- ordered_line(B,A,C).
line(A,B,C) :- ordered_line(B,C,A).
line(A,B,C) :- ordered_line(C,A,B).
line(A,B,C) :- ordered_line(C,B,A).
full(A) :- x(A).
full(A) :- o(A).
empty(A) :- not(full(A)).
all_full :- full(1),full(2),full(3),full(4),full(5),
full(6),full(7),full(8),full(9).
done :- ordered_line(A,B,C), x(A), x(B), x(C), write('Player 2 win.'),nl.
done :- ordered_line(A,B,C), o(A), o(B), o(C), write('Player 1 win.'),nl.
done :- all_full, write('Draw.'), nl.
move1 :- write('Player 1 (o) enter a move: '), read(X), between(1,9,X),
empty(X), assert(o(X)).
move1:-all_full.
move2 :- write('Player 2 (x) enter a move: '), read(X), between(1,9,X),
empty(X),assert(x(X)).
move2:- all_full.
printsquare(N) :- o(N), write(' o ').
printsquare(N) :- x(N), write(' x ').
printsquare(N) :- empty(N), write(' ').
printboard :- printsquare(1),printsquare(2),printsquare(3),nl,
printsquare(4),printsquare(5),printsquare(6),nl,
printsquare(7),printsquare(8),printsquare(9),nl.
clear :- x(A), retract(x(A)), fail.
clear :- o(A), retract(o(A)), fail.
play :- not(clear), repeat, move1, printboard, move2,printboard, done.
And that`s the error I get:
Game doesnt stop when player 1 wins
Hope you can help me :) Thanks in advance.
Edit: The "Player 2 wins" shows in "done" predicate. After successfull finishing line of 3 'o' or 'x' game should end with either Player 1 wins or Player 2 wins. I`ll include original code, which might help with understanding the problem I get Original code with comments

The problem is in the main predicate of the game:
play :- not(clear), repeat, move1, printboard, move2,printboard, done.
You do not check for done after player's 1 move.
In Prolog's syntax, logical "or" is written as ;. So one way to express the play predicate is:
play :- not(clear), repeat, move1, printboard, (done; move2, printboard, done).
Which says "after move1, check if the game is done (i.e., finished); if not, player 2 makes the move, and then the check is repeated.

Related

Prolog Chess AI, Alpha Beta Pruning

I am relatively new to Prolog. Currently I am trying to do an AI for a Minichess Game, named Diana chess or Ladies chess (https://en.wikipedia.org/wiki/Minichess). It's basically chess but with a 6x6 Board where you don't have a queen and only one knight.
For my AI, I am mainly looking at the book Prolog programming for artificial intelligence by Ivan Bratko (3rd Edition). There he shows an implementation of the alpha-beta algorithm.I am also looking at the Prolog-Code someone posted on GitHub, where he implemented the same code for his Checkers game. (https://github.com/migafgarcia/prolog-checkers/blob/master/checkers.pl)
The code should think a certain number of turns ahead and choose the best move out of them. Before I started programming in Prolog I did a lot oh Java programming, that's why "Zug" in the first line of my Code is the return of NextMove. I hope this all makes some sense.
My Problem is that when I run my code and, for example, I let the AI run against a human (me), when I start as white and do my first move (b2b3), the AI just doesn't work, the backtracking kinda stops right before NextMove and that way I can't get the best move back. Furthermore when the AI thinks for the minimizing player and needs to choose his move, he always takes the move with the highest value, but, when my understanding from the algorithm is not wrong, shouldn't it take the lowest Value?
I am grateful for every comment on this or for any feedback at all.
Here is the code: https://pastebin.com/sc4gLdQe
alphabeta(Player,MaxiPlayer,Alpha,Beta,Board,NextMove,Eval,Depth) :-
Depth<2,
NewDepth is Depth+1,
findall(
(Player,Figur,VonL,VonR,NachL,NachR,Gegner,Board),
zugbewegung(Player,Figur,VonL,VonR,NachL,NachR,Gegner,Board),
Moves
),
write('All Moves:'),
sysoutMoves(Moves),nl,
bounded_best(Player,MaxiPlayer,Alpha,Beta,Board,Moves,NextMove,Eval,NewDepth),
write('NextMove: '),
write(NextMove),nl,
!.
alphabeta(Player,_,_,_,Board,_,Eval,_) :-
value(Board,Eval,Player),
!.
bounded_best(Player,MaxiPlayer,Alpha,Beta,Board,
[(Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_)|Moves],
BestMove,BestEval,Depth) :-
sysoutMove((Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_)),
sysoutMoves(Moves),
ziehen(Board,(Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_),NewBoard),
gegner(Player,NextPlayer),
alphabeta(NextPlayer,MaxiPlayer,Alpha,Beta,NewBoard,_,Eval,Depth),
good_enough(Player,MaxiPlayer,Moves,Alpha,Beta,Board,
(Spieler,Figur,VonL,VonR,NachL,NachR,Gegner,_),
Eval,BestMove,BestEval,Depth).
good_enough(_,_,[],_,_,_,_,_,_,_) :- !.
good_enough(Player,MaxiPlayer,_,_,Beta,_,Move,Eval,Move,Eval,_) :-
minimizing(Player,MaxiPlayer),
Eval > Beta,
!.
good_enough(Player,MaxiPlayer,_,Alpha,_,_,Move,Eval,Move,Eval,_) :-
maximizing(Player,MaxiPlayer),
Eval < Alpha,
!.
good_enough(Player,MaxiPlayer,Moves,Alpha,Beta,Board,Move,Eval,BestMove,BestEval,Depth) :-
new_bounds(Player,MaxiPlayer,Alpha,Beta,Eval,NewAlpha,NewBeta),
bounded_best(Player,MaxiPlayer,NewAlpha,NewBeta,Board,Moves,Move1,Eval1,Depth),
better_of(Player,MaxiPlayer,Move,Eval,Move1,Eval1,BestMove,BestEval).
new_bounds(Player,MaxiPlayer,Alpha,Beta,Eval,Eval,Beta) :-
minimizing(Player,MaxiPlayer),
Eval > Alpha,
!.
new_bounds(Player,MaxiPlayer,Alpha,Beta,Eval,Alpha,Eval) :-
maximizing(Player,MaxiPlayer),
Eval < Beta,
!.
new_bounds(_,_,Alpha,Beta,_,Alpha,Beta).
better_of(Player,MaxiPlayer,Move1,Eval1,_,Eval2,Move1,Eval1) :-
maximizing(Player,MaxiPlayer),
Eval1 >= Eval2,
!.
better_of(Player,MaxiPlayer,_,Eval1,Move2,Eval2,Move2,Eval2) :-
maximizing(Player,MaxiPlayer),
Eval2 >= Eval1,
!.
better_of(Player,MaxiPlayer,Move1,Eval1,_,Eval2,Move1,Eval1) :-
minimizing(Player,MaxiPlayer),
Eval1 =< Eval2,
!.
better_of(Player,MaxiPlayer,_,Eval1,Move2,Eval2,Move2,Eval2) :-
minimizing(Player,MaxiPlayer),
Eval2 =< Eval1,
!.
maximizing(Player,MaxiPlayer) :-
Player == MaxiPlayer.
minimizing(Player,MaxiPlayer) :-
Player \== MaxiPlayer.

List processing calculation in Prolog to find a destination friends will visit

I'm trying to write a predicate that calculates which destination a group of friends will visit.
The friends list their countries of preferences like this
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
I want to write a predicate called where that takes 2 arguments to perform the calculation.
The formula I have in mind is that the first country is worth 3 points, the second one is worth 2 points, and the last one is worth 1 point.
Here's an example of what I'm trying to achieve.
?- where([marie,jean,sasha,helena,emma],Country).
peru .
So far I have this
where([], X).
where([H|T], N) :- choice(H, [A|B]), where(T,N).
It lets me iterate through all the different friends and shows their choices but I can't iterate through the list of choices and assign points to the destinations.
How should I go about iterating through the list of choices for each friend and assigning points to calculate the best destination?
While this will solve your problem, I know it uses many predicates that you have not seen. So think of this an opportunity to excel and learn a lot.
Even if you don't understand it all, there is enough detail and intermediate results in the test that you should be able to navigate your way to a proper solution you create.
Also this is by no means efficient, it was just a quick proof of concept I did to see how this could be done.
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
destinations(Destinations) :-
findall(D1,choice(_,D1),D2),
flatten(D2,D3),
list_to_set(D3,Destinations).
init_weights(Destinations,Weights) :-
empty_assoc(Assoc),
init_weights(Destinations,Assoc,Weights).
init_weights([],Weights,Weights).
init_weights([H|T],Assoc0,Weights) :-
put_assoc(H,Assoc0,0,Assoc1),
init_weights(T,Assoc1,Weights).
update_weights([C1,C2,C3],Weights0,Weights) :-
del_assoc(C1,Weights0,Value0,Weights1),
Value1 is Value0 + 3,
put_assoc(C1,Weights1,Value1,Weights2),
del_assoc(C2,Weights2,Value2,Weights3),
Value3 is Value2 + 2,
put_assoc(C2,Weights3,Value3,Weights4),
del_assoc(C3,Weights4,Value4,Weights5),
Value5 is Value4 + 1,
put_assoc(C3,Weights5,Value5,Weights).
person_weight(Person,Weights0,Weights) :-
choice(Person,[C1,C2,C3]),
update_weights([C1,C2,C3],Weights0,Weights).
people(People) :-
findall(Person,choice(Person,_),People).
choice(Destination) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
people(People),
update_choices(People,Weights0,Weights1),
cross_ref_assoc(Weights1,Weights),
max_assoc(Weights, _, Destination),
true.
cross_ref_assoc(Assoc0,Assoc) :-
assoc_to_list(Assoc0,List0),
maplist(key_reverse,List0,List),
list_to_assoc(List,Assoc).
key_reverse(Key-Value,Value-Key).
update_choices([],Weights,Weights).
update_choices([Person|People],Weights0,Weights) :-
person_weight(Person,Weights0,Weights1),
update_choices(People,Weights1,Weights).
Tests
:- begin_tests(destination).
test(destinations) :-
destinations([peru, greece, vietnam]).
test(init_weights) :-
destinations(Destinations),
init_weights(Destinations,Weights),
assoc_to_list(Weights,[greece-0, peru-0, vietnam-0]).
test(update_weights) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
update_weights([peru,greece,vietnam],Weights0,Weights),
assoc_to_list(Weights,[greece-2,peru-3,vietnam-1]).
test(person_weight) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
person_weight(jean,Weights0,Weights),
assoc_to_list(Weights,[greece-3,peru-2,vietnam-1]).
test(people) :-
people([marie,jean,sasha,helena,emma]).
test(update_choices) :-
destinations(Destinations),
init_weights(Destinations,Weights0),
people(People),
update_choices(People,Weights0,Weights),
assoc_to_list(Weights,[greece-10,peru-12,vietnam-8]).
test(cross_ref_assoc) :-
List0 = [1-a,2-b,3-c],
list_to_assoc(List0,Assoc0),
cross_ref_assoc(Assoc0,Assoc),
assoc_to_list(Assoc,[a-1,b-2,c-3]).
test(choice) :-
choice(peru).
:- end_tests(destination).
As suggested by GuyCoder, you need an accumulator to sum each person preferences, and foldl/N allows to does exactly this.
choice(marie, [peru,greece,vietnam]).
choice(jean, [greece,peru,vietnam]).
choice(sasha, [vietnam,peru,greece]).
choice(helena,[peru,vietnam,greece]).
choice(emma, [greece,peru,vietnam]).
where(People,Where) :-
foldl([Person,State,Updated]>>(choice(Person,C),update(State,C,Updated)),
People,
[0=greece,0=peru,0=vietnam],
Pref),
aggregate(max(S,S=W),member(S=W,Pref),max(_,_=Where)).
% sort(Pref,Sorted),
% last(Sorted,_=Where).
update(S0,[A,B,C],S3) :-
update(S0,3,A,S1),
update(S1,2,B,S2),
update(S2,1,C,S3).
update(L,V,C,U) :-
append(X,[Y=C|Z],L),
P is Y+V,
append(X,[P=C|Z],U).
I have left commented the last two goals replaced by the single goal aggregate/3, so you can try to understand the syntax...

Why is my Prolog code printing in reverse?

My code in prolog is appearing in the reverse expected order. Here is the code:
room(homermargeroom, donuts).
room(homermargeroom, tv).
room(ensuite, nothing).
room(lisaroom, beer).
room(bartroom, donuts).
room(hallway, nothing).
room(bathroom, nothing).
room(maggieroom, nothing).
/* These are the locations where Bart, Lisa and Maggie are hiding */
hiding(bart, cupboard).
hiding(lisa, ensuite).
hiding(maggie, bathroom).
canHomerGet(Start, End, Item) :-
homermove(Start, End),
canTravelThrough(Start, Item),
canTravelThrough(End, Item),
write('Homer moves from '), write(Start), write(' to '), write(End), nl.
canHomerGet(Start, End, Item) :-
homermove(Start, Somewhere),
canTravelThrough(Somewhere, Item),
canHomerGet(Somewhere, End, Item),
write('Homer moves from '), write(Start), write(' to '), write(Somewhere), nl.
canTravelThrough(Somewhere, _Item) :-
room(Somewhere, nothing).
canTravelThrough(Somewhere, Item) :-
room(Somewhere, tv),
Item == portableTV.
canTravelThrough(Somewhere, Item) :-
room(Somewhere, donuts),
Item == nachos.
canTravelThrough(Somewhere, Item) :-
room(Somewhere, sistersinlaw),
Item == blindfold.
canTravelThrough(Somewhere, Item) :-
room(Somewhere, beer),
Item == margarita.
canHomerFind(Child, Item) :-
hiding(Child, Destination),
canHomerGet(garage, Destination, Item).
Here is the output:
Homer moves from foyer to cupboard
Homer moves from diningroom to foyer
Homer moves from kitchen to diningroom
Homer moves from sidehall to kitchen
Homer moves from garage to sidehall
The way I have written this, I would expect this to print out 'Homer moves from garage to sidehall' fist, and then print that list in the reverse order that it is. Any suggestions on how to fix this?
Your definition of the predicate canHomerGet/3 only writes the move output at the end. In the second clause, the recursive call precedes the writing of the move. This makes this predicate non tail-recursive. I.e. the writing of the moves is saving on the implicit recursive stack and then popped from the stack when the call to the predicate succeeds. Thus, the first movement printed is the last one and the last movement printed is the first one.
You could be tempted to solve this problem by modifying the second clause to:
canHomerGet(Start, End, Item) :-
homermove(Start, Somewhere),
canTravelThrough(Somewhere, Item),
write('Homer moves from '), write(Start), write(' to '), write(Somewhere), nl,
canHomerGet(Somewhere, End, Item).
This would make the predicate tail-recursive, thus running in constant stack space, but rises a new problem: movements leading nowhere would also be printed as backtracking to find a successful route will not undo the printing of the movements. The usual solution is to build a list of steps (using an additional argument) and then to print the list (after reversing it) at the end. I will leave that to you as exercise.

How to get two values from the user by using SWI prolog

Im working with SWI prolog to ask the user to insert two different values according to the following codes:
base:-
write('\n Please enter the base and exponent or 0 and 0 to stop the program:'),
read (X),
read(Y),
bas(X,Y).
bas(0,0):- !.
bas(X,Y):-
f is X*Y,
write('The power of '),write(x),
write(' raised to '),write(y),
write(' is '),write(f),
base.
but after i do the consult the first line will display (insert The number) but when I insert the values it show for me error.
So what is the mistake and why the program cant read my different values
here a slight reformulation
base :-
writeln('enter base and exponent (terminate with .) or 0 and 0 to stop the program'),
read(X),
read(Y),
bas(X,Y).
bas(0,0):- !.
bas(X,Y):-
P is X ^ Y,
format('The power of ~w raised to ~w is ~w~n', [X,Y,P]),
base.
to me, such interaction seems a bit weird:
?- base.
enter base and exponent (terminate with .) or 0 and 0 to stop the program
|: 4.
|: 2.
The power of 4 raised to 2 is 16
enter base and exponent (terminate with .) or 0 and 0 to stop the program
|: 0.
|: 0.
true.

prolog puzzle, can't find error

Here's my code for my assignment, its not working correctly.
What it is supposed to do is take a family fact in the form of family(Title,[Name/Speed|_]) as seen in the the first line of code and produce all the possible results of the well known bridge puzzle with the flashlight. However, all it says is true or false, depending on if the family fact is there or not.
This program works perfectly fine if all the facts are laid out for example
person(dad,1)
person(mom,2)
person(kid,5)
person(granny,10)
when ^ is in the code, and the last assert_family is eliminated the code is good.
So it is something to do with the assert_family not being used correctly.
family(original, [father/1,mother/2,child/5,granny/10]).
moveFamily(Key,Journey, TotalTime):-
assert_family(Key),
findall(Person+Time, person(Person, Time), Left),
moveFamily(Left, [], Journey),
findall(Time, member([Time|_], Journey), LTime),
sumlist(LTime, TotalTime).
moveFamily([P1-T1, P2-T2], _, [[T, [P1-P2]]]):-
T is max(T1, T2).
moveFamily(Left, Right, [[LT, [P1-P2]],[RT, [P3]]|Journey]):-
select(P1-T1, Left, MLeft1),
select(P2-T2, MLeft1, MLeft2),
LT is max(T1, T2),
select(P3-RT, [P1-T1,P2-T2|Right], MRight),
moveFamily([P3-RT|MLeft2], MRight, Journey).
:- dynamic person/2.
assert_family(Key) :-
family(Key,List),
forall(member(N/V,List),
assertz(person(N,V))).
% this seems to work:
% LPA WIN-PROLOG 4.010 on mac with WinOnX
family(original, [father/1,mother/2,child/5,granny/10]).
moveFamily(Key,Journey, TotalTime):-
assert_family(Key),
findall(Person-Time, person(Person, Time), Left), % changed+
moveFamily(Left, [], Journey),
findall(Time, member([Time|_], Journey), LTime),
sumlist(LTime, TotalTime).
moveFamily([P1-T1, P2-T2], _, [[T, [P1-P2]]]):-
T is max(T1, T2).
moveFamily(Left, Right, [[LT, [P1-P2]],[RT, [P3]]|Journey]):-
select(P1-T1, Left, MLeft1),
select(P2-T2, MLeft1, MLeft2),
LT is max(T1, T2),
select(P3-RT, [P1-T1,P2-T2|Right], MRight),
moveFamily([P3-RT|MLeft2], MRight, Journey).
%:-dynamic(person/2).
assert_family(Key) :- dynamic(person/2),
family(Key,List),
forall(member(N/V,List),
assertz(person(N,V))).
select(H,[H|T],T).
select(X,[H|T],[H|T1]):-select(X,T,T1).
sumlist(L,Sum) :-
sumlist(L,0,Sum).
sumlist([],Sum,Sum).
sumlist([H|T],Count,Sum) :-
NewCount is Count + H,
sumlist(T,NewCount,Sum).

Resources