Related
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.
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...
I try to find how many children a person have. Created my sample space and in find procedure I should count the numbers of children and print the value.
Sample input/output:
?- find(joe,Result).
false.
I tried to change find(X,R) like that:
find(X,R) :-
R is 0 .
R is R+1 ,
father(X,Y).
write(R).
Then its input/output became like that
?- find(joe,R).
R = 0.
I tried what i know about prolog but i think i have few knowledge to do that task. All suggestions are welcome. Thanks for all your interest.
:- [library(clpr)].
mother(susan, sue).
father(joe, eric).
father(joe, smith).
father(joe, barrack).
father(sue, john).
father(eric, bill).
father(bill, george).
find(X,R):-
R is 0,
R is R+1 ,
father(X,Y).
To find the number of all children of a specific person, you need to collect all solutions to father(Father, Children) or mother(Mother, Children) and count them. For example:
number_of_childrens(Person, N) :-
findall(Children, (father(Person, Children); mother(Person, Children)), Childrens),
length(Childrens, N).
For example:
?- number_of_childrens(joe, N).
N = 3.
I was asked to write a Prolog code to solve the cryptarithmetic puzzle, using "generate and test". For example I get solve([R,O,B],[B,E,R,T],[N,O,R,E,S]) and I need to find an assign for the letters.
So I wrote this code:
sum(List1,List2,SumList) :-
append(List1,List2,List3),
append(List3,SumList,AllList),
assign([0,1,2,3,4,5,6,7,8,9],AllList),
add_zero(List1,List1Z),
add_zero(List2,List2Z),
add_zero(SumList,SumListZ),
name(Num1,List1Z),
name(Num2,List2Z),
name(SumNum,SumListZ),
SumNum is Num1+Num2,
!.
remove(X,[X|Xs],Xs).
remove(X,[_|Ys],Res) :-
remove(X,Ys,Res).
assign(Digits,[X|Tail]) :-
nonvar(X),
!,
assign(Digits,Tail).
assign(Digits,[X|Tail]) :-
remove(X,Digits,D1),
assign(D1,Tail).
assign(_,[]) :-
!.
add_zero([X|Tail1],[Y|Tail2]) :-
!,
Y is X+48,
add_zero(Tail1,Tail2).
add_zero([],[]) :-
!.
But I have a bug and I can't find it... can you help me?
The problem with your code is that in the second clause of remove/3 you are not keeping the item which is not removed.
It should read:
remove(X,[Y|Ys],[Y|Res]):-
remove(X,Ys,Res).
I tried your code with SEND + MORE = MONEY and it worked fine after fixing that procedure.
However it did not find a solution for ROB + BERT = NORES... According to this site, which has many solvers, your equation has no solution.
I have got some values H, and I would like to find the maximum one using \+, how can i do it?
maxValue(X) :-
Get(Id, X),
\+( Get(Id, Y), X < Y ).
don't have a clue....please help, thanks!
Using negation is one way to find the maximum. And it really works.
Here is an example:
p(2).
p(1).
p(3).
?- p(X), \+ (p(Y), Y > X).
X = 3
But the complexity will be O(n*n) where n is
the number of facts. But the maximum can be
determined in O(n). So maybe the following is
more efficient for large fact bases:
:- dynamic(the_max/1).
update_max(X) :-
the_max(Y), X>Y, !, retract(the_max(Y)), assertz(the_max(X)).
update_max(_).
find_max(X) :-
assertz(the_max(0)),
(p(Y), update_max(Y), fail; true),
retract(the_max(X)).
?- find_max(X).
X = 3
But watch out, when you use it from multiple threads,
you need to adapt it a little, i.e. make the_max
thread local.
Best Regards
See also these questions/answers:
Prolog query to find largest element in database?
Max out of values defined by prolog clauses