Lemma Soft Forums

Supporting creators of visual novels and story-based games since 2003.


Visit our new games list, blog aggregator, IRC, and wiki.
Activation problem? Email [email protected]
It is currently Sat May 18, 2013 5:06 pm

All times are UTC - 5 hours [ DST ]


Forum rules


Ask questions about one topic per thread, and use a descriptive subject. "NotImplemented error in script.rpy" is a good subject, "Tom's problems" is not. Remember to include all of traceback.txt or error.txt when reporting a problem, as well as the relevant lines of script. Use the [code] tag to format scripts.



Post new topic Reply to topic  [ 30 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Apr 16, 2012 11:50 pm 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
Hey there! I'm brand-new to Ren'Py, and coding all of this in general. I've been trying to figure out how to go about assigning point values to certain characters, and how to decide a winner out of those candidates. And further, how to solve a tiebreaker.

Here's what I've been trying to do:
I have about seven characters that the protagonist can choose from. I planned on assigning point values for favorable answers to questions, which is easy enough. And I learned that using 'if' and 'elif' codes, I can single out a specific person for the protagonist to continue to woo. But my question is this - what happens if two characters end up having the same point value by the time one must be chosen?

I've been reading over THIS tutorial, but it isn't much help; it only shows me what I've already learned regarding the if / elif statements and trying to make one choice over others or several simultaneous choices together.

Is there a way to make a statement that says, "in the case of a tie, allow the player to choose between the variables," and without having to make a very long list of "if A == B > max(C, D, E, F, G):" and "menu: "A": $ A +=1 " etc to choose between every single individual potential tie that exists?

So confusing!


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 1:23 am 
Veteran
User avatar

Joined: Mon Aug 09, 2010 10:02 pm
Posts: 237
Location: California, USA
Projects: Eastern Starlight Romance, Touhou Mecha
Organization: Dai-Sukima Dan
Here's one way to figure out which people have the highest number of points.
Code:
python:
    points = {
        'Person A': person_a_points,
        'Person B': person_b_points,
        'Person C': person_c_points,
        'Person D': person_d_points,
        'Person E': person_e_points,
        'Person F': person_f_points,
        'Person G': person_g_points,
    }
    highest_points = max(points.values())
    people_with_highest_points = []
    for person, person_points in points.iteritems():
        if person_points == highest_points:
            people_with_highest_points.append(person)
       
if len(people_with_highest_points) > 1:
    # Let the player choose between multiple people.
else:
    # No ties.

Notes:
- points is a dictionary. This Python data structure makes it easier to keep the connections between the characters and their point values.
- people_with_highest_points is a list. If the people tied with the highest number of points turned out to be A, C, and G, then this list's value would end up being ['Person A', 'Person C', 'Person G'].
- When you have several lines of Python code in a row like this, it's often cleaner to put them in a python: block instead of putting $ in front of every line.

There's probably more work to be done after this, such as actually constructing the menu choices once you know there's a tie. So feel free to ask again if you're unsure about that.

_________________
Dai-Sukima Dan blog


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 11:57 am 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
Ah, excellent, you are such an amazing help. :D Thank you so much!!

I do have another question, though; it deals specifically with this part of the code:

Code:
    highest_points = max(points.values())
    people_with_highest_points = []
    for person, person_points in points.iteritems():
        if person_points == highest_points:
            people_with_highest_points.append(person)

if len(people_with_highest_points) > 1:
    # Let the player choose between multiple people.
else:
    # No ties.


What do the various labels mean, such as 'len' or the 'points.iteritems,' etc? And once this happens in the game, how would the menu be constructed to go along with it? Would this portion of code automatically bring up the menu to decide the tiebreaker, or would I need to program that in elsewhere?

Oh, and for the different people, I assume 'Person A,' etc are labels that can be changed for each specific person. Would the 'person_a_points' be a variable set by me here, or one that automatically recalls the number of points based on the previous additions to code using the 'A +=1' sort of code earlier in the game? And that said, would the 'person_with_highest_points' bit immediately call up 'Person B' and 'Person C' if they were the two highest, and in a tie?

Thank you so much for your help so far, I really appreciate it!


Last edited by zomgenius on Tue Apr 17, 2012 12:13 pm, edited 1 time in total.

Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 12:09 pm 
Regular
User avatar

Joined: Sat Feb 04, 2012 9:46 pm
Posts: 34
len is short for length : it is a function that return the length of the list that is passed as a parameter (here, the length of the lists of persons with the highest number of points.

points.iteritems() is an iterator. An iterator is an element that let you, as the name says, iterate over a collection - a collection being either a list [a,b,c], a dictonary {a:1, b:25, c:"zoo"} or a tuple (a,b,c). By passing the iterator as a parameter to the for ... in ... , it lets the program apply the same block of code to each of the values of the collection.

Here, for person, person_points in points.iteritems():
  • points is a dictionnary : it contains two values by item - the key (which let us access the item) and the value (which is... the value).
  • Thus, two variables, person and person_points are created to hold each of this value
  • Therefore, the line truely reads : "For each item in points, let's person and person_points hold the values of the two parts of the item (the key and the value).

Quote:
how would the menu be constructed to go along with it? Would this portion of code automatically bring up the menu to decide the tiebreaker, or would I need to program that in elsewhere?


What you need is the renpy.display_menu(...) function.
Quote:
renpy.display_menu (items, window_style='menu_window', interact=True, with_none=None):
Function: Displays a menu containing the given items, returning the value of the item the user selects.
items - A list of tuples that are the items to be added to this menu. The first element of a tuple is a string that is used for this menuitem. The second element is the value to be returned if this item is selected, or None if this item is a non-selectable caption.


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 12:25 pm 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
Hmm... Seems like there is a lot I need to read up on, then. This has helped to clear up a lot for me, but I'm still a bit confused by how the code works as a whole.

Valmoer, thank you for your explanations! I had edited this part into my previous post right before you replied:
Quote:
Oh, and for the different people, I assume 'Person A,' etc are labels that can be changed for each specific person. Would the 'person_a_points' be a variable set by me here, or one that automatically recalls the number of points based on the previous additions to code using the 'A +=1' sort of code earlier in the game? And that said, would the 'person_with_highest_points' bit immediately call up 'Person B' and 'Person C' if they were the two highest, and in a tie?


Could you help me out with this part as well? Thank you!!


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 12:44 pm 
Regular
User avatar

Joined: Sat Feb 04, 2012 9:46 pm
Posts: 34
Well, points is a dictionary : a dictionary is a collection whose access is handled by keys. Each item in a dictionary thus has two parts :
the key -> it identifies the wanted value.
the value -> pretty self explanatory.

In SleepKirby's example
Code:
    points = {
        'Person A': person_a_points,
        ...
        'Person G': person_g_points,
    }

'Person A', 'Person B', ..., 'Person G' (which are text strings) are the keys of the dictionary items.
person_a_points, person_b_points, ..., person_g_points are the items initialisation values.
If, just after the initialisation, you accessed points['Person B'], you'd have a value equal to person_b_points.
You could also
Code:
#change the value to a new value
points['Person B'] = newvalue

#modfiy the value (here, increment by one)
points['Person B'] += 1

Do note that values might not always be numbers, nor keys have to be text strings. Both can be any kind of object, though the string-keyed, number-valued is the most common.

Here is the official tutorial about dictionaries
The official python tutorial is awesome as a source of information anyway : do read it.

Quote:
And that said, would the 'person_with_highest_points' bit immediately call up 'Person B' and 'Person C' if they were the two highest, and in a tie?

That is actually, what SleepKirby example is all about :
Code:
    highest_points = max(points.values())
    people_with_highest_points = []
    for person, person_points in points.iteritems():
        if person_points == highest_points:
            people_with_highest_points.append(person)
       
if len(people_with_highest_points) > 1:
    # Let the player choose between multiple people.
else:
    # No ties.


Translated in everyday language
  • Let highest_points be the maximum of the values in points.
  • Let there be a list called people_with_highest_points.
  • For each key-value item in points, let person hold the key and person_points hold the value
  • ---If a person's points (person_points) is equal to the maximum value of points (highest_points)
  • ------Then append that person's name (person) to the list people_with_highest_points
  • If there is more than one person with the maximum number of points
  • ---Let the player decide (with the renpy.display_menu() function)
  • Else
  • ---There is no tie*

Always try to say your algorithms out loud, in natural english.


As an additional gift, the recipe for success in renpy (and computing in general (and even in life :))))
  1. Read docs and tutorial
  2. Practice
  3. Ask questions
  4. ????
  5. Profit!

