Prolog - searching optimal response - tic tac toe - prolog

I am trying to understand how the response works for a game of tic tac toe. At this point I have winningLine facts that will tell if someone has won the game. The user can enter a position and the position will be filled, then Prolog will answer with a simple fact.
I'm trying to understand how to search for the most optimal response to a players input and send back a response.
I as thinking of using a forall and cycle through each user input to find the best spot for the response.
Here is what I have so far regarding the players:
% Specify the the 2 players and empty space for the board
player(X) :- x(X).
% This will be the computer
player(X) :- o(X).
% This is the blank space that we print out first and if the spot has not
been taken yet
blankSpace(X) :- not(player(X)).
% Print the board 3 rows across by 3 columns wide
buildBoard :- printBox(1),printBox(2),printBox(3),nl,
printBox(4),printBox(5),printBox(6),nl,
printBox(7),printBox(8),printBox(9),nl.
next_move(('[x]','[2]','[3]','[4]','[5]','[6]','[7]','[8]','[9]',8).
next_move(6).
playersMove :-
repeat, write('Your turn, please choose a space between 1-9'),
% Read Input
read(X),
% Check if the number the user (X) entered is between 1 and 9
between(1,9,X),
% Select the numbered blank space the user has specified
blankSpace(X),
% Replace the numbered space with the text for the X player
assert(x(X)).
computerMove :-
next_move(X),
blankSpace(X),
assert(o(X)).
My question: How do I find the user input and use the computerMove to find the optimal response on the board.

Related

Prolog project getting started

This is my first time asking a question on here. Sorry if I do something incorrectly.
First, I want to be up front; this is a homework question. I am not looking for anyone to do it for me. I just need help getting started.
The problem is-
Table of data
Using above information, Write a prolog program to answer the following queries.
Please note that you cannot simply answer these queries with only facts, you
should answer then using facts and rules.
A. Which country/countries has/have the largest case number?
B. Which country/countries has/have the smallest number of deaths?
C. Which country has the largest case number or the largest number of deaths?
D. Which country has the largest case number less than 5?
E. How many cases are reported for Austria?
F. How many cases are reported for the Asia?
G. How many cases are reported for the Asia and middle east?
H. Which region has the smallest case number?
I. Which region has the largest number of deaths?
J. Which regions have a smaller case number than Europe?
K. Which country/countries has/have a larger number of deaths than Iran?
L. Which country/countries has/have a larger case number and a larger number
of deaths than Iran?
M. Which country/countries has/have a larger case number or a larger number of
deaths than Iran?
N. What is the average case number in this report?
Ok so. I roughly understand how to complete the rules for how many cases are reported in X. That seems easy. But what I don't understand, is how we can traverse data and compare it in Prolog. In java for example, I would just put all the values in an array and sort it. Making this easy.
My question boils down to this:
How would you store the initial data from the table? (facts)
how would you write the rules to find the largest element?
Example for question A, using swi-prolog:
country_cases_deaths('Jordan', 35, 14).
country_cases_deaths('UK', 5, 3).
country_cases_deaths('Someplace Else', 35, 5).
country_cases_deaths('Austria', 2, 1).
country_max_cases(Country, MaxCases) :-
aggregate_all(
% Want the maximum
max(Cases),
% This is the filter
country_cases_deaths(_, Cases, _),
% Variable to put the max in
MaxCases
),
% Lookup country - could be multiple countries having the same max
country_cases_deaths(Country, MaxCases, _).
Result:
?- country_max_cases(Country, MaxCases).
Country = 'Jordan',
MaxCases = 35 ;
Country = 'Someplace Else',
MaxCases = 35.
?- findall(Country, country_max_cases(Country, _), Countries).
Countries = ['Jordan','Someplace Else'].
Some useful links: aggregate_all (click the orange "show source" icon in the top right, to see sourcecode), discussion
% dt.pl
data(japan,10).
data(usa,20).
readDatas(Datas):- findall(dt(Num,Country),data(Country,Num),Datas).
findMax(Datas,Max):-
sort(Datas,SortedDatas),reverse(SortedDatas,RevDatas),
[Max|_]=RevDatas.
:- readDatas(Datas),findMax(Datas,Max),writeln(Max).
:- halt.
$ apt install swi-prolog
$ swipl dt.pl

Prolog - Check winning condition in X and O game

I have a simple game of tic tac toe, there are many examples of this online that use min maxing however I just want to use a simple example for my own understanding.
I have displayed the board in a 3x3 box that have numbering system that users can pick from, in the following way:
:- dynamic o/1.
:- dynamic x/1.
% The computer has made a turn, print O
printBox(N) :- o(N), write('[o]').
% The player makes a turn, print X
printBox(N) :- x(N), write('[x]').
% We just want to print the empty board
printBox(N) :- blankSpace(N), write('[_]').
buildBoard :- printBox(1),printBox(2),printBox(3),nl,
printBox(4),printBox(5),printBox(6),nl,
printBox(7),printBox(8),printBox(9),nl.
playersMove :-
read(X),
blankSpace(X),
assert(x(X)).
When a user select from the above options (1-9) the board is filled with an X for the human player and O for the computer.
Now I also have facts for the winning lines:
winningLine(1,2,3).
winningLine(4,5,6).
winningLine(7,8,9).
%Winning rows from left to right
winningLine(1,4,7).
winningLine(2,5,8).
winningLine(3,6,9).
%Winning diagnolly
winningLine(7,5,3).
winningLine(9,5,1).
So after each move I want to check if one of the winning line combinations has been played ie does the board contain any of the winningLine combinations and with player has that combination. I have been thinking about it and a findall method could be used here however I would be open to suggestion.
My Question: How do I check the board for the winning conditions?
The simple answer
Since you are working with a global game position, we can assume the existence of a predicate checked(Player, Square) which holds iff player Player has checked the square Square.
Then all you need to check in order to see if a player has won the game is to ask whether there is a winning line where all three squares are checked by the same player:
is_win(Player) :-
winning_line(P1,P2,P3),
checked(Player,P1),
checked(Player,P2),
checked(Player,P3).
You can generate checked/2 by use of assertz:
:- dynamic checked/2.
player_move(Player, Square) :- assertz(checked(Player, Square)).
The better Way
However, if you want to go beyond simulating a simple game you should represent your state in a single data item and not put in in the global database, for example:
initial_state(board([empty, empty, empty],
[empty, empty, empty],
[empty, empty, empty]).
and adjust player_move and checked accordingly:
/* Should make a new board from Position0 by adding the move */
player_move(Player, Square, Position0, Position) :- ...
/* Should check if a player has checked a square inside Position */
checked(Player, Square, Position) :- ...
With the above global state representation, you basically have to call the winningLine(I, J, K) to obtain a triple of indices, then for all these indices, either x(X) (with X replaced by I, J and K) should hold; or for all indices o(X) should hols, like:
xwin :-
winningLine(I, J, K),
x(I),
x(J),
x(K).
nwin :-
winningLine(I, J, K),
o(I),
o(J),
o(K).
win :-
xwin.
win :-
owin.
So here xwin/0 is satisfied, given the player for x has a winning line, owin/0 is satisfied, given the player for o has a winning line, and win/0 is satisfied if any of the players has won.
But personally I think using a global state is not an elegant way to solve problems (actually in most, if not all programming paradigms). Here you can not make use of Prolog's powerful backtracking mechanism, and furthermore if you for example would like to search if there still exists a way how a user can win, you can thus not simply duplicate the board, and run a solver on the duplicated board.

Why does Prolog does not backtrack on comparison?

I would expect that the following should always be true if a comparison backtrack, right? unless it goes into an infinite loop!
?- Y=2 , random:random(1,3,X), X =\= Y.
Y = 2,
X = 1.
?- Y=2 , random:random(1,3,X), X =\= Y.
false.
but I got false!
In general, my question is why doesn't comparison backtrack?
Thanks for all the answers. My confusion seemed to come primarily from my expectation of random keep generating new-random numbers, so I confused that comparison was not backtracking, instead, the reason was that random does its thing only once and fails afterwards.
I was unaware of semi-determinate nature of some predicates.
But now I can be on a lookout ;) for cases like this. thanks again.
In your example, there is nothing to backtrack.
All predicates you are using in these examples ((=)/2, random/3 and (=\=)/2) are semi-deterministic: This means that they either fail, or succeed exactly once.
In other words, they can all succeed at most once.
Therefore, if at least one of these predicates fails, then the query fails.
To generate a succession of pseudo-random numbers on backtracking, use for example repeat/0.
Warning: random/3 is an impure predicate: It may yield different solutions even though the query is exactly the same. It may lead to failure on one invocation, and to success on another. This complicates testing and reasoning about your code considerably.
Prolog works with what are called Horn-clauses. This means that each term individually, for example Y=2, is a separate goal in a question to be answered. The result will be yes or no for each goal, and if all goals answer yes, the question is answered with yes.
What your code asks is as follows:
%Is Y equal to 2 or can it be made equal?
%Yes, Y is a variable and can be assigned the numerical atom 2
Y=2 ,
%Give me a random number between 1 and 3.
%Is X equal to the random number or can it be made equal?
%Yes, X is a variable and can be assigned the outcome atom of random:random
random:random(1,3,X),
%is the term contained within X NOT equivalent to Y?
X =\= Y.
You can check out existing comparison predicates in for example the SWI documentation or on Learn Prolog Now!.
Depending on your implementation you can use trace and write to output the actual atoms in the variables, allowing you to explore how your program actually works.
?- trace, (Y=2 , random:random(1,3,X), write(X),nl, X =\= Y). %SWI-Prolog
SWI-prolog online editor
Infinite recursion looks like p(P) :- p(P).. It has a call to the question it is supposed to solve inside the answer itself, meaning to solve for p(P) it will check p(P), which never ends.
Backtracking only happens when Prolog has choicepoints. Choicepoints are points where in the decision tree, there are MULTIPLE POSSIBLE WAYS to satisfy the question Prolog is currently processing. Prolog works from top to bottom, then left to right.
Think of a cars salesman who gets asked "which car is the best for me?". He has more than one possible car to sell you, so he'll start showing you different cars that meet your criteria. The car needs to have a transport capacity of a volume over 400 liters? All cars that don't satisfy this condition are not presented as a solution.
Prolog does a depth-first search, meaning it goes down to the first answer it finds, then checks whether there's other ways to answer it. If there is no result, the answer is no. If there is at least one solution, the answer is yes and you get all possible answers for your question. This way you only get results that satisfy a whole chain of goals you've set.
I think this will help.
% Generate random value from Min to Max(with backtrack)
rand_backtrack(Min,Max,RandVal):-
create_list(Min,Max,List),
randomize_list(List,Randomized),
length(Randomized,Len),
% Choose one Variable from Randomized (From first element to last).
% When backtrack occured, next element is chosen.
between(1,Len,Idx),
nth1(Idx,Randomized,RandVal).
% create integer order list
% [Min,Min+1,Min+2,....,Max]
create_list(Max,Max,[Max]):-!.
create_list(Min,Max,[Min|Rest]):-
Min1 is Min+1,
create_list(Min1,Max,Rest).
% shuffle List.
% result always changes.
% ex.randomize_list([1,2,3,4,5,6],R) R=[4,2,6,1,3,5]
%
randomize_list([Val],[Val]):-!.
randomize_list(List,[RandVal|RestRandomized]):-
length(List,Len),
random(1,Len,RandIdx),
nth1(RandIdx,List,RandVal),
select(RandVal, List, Rest),
!,
randomize_list(Rest,RestRandomized).
?- rand_backtrack(3,19,A).
A = 6 ;
A = 4 ;
A = 8 ;
A = 13 ;
A = 15 ;
A = 16 ;
A = 9 ;
A = 18 ;
A = 7 ;
A = 3 ;
A = 12 ;
A = 10 ;
A = 17 ;
A = 11 ;
A = 14 ;
A = 5 ;
A = 19.

Check if a country is needed for a critical amount of votes

Hey guys I'm new to prolog and learning it myself.
I found this question on the internet but without any answers.
I have this database
countries([belgium, france, germany, italy, luxembourg, netherlands]).
weight(france, 4).
weight(germany, 4).
weight(italy, 4).
weight(belgium, 2).
weight(netherlands, 2).
weight(luxembourg, 1).
threshold(12).
Now I made this program to see if a list of countries have enough votes to get over the threshold.
winning([], 0).
winning([Head | Tail], N):-
weight(Head, N1),
winning(Tail, N2),
N is N1 + N2.
winning(Y):-
winning(Y, N),
threshold(X),
N >= X.
Now I need to write a program critical/2, first argument a country, second argument a list of countries. Is the first country needed to get over the threshold or not.
example:
?- critical(netherlands, [belgium, france, germany]).
True
?- critical(netherlands, [france, germany, italy]).
False
for this program I need to check first, if the second argument is winning already. And if so it will fail. If not i need to get the value of the first argument add it to the second value and then check if its over the threshold. If it won't be enough it will fail. If it is enough it will succeed.
critical(X,Y):-
winning(Y,N),
weight(X,Value),
N1 is N+Value,
threshold(X),
N1 >= X.
I'm doing alot of things wrong here but I have no idea how to fix it.
You are quite close to the solution. A few hints:
First, a good naming convention helps you keep track of which argument is what:
critical(C, Cs) :-
This makes clear that the first argument is a single country, and the second is a list of zero or more countries.
So, let us first relate Cs to their total weight. Again, it helps to have a good naming convention, making clear which argument is what:
country_weight(france, 4).
country_weight(germany, 4).
country_weight(italy, 4).
country_weight(belgium, 2).
country_weight(netherlands, 2).
country_weight(luxembourg, 1)
Now, to relate multiple countries to their respective weights, we use the meta-predicate maplist/3:
maplist(country_weight, Cs, Ws)
and to sum the weights, we use sum_list/2:
sum_list(Ws, Sum)
It is obvious how to describe that Sum must be below the threshold (left as an exercise).
Finally, to denote that the sum plus the weight of the country denoted by the first argument is greater than the threshould, we use:
country_weight(C, W),
W + Sum > Threshold
This completes the definition. Notice that it was not necessary to describe which countries exist at all. Therefore, you can omit the first predicate of your program.

SWI-Prolog depth-first search?

My aim is to develop a simple program to run a game called 'Kayles' which is similar to Skittles. You have a row of bottles, 2 players knock them down in turns, and the player to knock the last bottle(s) down wins. You can knock 1 or 2 bottles down only, and they have to be next to each other.
The first part of this program was to decide how to represent a bottle, and an empty space. I have decided to use a list, where 1 represents a bottle and 0 represents an empty space.
The second part of this program was to print the current state to the terminal where a bottle is represented by an asterisk * and an empty space is represented by a gap.
The third part of this program was to list and print all the possible next states reachable from a current state. For example, if you have 3 bottles [1,1,1], one possible next state is [0,1,1] as the first bottle was knocked down. So this predicate is supposed to print a list of all possible next states, again represented by * and space.
I have successfully done the above, and this is my code so far:
printstate([]) :- nl.
printstate([B|L]) :- (B=0 -> write(' '); write('*')), printstate(L).
next([1|S], [0|S]).
next([1,1|S], [0,0|S]).
next([0|S], [0|T]) :- next(S,T).
next([1|S], [1|T]) :- next(S,T).
printnextstates(S,T) :- next(S,T), printstate(T), fail.
The next bit is the bit I am stuck on! So the aim is to define the value of a state to be 1 if the first player to move can force a win, and 0 otherwise. I am to write a predicate value(S,X) which will compute the value X of any game state S by a depth-first search through the game tree. I should use assert if possible to avoid the search exploring any position more than once.
I have no idea how to do this!
So far this is what I can come up with:
value(S,X) :- S = 1, ([S,T] = 1); 0.
But I'm sure it's not that simple, yet I have no idea how to begin this question! I've researched depth-first search but I don't really understand it enough to write this... does anyone have any idea how one may go about in starting this question?
Any help is much appreciated!
Since a player is free to pick elements at any position, a remarkable simplification results from an appropriate data representation of the problem: let's say that we keep a list of available bottles. Then you could do just declaring the logic:
win(P) :-
move(P, P1),
\+ win(P1).
win(P) :- move(P, []).
move([_|R], R).
move([_,_|R], R).
Here a dumb test, up to some arbitrary length, without noting that a simple recurrence formula gives the result
?- setof(N, L^(between(1,20,N),length(L,N),win(L)), Wins).
Wins = [1, 2, 4, 5, 7, 8, 10, 11, 13|...].

Resources