Page 1 of 4

Writing answers instead of choosing menu options [EDITED]

Posted: Mon Apr 22, 2013 9:49 pm
by xavimat
EDITED CODE.

A simple function that recognizes the words written by the player and can be used as a substitute of the menus.
It's inspired in fiorica's "The Doll's Stories" and uses some code from SusanTheCat's "Thera'Py".

Simply add the file 'reply.rpy' to the game directory and use the 'reply()' function to make questions to the player.
The functions needs a question (a string) and a list of possible answers (only simple words), and returns one of the words given.
If the player doesn't respond any of the options, the function presents a warning message, and repeats itself.
The function reply2() returns the list of valid keywords found in the player's answer.

An example:

Code: Select all

$ r = reply("Where can we go?", "park, beach, home")
if r == 'park':
    # do something
elif r == 'beach':
    # do something
else:
    # do something

$ r2 = reply2("Where can we go?", "park, beach, home")
if "park" in r2:
    e "You have chosen park"
if "beach" in r2:
    e "You have chosen beach"
if "home" in r2:
    e "You have chosen home"

$ l = len(r2)   
if l > 2:
    e "You have chosen too much."   
    
#Note that with reply2() we don't use "elif" but separated "if" because all of the keywords can be found. With "len()" you have the total of keywords found in the user's reply.
Here the code of 'reply.rpy':

Code: Select all

# Reply and Reply2
# by xavimat (cc-by, 2013)
# inspired by fiorica's "The Doll's Stories" and SusanTheCat's "Thera'Py"
# improved with help of jw2pfd

init python:

    def reply(question = "Yes or no?",
        answers = "yes,no",
        invalid_character = None,
        invalid_answer = "(Invalid answer)",
        show_answers = True):

        thequestion = question
        found_it = ""
        ans = answers.replace(" ", "")
        ans = ans.split(",")
                       
        if show_answers:
            thequestion += " {size=-10}("
       
            for element in ans:
                thequestion += element + ', '
            thequestion = thequestion[:-2] + "){/size}"           
       
        while found_it == "":
           
            phrase = renpy.input(thequestion)
            phrase = phrase.lower()       

            for chara in ',.;:!?-+*()[]{}&%$':
                phrase = phrase.replace(chara, ' ')           
            phras = phrase.split()
       
            for element in ans:
                for theword in phras:
                    if found_it == '' and element == theword:
                        found_it = element
                       
            if found_it == "":
                renpy.say(invalid_character, invalid_answer)

        return found_it

    def reply2(question = "Yes or no?",
        answers = "yes,no",
        invalid_character = None,
        invalid_answer = "(Invalid answer)",
        show_answers = True):

        thequestion = question
        found_it = [ ]
        ans = answers.replace(" ", "")
        ans = ans.split(",")
                       
        if show_answers:
            thequestion += " {size=-10}("
       
            for element in ans:
                thequestion += element + ', '
            thequestion = thequestion[:-2] + "){/size}"           
       
        while found_it == [ ]:
           
            phrase = renpy.input(thequestion)
            phrase = phrase.lower()       

            for chara in ',.;:!?-+*()[]{}&%$':
                phrase = phrase.replace(chara, ' ')           
            phras = phrase.split()
       
            for element in ans:
                for theword in phras:
                    if element == theword and element not in found_it:
                        found_it.append(element)
                       
            if found_it == [  ]:
                renpy.say(invalid_character, invalid_answer)

        return found_it
The function takes five arguments. 'invalid_character' and 'invalid_answer' are used if the warning message is said by a character. In this example, we have Eileen (defined as the character 'e'), saying the warning message:

Code: Select all

$ r = reply("Where do you want to go?", "park, beach, home", e, "Can you repeat your answer, please?")
The code for 'reply.rpy' and an example of 'script.rpy' can be downloaded here. Put the two files in a newly created game:
reply.zip
reply.rpy, script.rpy. Edited code.
(2.6 KiB) Downloaded 278 times
Additionally, jw2pfd has made another code that does essentially the same thing, with some features added like synonyms recognition. You can found his code and explanation here: http://lemmasoft.renai.us/forums/viewto ... 45#p288203.

Every suggestion will be appreciated.

Re: Writing answers instead of choosing menu options

Posted: Fri Apr 26, 2013 12:03 pm
by TrickWithAKnife
Do you use IRC? I'd been working on some ideas a few weeks ago (inspired by the same projects), and perhaps some of what I've done could be of use to you.

Some of the features I've got working include:
  • A thesaurus for understanding words with similar meanings.
    "I want to visit the seaside."
  • Being able to process the grammar a little more. For example, being able to recognise negative sentences.
    "I don't feel like going to the beach. Let's go somewhere else."
  • Assigning properties to objects.
    "I can't eat the beach, it's not food!"
What I have is pretty incomplete though, but it does work.

Re: Writing answers instead of choosing menu options