*the tie is a lie**
** the tie is a cake***
*** bowtie are cool
**** mods are asleep, post ponies


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 1:13 pm 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
Valmoer, you are so fantastic. That helps me a ton!! :D I'm going to practice with this myself some, and I'll come back if I have more questions. But thank you so very much; this clears up a lot of questions already! One final little thing: would this example still function for if there is a key or more than one key (person) with a higher number of points (value) but not necessarily the maximum of points? So if Person A and Person B manage to get 8 / 10 points, placing them at the highest AND a tie?*

*ponies are always relevant


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 1:31 pm 
Regular
User avatar

Joined: Sat Feb 04, 2012 9:46 pm
Posts: 34
You misundertood the highest_points = max(points.values()) line. It doesn't return the 'maximum possible' (there's no such thing in computing (except if you go over the max value for an integer, which is 2^63 = 9.22337204 × 10^18 ** - so not gonna happen soon.)) It returns 'the maximum of the values in the dictionary points'*. So this function will always return the highest. As far as computing goes, maximum and highest are synonyms.
And that's how Equestria was made.


*actually, max(list) return the maximum of list's values , and points.values() (or any dictionnary.values()) return a list of the dictionnary values. So both at the same time do...
** 2^31 if you have a 32-bit computer. (Generally, for a N-bit architecture computer, 2^(N-1) for a signed integer, 2^N for an unsigned integer)


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 1:55 pm 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
Oh!! Hahah, I feel silly now. Okay, excellent! Thank you so much. :D I'll go practice some now, and then come back. :3 You're awesome!


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 3:50 pm 
Veteran
User avatar

