Related
I follow the book Problem solving with Prolog by John Stobo. I've studied the Chapter 1 (Programming with Facts) and Chapter 2 (Programming with Rules) Now I am at Chapter 3: Recursion in Rules and I'm practicing the program given in the Section 3.1. I've elobarated the program a bit (without changing the main structure) and added my own functor (or function or rule ?) named is_rank_lower/2 but it doesn't work as expected.
When I enter (or ask Prolog)
is_rank_lower(ryan, jondoe).
the output is
false.
the expected output: true.
Because ryan is a private and jondoe is a corporal and private is lower in rank than corporal.
The explanations are in the code.
Question #1: How to make my own functor is_lower_rank work as expected?
Question #2: This question might be related to the book because when I write down the program exactly as it is, it works slightly wrongly and that might be causing my own functor to function wrongly, too. Just a guess.
When I enter:
lower_rank(private, corporal).
Prolog returns with true and waits at it, I have to put a dot after the true and click enter only then does it return to the ?- prompt.
The expected output is:
return with true. then return to the ?- prompt
The author seems to talk about this problem. In page 57 he writes "lower_rank would not terminae if the goal ought to fail" I've applied all the instructions but the functor still doesn't work. How to make it work?
My prolog version swi-prolog 7.2.0
% John Stobo, problem solving with Prolog, March.1989
% FACTS:
next_degree(private, corporal).
next_degree(corporal, sergeant).
next_degree(sergeant, lieutenant).
next_degree(lieutenant, captain).
next_degree(captain, major).
next_degree(major, "lieutenant colonel").
next_degree("lieutenant colonel", colonel).
next_degree(colonel, "brigadier general").
next_degree("brigadier general", "major general").
next_degree("major general","lieutenant general").
next_degree("lieutenant general", general).
soldier(ryan, private).
soldier(jondoe, corporal).
sooldier(smartson, captain).
% RULES:
lower_rank(R1, R2) :-
next_degree(R1, R2).
lower_rank(R1, R2) :- % this works but if
next_degree(R1, R3), % the result is "true"
lower_rank(R3, R2). % it doesn't end properly
% only if the user types a dot, it ends properly
is_rank_lower(A1,A2) :-
lower_rank(soldier(A1,X), soldier(A2,X)).
% doesn't work because the functors are inserted as
% 'soldier(ryan, _G1471), soldier(jondoe, _G1471))
% not as private, corporal, i.e. they are not evaluated
That next_degree/2 seems like a bizarre method - is the book suggesting it as sensible, or as an example of what not to do?
There are decent books at https://swi-prolog.discourse.group/t/useful-prolog-references/1089
This works:
% First argument is an atom, hence single quotes in swi-prolog
rank_order(private, 1).
rank_order(corporal, 2).
rank_order(sergeant, 3).
rank_order(lieutenant, 4).
rank_order(captain, 5).
rank_order(major, 6).
rank_order('lieutenant colonel', 7).
rank_order(colonel, 8).
rank_order('brigadier general', 9).
rank_order('major general', 10).
rank_order('lieutenant general', 11).
soldier(ryan, private).
soldier(jondoe, corporal).
% Not mis-spelled as "sooldier"
soldier(smartson, captain).
rank_lower(RankLower, RankUpper) :-
rank_order(RankLower, RankLowerOrder),
rank_order(RankUpper, RankUpperOrder),
RankLowerOrder < RankUpperOrder.
soldier_rank_lower(SoldierLower, SoldierUpper) :-
soldier(SoldierLower, RankLower),
soldier(SoldierUpper, RankUpper),
rank_lower(RankLower, RankUpper).
Results in swi-prolog:
?- rank_lower(private, corporal).
true.
?- soldier_rank_lower(ryan, jondoe).
true.
?- soldier_rank_lower(L, U).
L = ryan,
U = jondoe ;
L = ryan,
U = smartson ;
L = jondoe,
U = smartson ;
false.
2nd attempt:
rank_next(private, corporal).
rank_next(corporal, sergeant).
rank_next(sergeant, lieutenant).
rank_next(lieutenant, captain).
rank_next(captain, major).
rank_next(major, 'lieutenant colonel').
rank_next('lieutenant colonel', colonel).
rank_next(colonel, 'brigadier general').
rank_next('brigadier general', 'major general').
rank_next('major general', 'lieutenant general').
rank_next('lieutenant general', general).
soldier(ryan, private).
soldier(jondoe, corporal).
soldier(smartson, captain).
rank_lower(RankLower, RankUpper) :-
rank_next(RankLower, RankLower1),
% Increase lower to eventually meet with upper
( RankLower1 = RankUpper ;
rank_lower(RankLower1, RankUpper)
).
soldier_rank_lower(SoldierLower, SoldierUpper) :-
soldier(SoldierLower, RankLower),
soldier(SoldierUpper, RankUpper),
% Won't have multiple answers
once(rank_lower(RankLower, RankUpper)).
This makes the following deterministic:
?- soldier_rank_lower(ryan, jondoe).
true.
... whilst keeping the generality of rank_lower/2, i.e.:
?- findall(L-U, rank_lower(L, U), Pairs), length(Pairs, Len).
Pairs = [private-corporal,private-sergeant,private-lieutenant, ...
Len = 66.
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.
I want to create a predicate that only accepts a specific input from the user and it will keep asking for the right input if the user gives the wrong input.
I've created this, but its not completed because it does not ask for the new input if its wrong:
askchar(X):- write('give char'),nl, get_char(X), test(X).
test(X):- X=a, write('ok'). %accepts a
test(X):- X='1', write('ok'). %accepts 1
test(X):- write('wrong input. try again'),nl.
in systems lacking a decent tail recursive optimization, processing side effects can be conveniently done with a failure driven loop
1 ?- [user].
|: askchar(C) :- repeat, get(C), (C = 0'a ; C = 0'1, ! ; fail).
% user://1 compiled 0.07 sec, 2 clauses
true.
2 ?- askchar(X).
|: 5
|: a
X = 97 .
Here is what I got:
askChar(Char) :- get_char(Char), test(Char), write('This is the right char, thank you.'), !.
askChar(Char) :- write('That is wrong char!'), askChar(Char).
test(s).
It asks again and again until the char s is typed in.
I'm trying to make a simple hangman game in SWI Prolog.
Since we made this program run can you help me enchance the program with the following:
1) By keeping up with the letters that have been guessed so far. If the user guesses a letter that has already been guessed, the program should say 'You guessed that!' and just continue the game.
2) Lastly, add a counter that counts the number of incorrect guesses and quits the game when a certain number is reached. The program should tell the user that they lose, display what the phrase really was, and terminate. Duplicate guesses should not be counted as wrong.
I would like to thank everyone who helped me so far. This means a lot to me.
I provide you with the code and comments.
% This top-level predicate runs the game. It prints a
% welcome message, picks a phrase, and calls getGuess.
% Ans = Answer
% AnsList = AnswerList
hangman:-
getPhrase(Ans),
!,
write('Welcome to hangman.'),
nl,
name(Ans,AnsList),
makeBlanks(AnsList, BlankList),
getGuess(AnsList,BlankList).
% Randomly returns a phrase from the list of possibilities.
getPhrase(Ans):-
phrases(L),
length(L, X),
R is random(X),
N is R+1,
getNth(L, N, Ans).
% Possible phrases to guess.
phrases(['a_picture_is_worth_a_thousand_words','one_for_the_money','dead_or_alive','computer_science']).
% Asks the user for a letter guess. Starts by writing the
% current "display phrase" with blanks, then asks for a guess and
% calls process on the guess.
getGuess(AnsList, BlankList):-
name(BlankName, BlankList),
write(BlankName),
nl,
write('Enter your guess, followed by a period and return.'),
nl,
read(Guess),
!,
name(Guess, [GuessName]),
processGuess(AnsList,BlankList,GuessName).
% Process guess takes a list of codes representing the answer, a list of codes representing the current
% "display phrase" with blanks in it, and the code of the letter that was just guessed. If the guess
% was right, call substitute to put the letter in the display phrase and check for a win. Otherwise, just
% get another guess from the user.
processGuess(AnsList,BlankList,GuessName):-
member(GuessName,AnsList),
!,
write('Correct!'),
nl,
substitute(AnsList, BlankList, GuessName, NewBlanks),
checkWin(AnsList,NewBlanks).
processGuess(AnsList, BlankList,_):-
write('Nope!'),
nl,
getGuess(AnsList, BlankList).
% Check to see if the phrase is guessed. If so, write 'You win' and if not, go back and get another guess.
checkWin(AnsList, BlankList):-
name(Ans, AnsList),
name(BlankName, BlankList),
BlankName = Ans,
!,
write('You win!').
checkWin(AnsList, BlankList):-
!,
getGuess(AnsList, BlankList).
% getNth(L,N,E) should be true when E is the Nth element of the list L. N will always
% be at least 1.
getNth([H|T],1,H).
getNth([H|T],N,E):-
N1 is N-1,
getNth(T,N1,E1),
E=E1.
% makeBlanks(AnsList, BlankList) should take an answer phrase, which is a list
% of character codes that represent the answer phrase, and return a list
% where all codes but the '_' turn into the code for '*'. The underscores
% need to remain to show where the words start and end. Please note that
% both input and output lists for this predicate are lists of character codes.
% You can test your code with a query like this:
% testMakeBlanks:- name('csc_is_awesome', List), makeBlanks(List, BlankList), name(Towrite, BlankList), write(Towrite).
makeBlanks(AnsCodes, BlankCodes) :-
maplist(answer_blank, AnsCodes, BlankCodes).
answer_blank(Ans, Blank) :-
Ans == 0'_ -> Blank = Ans ; Blank = 0'* .
% substitute(AnsList, BlankList, GuessName, NewBlanks) Takes character code lists AnsList and BlankList,
% and GuessName, which is the character code for the guessed letter. The NewBlanks should again be a
% character code list, which puts all the guesses into the display word and keeps the *'s and _'s otherwise.
% For example, if the answer is 'csc_is_awesome' and the display is 'c*c_**_*******' and the guess is 's', the
% new display should be 'csc_*s_***s***'.
% You can test your predicate with a query like this:
% testSubstitute:- name('csc_is_awesome', AnsList), name('c*c_**_*******', BlankList), name('s',[GuessName]), substitute(AnsList, BlankList, GuessName, NewBlanks),
% name(Towrite, NewBlanks), write(Towrite).
% Also, since the predicate doesn't deal directly with character codes, this should also work:
% substitute(['c','s','c'],['c','*','c'],'s',L). L should be ['c','s','c'].
substitute(AnsCodes, BlankCodes, GuessName, NewBlanks) :-
maplist(place_guess(GuessName), AnsCodes, BlankCodes, NewBlanks).
place_guess(Guess, Ans, Blank, Display) :-
Guess == Ans -> Display = Ans ; Display = Blank.
maplist/3 & maplist/4 apply their first argument (a predicate of appropriate arity) against all elements of other arguments lists, then your makeBlanks could be:
makeBlanks(AnsCodes, BlankCodes) :-
maplist(answer_blank, AnsCodes, BlankCodes).
answer_blank(Ans, Blank) :-
Ans == 0'_ -> Blank = Ans ; Blank = 0'* .
and substitute:
substitute(AnsCodes, BlankCodes, GuessName, NewBlanks) :-
maplist(place_guess(GuessName), AnsCodes, BlankCodes, NewBlanks).
place_guess(Guess, Ans, Blank, Display) :-
Guess == Ans -> Display = Ans ; Display = Blank.
edit:
on additional requests: 1) can be solved with an additional predicate:
alreadyGuessed(Guess, AnsCodes) :-
memberchk(Guess, AnsCodes).
while regards 2) getGuess and processGuess together make a loop, that will just terminate when no more calls happen. Remove the last rule of checkWin, add an argument as counter to keep track of failed guesses, and extend processGuess to signal failure:
processGuess(AnsList, BlankList, _, CountFailed) :-
( CountFailed == 5
-> format('Sorry, game over. You didn\'t guess (~s)~n', [AnsList])
; write('Nope!'),
CountFailed1 is CountFailed + 1,
getGuess(AnsList, BlankList, CountFailed1)
).
Why so many cuts? Check out SWI library predicates that may be useful to you: memberchk/2, format/2 and nth1/3.
this is the facts I entered in the knowledge base and average takes a list and returns the result but when i pose the query
"average([],X)."
it returns X=0 then when i press ; it gives me zero divisor error and I dont understand why,I tried posing the following 4 facts in the KB
average(0,0).
average([],0).
average(0/0,0).
average(0,0/0).
I'm not sure what you trying to achieve by writing 0/0 (as a matter of fact I'm not sure what any of the facts other than average([],0). are there for), but clearly dividing 0 by 0 will cause a division by zero error.
So that's your problem. Remove the occurrences of 0/0 and the error will disappear.
what is the code of average/2?
assuming that the current code is:
average(L,X):-
sumlist(L,Sum),
length(L,N),
X is Sum/N.
then you should enter the special case like this:
average([],0).
average(L,X):-
sumlist(L,Sum),
length(L,N),
X is Sum/N.
this will have the behavior you described: "when I try posing the query average([],X). it returns X=0 which is true but I can still press ; which gives a 0/0 division error .. "
to avoid the second error you should prevent prolog from continuing to the second clause if the list is empty.
you can do that either with a cut:
average([],0):-!.
average(L,X):-
sumlist(L,Sum),
length(L,N),
X is Sum/N.
or by checking the length of the list before dividing
average([],0):-!.
average(L,X):-
sumlist(L,Sum),
length(L,N),
N>0,
X is Sum/N.
I can't comment on thanosQR's answer (insufficient rep) but you can avoid the cuts by pattern matching:
average([], 0).
average([H|T], X):-
sumlist([H|T], Sum),
length([H|T], N),
X is Sum / N.
or using the if -> then ; else construct:
average(L, X):-
( L = [] ->
X = 0
; sumlist(L, Sum),
length(L, N),
X is Sum / N
).