Posted: Fri Apr 26, 2013 3:39 pm
by Jod
Hey, this is pretty interesting! I´ve made something similar for my game myself, including:

-Timed input (x seconds to give input)
-Synonyms (punch equals jab, equals.... etc. etc. etc.)
-Story-specific things

My code is nowhere close to being easily exportable, I'm afraid. That was still on the to-do list :). If I have some free time I'm going to see how this runs and I'll let you know!

Re: Writing answers instead of choosing menu options

Posted: Sun Apr 28, 2013 9:50 am
by xavimat
Thanks TrickWithAKnife and Jod.

@TrickWithAKnife: I write in several languages, and I'm not sure if that grammar processing will be easy to "translate". Anyway, I'm interested in it. PM me, if you want. I'm not sure about IRC.

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 4:57 am
by animedaisuki
hi could you give an example of doing fuzzy matching (as opposed to exact matching) with this code? or is the function not included?

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 5:37 am
by TrickWithAKnife
There are different methods for getting the "fuzziness".

Some possible ways could be:
1. Simplify vocabulary to their grammatical functions.
2. Use a thesaurus to change words to understood forms.
3. Completely ignore grammar, and hope you pick up enough keywords to get the general meaning.
4. Some bizarre combination of the above methods, or something else.

I think method 3 is generally the most common, but adding method 2 isn't so difficult, and greatly increases the chances of recognising words.

Method 1 is challenging, but would get the most accurate responses. I wouldn't recommend this method, especially with a messy language like English. And I'm saying this as an English teacher.

@xavimat: I'm not sure what to PM.
I generally avoid PMing people, because it kills the flow of conversation, and I'm unreliable at replying.

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 5:44 am
by animedaisuki
yeah, my plan was to do #3, since it's the simplest... but how do i do it with the code they have here? as u probably can see, i don't know how those codes work at all...

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 6:36 am
by TrickWithAKnife
Sometimes you need to reverse-engineer sentences and their responses.
For example, think about what kind of sentences the user is likely to enter, and what kind of responses you want them to see. Figuring out the logic first is a lot better than trying to code it at the same time.
This kind of thing isn't the sort of thing people throw together in an afternoon, and being a proficient programmer isn't enough.