Joined: Mon Aug 09, 2010 10:02 pm
Posts: 237
Location: California, USA
Projects: Eastern Starlight Romance, Touhou Mecha
Organization: Dai-Sukima Dan
Good stuff, Valmoer, nice explanations!

_________________
Dai-Sukima Dan blog


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 3:53 pm 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
Okay, so I was trying to practice with the whole thing, when I realized... how should I introduce the points in the beginning anyhow? I figured I should introduce it using something like the following:

Code:
$ hunger = 0
$ drink = 0
$ social = 0

label start
e "I wonder what I should do today!"
"She thought about her choices for a moment."

menu:
   "Get something to eat."
   $ hunger += 1
   "Get a glass of water."
   $ drink += 1
   "Call a friend."
   $ social += 1


Am I on the right track here?
And when using a menu like this, do I need to have each option then jump to a new label, or will it allow the player to continue on with the story directly after the choice is made and the variable has had a point added? And when should the entire bit of python code we've been discussing come into the script - at the end, when it is time to decide?

Next, at the end, how do I set up the renpy.displaymenu function? Will it automatically create a menu after I enter that code, or will I have to specify how the menu should look and what it should lead to? And further, does it allow the menu options to be changed in a way to show "I think I'll get another snack," or "I'd rather have another glass of water" instead of "hunger" or "drink," as per the example above?

Thank you so much for your help, and I hope I'm not too much trouble!!


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 4:06 pm 
Regular
User avatar

Joined: Sat Feb 04, 2012 9:46 pm
Posts: 34
Quote:
Am I on the right track here?
You're mostly right, but your code needs proper indentation :
Code:
hunger = 0
$ drink = 0
$ social = 0

label start:
   e "I wonder what I should do today!"
   "She thought about her choices for a moment."

menu:
   "Get something to eat.":
      $ hunger += 1
   "Get a glass of water.":
      $ drink += 1
   "Call a friend.":
      $ social += 1

Always a colon : at the end of a choice, label or menu statement, and all blocks that depends on that statement must be (evenly) indented.


Quote:
And when using a menu like this, do I need to have each option then jump to a new label, or will it allow the player to continue on with the story directly after the choice is made and the variable has had a point added?

Yes to the second part. Once a choice is made, the code under the block is executed, then the code execution continues at the end of the menu block.
The concept of block and indentation is crucial in python, and thus in renpy.

Quote:
And when should the entire bit of python code we've been discussing come into the script - at the end, when it is time to decide?

Exactly.
Though you can put it in a function (or a callable label) and call that function when you need, but that's for later :).


Quote:
Next, at the end, how do I set up the renpy.displaymenu function?

renpy.display_menu(items):
The function display_menu, which is damn useful to create a menu made of dynamically created choices, takes as a parameter a list of 2-tuples, here called items.
The parameters is expected to be of the form : [ ( "Text of the first choice", returned_on_first choice ) , ("2nd Choice Text", returned_on_2nd_choice) , ... ].
The first part of the element will be a text string, which will be displayed as a choice button. The second part will be the value returned by the function once the user click is done.

As an example, here is some code :
Code:
$ menuitems = [ ("I'll take a sandwich", "eat"), ("I'll rather have a glass of water", "drink") ]
$ result = renpy.display_menu(menuitems)
"You picked the option [result]."


Here is what the user will see :
Attachment:
File comment: Result of the menu screen
screenshot0003.png
screenshot0003.png [ 17.24 KiB | Viewed 392 times ]


