I am trying to execute a loop until the output of the predicate is false using Prolog. I am implementing the "Wordle" game and here are some of the relevant predicates I am using:
available_length(L):-
word(W,_), %Passing through every word W resulting from predicate word(W,C).
atom_chars(W,Y), %Adding the letters of the word W to list Y.
length1(Y,L). %Finding the length of the list.
length1([],0).
length1([_|Tail],L):-
length1(Tail,Prev),
L is Prev + 1.
The previous predicate basically takes as an input an integer and checks whether there exists a word in the predicate word(W, C) having a length equal to that integer(where W is the word and C is the category). Shown below is the Knowledge Base of this predicate:
word(horse,animals).
word(panda,animals).
word(hello,greetings).
word(banana,fruits).
word(bison,animals).
word(hoard,collections).
This is how the "available_length" predicate works:
available_length(L): succeeds if there are words in the KB with length L.
Examples:
?- available_length(5).
true. %becuase there exists words with length 5 in the KB(horse,panda,hello,bison,hoard).
?- available_length(9).
false. %becuase there don't exist words with the length 9 in the KB.
As the game starts, it will ask the user to choose a category as well as a length for a word of his choice. Then based on what he chooses, a random word from the category he entered is chosen and the game starts giving the user a number of guesses for the word equal to the (length of the word + 1). However, if the user enters a length of a word that doesn't exist in the category he chooses, he should be prompted with a message saying that the length he choose doesn't exist and is allowed to choose again until he enters a length that exists.
I implemented another helper predicate shown at the bottom that uses the above predicate "available_length" to check whether the user entered a proper length that exists or not and if he doesn't, he should be shown a prompt message as mentioned above.
And this is the relevant part of the of the predicate that executes the game:
play:-
write('The available categories are: '), %Not implemented yet.
nl,
write('Choose a category: '),
nl,
read(Category),
nl,
write('Choose a length: '),
nl,
read(WordLength), %Takes the length as an input from the user.
check_length, %Executes the helper predicate"check_length"shown below.
Guesses is WordLength + 1, %The rest is executed only if "check_length" succeeds.
write('Game started. You have '),
write(Guesses),
write(' guesses.'),
The question is how do I keep prompting the user with the message until the output of the "available_length" predicate becomes true (meaning that the user entered a correct length). I already tried the following helper predicate, but it didn't work, it prompts the message whether I enter a length that exists or not:
check_length:-
read(WordLength),
(available_length(WordLength) = true; %Loops until the length entered does exist.
(write('There are no words with this length.'), %Otherwise,this prompt message appear.
nl,
write('Choose a length: '), %The user is allowed to choose a length again.
check_length)
).
Does anybody have a clue about how it is done? Another question is how to execute a predicate that chooses a random word in the category that the user chooses from the predicates of the KB. For example, if he chooses the "animals" category, one of the three words: horse, panda, and bison should be chosen randomly). The categories are shown again below:
word(horse,animals).
word(panda,animals).
word(hello,greetings).
word(banana,fruits).
word(bison,animals).
word(hoard,collections).
To check the availability of a word of a given length, you need to know which category it belongs to. Also, to determine the length of an atom, you can use the ISO predicate atom_length/2
:
available_length(Category, Length) :-
( word(Word, Category),
atom_length(Word, Length)
-> true ).
To read a term entered by the user, you can use the predicate:
input(Prompt, Term) :-
write(Prompt),
read(Term).
To repeat the input until a valid word category is entered, use the predicate:
input_category(Category) :-
( input('Choose a category:\n', Category),
word(_, Category)
-> true
; write('Invalid category!\n'),
input_category(Category) ).
Example:
?- input_category(C).
Choose a category:
|: planets.
Invalid category!
Choose a category:
|: animals.
C = animals.
To repeat the input until a valid word length is entered, for a given category, use the predicate:
input_length(Category, Length) :-
( input('Choose a length:\n', Length),
available_length(Category, Length)
-> true
; format('Category "~w" has no word with this length.\n', [Category]),
input_length(Category, Length) ).
Example:
?- input_length(animals, L).
Choose a length:
|: 9.
Category "animals" has no word with this length.
Choose a length:
|: 6.
Category "animals" has no word with this length.
Choose a length:
|: 5.
L = 5.
To choose a random word from a given category, you can use the predicate random_member/2
:
random_word(Category, Word) :-
findall(Word, word(Word, Category), Words),
random_member(Word, Words).
Examples:
?- random_word(animals, W).
W = panda.
?- random_word(animals, W).
W = horse.
?- random_word(animals, W).
W = bison.
SWI-Prolog defines random_member/2
as follows:
random_member(X, List) :-
must_be(list, List),
length(List, Len),
Len > 0,
N is random(Len),
nth0(N, List, X).