I hope Xavimat, Jod, anyone else who is working on something like this and I can put our heads together and combine our efforts.

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 7:02 am
by xavimat
@animedaisuki: The code I posted is really really simple. It's only a loop:
1. When the 'reply()' function is called, you must include the possible answers that will be recognized. These must be simple words (can't be expressions with spaces). You can include as many words as you want.
2. The function asks the user for an input, and the user writes something.
3. And finally, the function makes a loop: For any word in the 'possible answers list' searches if that word is found in the users' input. If it's found, then that will be the return of the reply() function. And all the other words will be ignored. If not, it tries with the next possible answer.
4. If none of the 'possible answers' is found, the reply() function starts again, asking for a new input.

There is, though, no grammar involved. It's a simple 'find the first word that match' function.

The story can continue with an if-elif-else statement (as shown in the OP). The reply() function will repeat itself forever while no correct word is found in the input (because of that, the possible answers should be given as a hint to the user).

@TrickWithAKnife: Can you show us (or PM me) the code that recognizes negative sentences? Thanks.

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 8:38 am
by TrickWithAKnife
I just have a list of negative words that can be run through a thesaurus. If they are found in the sentence, a variable named is_the_sentence_negative changes. Then this is referenced when generating responses. There's no reason why it can't check the position of the negative words in the sentence though, for more exact understanding.

I'll post part of the code when I get home, anyway. I have no problems sharing all my code, but as its all WiP, it's probably unreadable to other people.

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 12:36 pm
by TrickWithAKnife
I took the liberty of messing a little with your code, to crowbar in checking for negative words. I haven't actually tested this new code (or your original), so if it doesn't work, it's because I'm lazy.

Also, it doesn't actually change the responses (although that is easy to do). It just changes the value of one variable.

Also, unless I'm mistaken, it looks like your function stops processing the user's input the moment it finds a keyword. That might be okay in some cases, but not if the coder wants to check for more than one particular word, or checking the sentence's meaning. For example "The beach is okay, but the park is best" would show up as "beach" being the desired destination. But again, perhaps I misread.

I'm also curious about whether your code changes
,.;:!?-+*()[]{}&%$
into spaces. If so, that could cause problems with apostrophes, so may be safer to just remove them entirely.

Code: Select all

# Reply
# by xavimat (cc-by, 2013)
# inspired by fiorica's "The Doll's Stories" and SusanTheCat's "Thera'Py"

init python:
    
    def reply(question = "Yes or no?", 
        answers = ["yes", "no"], 
        invalid_character = None, 
        invalid_answer = "(Invalid answer)", 
        show_answers = True):

        ## Change this to whatever the thesaurus is called ##
        definitions = english_thesaurus ##
        was_the_sentence_positive_or_negative = "+" ##

        
        thequestion = question        
        if show_answers:
            thequestion += " {size=-10}("
            for element in answers:
                thequestion += element + ', '
            thequestion = thequestion[:-2] + "){/size}"            
        phrase = renpy.input(thequestion)
        phrase = phrase.lower()        

        for chara in ',.;:!?-+*()[]{}&%$':
            phrase = phrase.replace(chara, ' ')            
        pre_phras = phrase.split() ##
        ## This next line checks the thesaurus for matching words, and changes them. ##
        phras = [ definitions[word] if word in definitions else word for word in pre_phras] ##
        found_it = ''
        for element in answers:
            for theword in phras:

                ## Just to make a note if there is a negative word in the sentence. Do with that whatever you like. ##
                if element == "NEGATIVE": ##
                    was_the_sentence_positive_or_negative = "-" ##

                if found_it == '' and element == theword:
                    found_it = element   
   

              
        while found_it == '':
            renpy.say(invalid_character, invalid_answer)
            found_it = reply(question, answers, invalid_character, invalid_answer, show_answers)            
            
        return found_it
Also, this bad boy is an example of a very short thesaurus:

Code: Select all

   
    english_thesaurus = {
        "isnt" : "NEGATIVE", 
        "not" : "NEGATIVE", 
        "dont" : "NEGATIVE", 
        "cant" : "NEGATIVE", 
        "wont" : "NEGATIVE", 
        "shouldnt" : "NEGATIVE", 
        "mustnt" : "NEGATIVE", 
        "wouldnt" : "NEGATIVE", 
        "couldnt" : "NEGATIVE", 
        "didnt" : "NEGATIVE", 
        "no" : "NEGATIVE", 
        "arent" : "NEGATIVE", 
        "wasnt" : "NEGATIVE", 
        "banned" : "NEGATIVE", 
        "prohibited" : "NEGATIVE", 
        "disallowed" : "NEGATIVE"}
Hope you don't mind me posting this here (can move it if you prefer).

Re: Writing answers instead of choosing menu options

Posted: Tue May 07, 2013 2:44 pm
by xavimat
Thanks for the suggestions, TrickWithAKnife.
TrickWithAKnife wrote:Also, unless I'm mistaken, it looks like your function stops processing the user's input the moment it finds a keyword. That might be okay in some cases, but not if the coder wants to check for more than one particular word, or checking the sentence's meaning. For example "The beach is okay, but the park is best" would show up as "beach" being the desired destination. But again, perhaps I misread.
You are right. The function only checks for the first word that match, and then returns it. It wouldn't be difficult to check for more than one word and then complain about "ambiguity" and ask the user for a new answer. (For example, in a game with the user talking to a dump computer.)
TrickWithAKnife wrote:I'm also curious about whether your code changes
,.;:!?-+*()[]{}&%$
into spaces. If so, that could cause problems with apostrophes, so may be safer to just remove them entirely.
I didn't include the quotes in this line (single neither double) to avoid problems with apostrophe. I've made the test and it works with answers with apostrophes ("don't", etc.)
TrickWithAKnife wrote:Hope you don't mind me posting this here (can move it if you prefer).
It's OK. I'm learning a lot.

Re: Writing answers instead of choosing menu options

Posted: Wed May 08, 2013 12:35 am
by TrickWithAKnife
I noticed in your example, you check for certain words, and have pre-defined responses each time.

I wonder if it would be useful to include some set responses in addition. For example, if someone uses an offensive word, the response could be something about not using offensive language, or if the user's input starts with a question word and contains a question mark, then say something like "Hey, I'm asking the questions here."

I should be careful. I have loads of ideas, but each one makes things more complicated.
Also different methods are better for different situations. I don't think there is a best way for all situations.

Re: Writing answers instead of choosing menu options

Posted: Wed May 08, 2013 10:10 am
by xavimat
My first idea was inspired by fiorica's "The Doll's Stories" where the user can write (as 'speaking' to the dolls characters), but that doesn't change the path (it doesn't work as a menu). I'm not sure if the responses that the user writes have an actual influence in the game (do they raise stats or something?). Anyway, I enjoyed a lot the possibilities of that as a game.

That was the initial purpose of this function, a simulation game were the user "simulates" saying something instead of "clicking" a box with a word written in it, as in the usual menus.

Of course, that can be extended with many possibilities (thesaurus, negation recognition, grammar processing, offensive language detection...), so, they are welcome. :)

Re: Writing answers instead of choosing menu options

Posted: Wed May 08, 2013 11:26 am
by TrickWithAKnife
I talked with Fiorica quite a bit about Dolls' text system while I was messing around with my own, and I think we seemed quite keen on a more flexible system too.

I actually have 2 or 3 different methods that I was working with, but each has disadvantages.