And then after the click :
Attachment:
File comment: Result after click on the sandwich option
screenshot0004.png
screenshot0004.png [ 12.92 KiB | Viewed 392 times ]


You'll notice that the message do carry the correct return value, ie "eat". It is inclued in the message though the "text [variable] more text" syntax, that reads the value of the variable and 'injects' it into the text string.

Quote:
Thank you so much for your help, and I hope I'm not too much trouble!!

Not at all! You're polite, well-spoken (well, well-typed), you say I'm awesome (which is true) and you like ponies.


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 5:09 pm 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
I see!! That makes a lot of sense, now. :3

So, to set up the renpy.display_menu function after the part that checks for a tie, would it look something like this?

Code:
python:
    points = {
        'Person A': person_a_points,
        'Person B': person_b_points,
        'Person C': person_c_points,
    }
    highest_points = max(points.values())
    people_with_highest_points = []
    for person, person_points in points.iteritems():
        if person_points == highest_points:
            people_with_highest_points.append(person)
       
if len(people_with_highest_points) > 1:
   renpy.display_menu (items, window_style='menu_window', interact=True, with_none=None):
      $ menuitems = [ ("I'll take a sandwich", "eat"), ("I'll rather have a glass of water", "drink") ]
      $ result = renpy.display_menu(menuitems)
   "You picked the option [result]."
else:
    # No ties.


Argh, I also got confused as to which parts in the first bit of code I should be changing, based on what items have points allocated to them. :c I left them as 'Person A,' etc. since I wasn't quite sure about it. Would I be changing the 'Person A,' etc. parts and the 'person_a_points' parts to something using 'hunger,' 'drink,' or 'social,' as well? Or do I leave those alone? And the person_points and people_with_highest_points parts, too. I'm not sure if those should change as items change, or if they are general terms and fit whatever items are in the list.

Another question about the display_menu function as well -
Is there a way to set the display of a result to be completely different, and not show 'You picked [result]?' I guess, in the example we've been using, something long the lines of saying "The sandwich was delicious," instead of just saying that they chose to eat.

Ah, and will the tiebreaker bit of code be able to allocate another point to the specified item, in order to make them the highest value? Or how would that work, for moving on to the next bit of story with that character?

I suppose that maybe explaining a little of what I'm hoping to do with the code might help more, too! I plan on having the protagonist of the game choose different people for different sorts of relationships as the story progresses. Namely, rival, best friend, lover, mentor, etc. So in the process of deciding your rival, you end up with a tie. Another option will be presented to the player that allows them to choose between the tied candidates. After they make their choice, the game will continue on with that person as their rival.

Would another block of code be necessary to decide between the candidates once the tie has been resolved? And if so, would it need to be completely new, or would copying and pasting the same block of python code previously used to find the highest value work?

Thank you again, I'm really learning a lot! :D and who couldn't like ponies? they're PONIES, and are inherently wonderful c:


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 5:58 pm 
Regular
User avatar

Joined: Sat Feb 04, 2012 9:46 pm
Posts: 34
Quote:
Argh, I also got confused as to which parts in the first bit of code I should be changing, based on what items have points allocated to them. :c I left them as 'Person A,' etc. since I wasn't quite sure about it. Would I be changing the 'Person A,' etc. parts and the 'person_a_points' parts to something using 'hunger,' 'drink,' or 'social,' as well? Or do I leave those alone? And the person_points and people_with_highest_points parts, too. I'm not sure if those should change as items change, or if they are general terms and fit whatever items are in the list.

Variables names are names.
Keys values are values (and can be either text, numbers...).
They don't have an inherent meaning. The only words that are inchangeable are the language keywords. (for, in, len,...)
I could take your code and rename everything :
Code:
python:
    elements_of_harmony = {
        'Apple Bloom': cutie_marks,
        'Twilight Sparkle': books,
        'Pinkie Pie': balloon_lollipops_streamers
    }

and the code would work exactly the same way - the computer don't care for particular names : he just see you use one name for one variable, so that name is the codder access to the value stored.

Given all that, you should always use as variable names and/or as dictionary keys words that makes sense in the context of your code : here, except if i was making MLP:FiM the Visual Novel, it wouldn't mean anything (i.e. it wouldn't help the human coder understand what the variable is used for).

Quote:
Is there a way to set the display of a result to be completely different, and not show 'You picked [result]?' I guess, in the example we've been using, something long the lines of saying "The sandwich was delicious," instead of just saying that they chose to eat.

Yes, yes and yes. There is a fundamental thing in coding that you must understand. Apart from using the same data, and save a few particular cases (that we'll see later, I guess) a line of code is independant of the one that came before, nor cares for the one that will come after. Basic rule of computing. Learn it. FOREVER.

So here, we have:
Code:
$ menuitems = [ ("I'll take a sandwich", "eat"), ("I'll rather have a glass of water", "drink") ]
$ result = renpy.display_menu(menuitems)
"You picked the option [result]."

My lesson of "don't care about any code but my own line" applies, so as far as the ren'py.display_menu is concerned, it looks like.
Code:
# Bunch of code that may or may not have set menuitems to anything
$ result = renpy.display_menu(menuitems)
# Bunch of code that may or may use the 'result' variable

Quote:
Ah, and will the tiebreaker bit of code be able to allocate another point to the specified item, in order to make them the highest value? Or how would that work, for moving on to the next bit of story with that character?

You know what ? I'm gonna be evil and not answer that question. :twisted: If you did understand my lesson about the separate nature of codelines, you should be able to figure it out yourself. It will be your homework. :lol:

Quote:
Would it look something like this?

Code:
if len(people_with_highest_points) > 1:
   renpy.display_menu (items, window_style='menu_window', interact=True, with_none=None):
      $ menuitems = [ ("I'll take a sandwich", "eat"), ("I'll rather have a glass of water", "drink") ]
      $ result = renpy.display_menu(menuitems)
   "You picked the option [result]."


That part is all wrong.
A function call works like this : you have a function (let's call it function) that has parameters (param1 & param2, for example.)
Code:
def function(param1, param2):
   #bunch of code that defines a_value from param1, param2, and other things maybe
   return a_value

When in your code, you use function, for example,
Code:
$ my_result = function(2, 25)
it will execute all the code in the def function block, but param1 will be equal to 2, and param2 will be equal to 25.
When the execution of the function blocks reaches return a_value, it will return the value of a_value to the original function call. Thus, once the function call is done, the line would have the same effect (to the computer) that the line.
Code:
$ my_result = a_value
but remember that the a_value's value depends of the parameters that you have given at first.

Thus, if later I code the line
Code:
$ my_other_result = function(58, 33)
, my_other_result will have a different value than the one you had for my_result with 2 & 25.

Thus, in your call to renpy.display_menu(...), you must first define the values you'll pass as parameter, thus my
Code:
$ menuitems = [ ("I'll take a sandwich", "eat"), ("I'll rather have a glass of water", "drink") ]
line, and after (and only after) that, you call the function with your parameters and store it into a variable.


Hey, second homework :twisted: : try explaining those two lines in "natural language", as I did before :).
Code:
$ menuitems = [ ("The Number Five", 5), ("The Word 'blue'", "blue") ]
$ result = renpy.display_menu(menuitems)


Top
 Profile Send private message  
 
PostPosted: Tue Apr 17, 2012 6:23 pm 
Newbie
User avatar

Joined: Mon Apr 16, 2012 11:32 pm
Posts: 16
Location: Missouri, US
Projects: Alternia Academy
Hmm.. Okay. I think I understand how I can get the renpy.display_menu bit to work how I would like it to! Once I'm on my other computer, I'll have to test it out myself; trial and error seems like a good plan, since I have a basic idea of that.

I suppose I am a little confused by the function call now. I'm not really sure what part was wrong in the code I tried to do. Should it look like this, instead?

Code:
if len(people_with_highest_points) > 1:
      $ menuitems = [ ("I'll take a sandwich", "eat"), ("I'll rather have a glass of water", "drink") ]
      $ result = renpy.display_menu(menuitems)
   "You picked the option [result]."


I think I have an idea of how to get it to allocate a point to the respective options as well, though I'm not sure how to write it! I'm thinking I'll have to use 'if' and 'elif,' as well as "hunger": $ "hunger" += 1 in there to do so. Would that be correct?

Second Homework time!
Code:
$ menuitems = [ ("The Number Five", 5), ("The Word 'blue'", "blue") ]
$ result = renpy.display_menu(menuitems)

  • Let there be a function called menuitems.
  • Let the function's parameters include the key items "The Number Five," and "The Word Blue."
  • Let the values of those key items include "5" and "blue."
  • Let there be a function called result.
  • For the function result, display a menu containing the parameters of the function 'menutitems.'

I got all fancy with the formatting, but I wanted to see if I could highlight the differences between the code, the parameters, and the titles of the functions. :P


Top
 Profile Send private message  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 30 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours [ DST ]


Who is online

Users browsing this forum: No registered users


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Protected by Anti-Spam ACP
Powered by phpBB® Forum Software © phpBB Group