Making Python take an argument or string as a variable name

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
jdlang
Newbie
Posts: 13
Joined: Wed Mar 22, 2017 2:59 pm
Contact:

Making Python take an argument or string as a variable name

#1 Post by jdlang » Sat Feb 15, 2020 12:43 pm

Hey guys!
I want to do something that should be very simple, but Python isn't having it. There has to be an easy solution, but ~8 hours of internet searches haven't turned it up, and this is slowly driving me crazy. Please help me!
I'm making a turn-based game, where every minute is one turn. In order to track which character is where at what time, I've got a somewhat complex setup of nested dictionaries and such. As a simplified example, let's say I have three dictionaries named A, B, and C, each one containing three lists under the keys "objects", "characters", "places". Instead of hardcoding everything, I want to have one function that can access and modify either of these dictionaries when I call it with the dictionary name as an argument. Alternatively, I've tried substituting part of the dictionary's name with a string stored in a variable. But so far, Python has insisted that I type every single letter out.

Thanks in advance!

User avatar
trooper6
Lemma-Class Veteran
Posts: 3712
Joined: Sat Jul 09, 2011 10:33 pm
Projects: A Close Shave
Location: Medford, MA
Contact:

Re: Making Python take an argument or string as a variable name

#2 Post by trooper6 » Sat Feb 15, 2020 7:39 pm

I don't have a completely clear view of what exactly you are doing, but maybe put all of the dictionaries into a class and then have the class do the accessing and modifying?
Also, how many turns is this game going to be? Do you have a dictionary for each minute...will there be hundreds and hundreds of minutes? What exactly is the structure you are going for...because maybe there is an easier way to go about it?
A Close Shave:
*Last Thing Done (Aug 17): Finished coding emotions and camera for 4/10 main labels.
*Currently Doing: Coding of emotions and camera for the labels--On 5/10
*First Next thing to do: Code in all CG and special animation stuff
*Next Next thing to do: Set up film animation
*Other Thing to Do: Do SFX and Score (maybe think about eye blinks?)
Check out My Clock Cookbook Recipe: http://lemmasoft.renai.us/forums/viewto ... 51&t=21978

User avatar
jdlang
Newbie
Posts: 13
Joined: Wed Mar 22, 2017 2:59 pm
Contact:

Re: Making Python take an argument or string as a variable name

#3 Post by jdlang » Sun Feb 16, 2020 7:06 am

trooper6 wrote:
Sat Feb 15, 2020 7:39 pm
I don't have a completely clear view of what exactly you are doing, but maybe put all of the dictionaries into a class and then have the class do the accessing and modifying?
That would probably be somewhat more elegant generally, but it wouldn't solve my problem. Actually, I've already thought about using classes, but couldn't find anything about whether they accept dictionaries or lists as attributes.
trooper6 wrote:
Sat Feb 15, 2020 7:39 pm
Also, how many turns is this game going to be? Do you have a dictionary for each minute...will there be hundreds and hundreds of minutes?
There's going to be thousands of minutes... At least seven days, if not more. Haven't planned it out exactly, since I wanted to prototype, test and tweak the core gameplay mechanics first before making up an entire story.
trooper6 wrote:
Sat Feb 15, 2020 7:39 pm
What exactly is the structure you are going for...because maybe there is an easier way to go about it?
Yes! I would very much like to have an easier (and maybe more elegant) way to do this, but I'm super clumsy at coding... If you could give me any pointers, that would be great! So here's how I've planned it so far.
There's a game world that is divided into cells. When the player enters a cell, the code accesses a list of objects, characters, and exits, and displays them to the player, who can then (like in an old-school point and click game) choose to interact with them in different ways. I've gotten this to work in principle, though only hard-coded - because of the very problem I'm asking about: When I try to write a function that could access any cell's object, character, and exit list when it's passed the cell's name, Python simply tells me to go frag myself.
Anyway, that would be very static on its own, so each character in the game moves independently of the player according to a timetable. This timetable should be stored separately for each character to make managing it easier. However, to decrease the operations needed, the game should generate a timetable containing all character movements that allows to easily access all entries for any given minute (f.ex. "at minute 583 character A moves from cell Z to cell Y, and character B moves from cell X to cell W"). Now, when the player interacts with something in the game world, that takes a minute or more, so I have a function to pass the time and one to check the unified timetable. The latter then moves entries of all characters that have changed places from the characters list of one cell to another.

User avatar
RicharDann
Veteran
Posts: 263
Joined: Thu Aug 31, 2017 11:47 am
Contact:

Re: Making Python take an argument or string as a variable name

#4 Post by RicharDann » Sun Feb 16, 2020 8:09 am

You should definitely use classes for this, at least to define your cells, and classes do allow the use of lists or dictionaries as attributes.

As for your original question, I still don't quite understand what are you trying to do or why python isn't doing what you want. You should at least show us the problematic section of your code so we can figure out what is wrong.
The most important step is always the next one.

User avatar
jdlang
Newbie
Posts: 13
Joined: Wed Mar 22, 2017 2:59 pm
Contact:

Re: Making Python take an argument or string as a variable name

#5 Post by jdlang » Sun Feb 16, 2020 8:58 am

RicharDann wrote:
Sun Feb 16, 2020 8:09 am
As for your original question, I still don't quite understand what are you trying to do or why python isn't doing what you want. You should at least show us the problematic section of your code so we can figure out what is wrong.
Alright, here's a snippet of code. I hope that makes it clearer.

Code: Select all

init python:

    def xnode_prep(c):

        playerlocation_objects = cells[c]["objects"]
        playerlocation_characters = cells[c]["characters"]
        playerlocation_go = cells[c]["go"]
The three variables beginning with "playerlocation" are lists that store all objects, characters, and exits currently available at the player's location. (I know, this is redundant, but it was an attempt at a workaround.) "cells" is a nested dictionary. Inside it are dictionaries of all cells in the game, which in turn contain lists of objects, characters, and exits located in that cell. But here's where it gets interesting. That "c" in square brackets after cells is supposed to be the specific cell's name, passed to the function, so it can dynamically access the lists of whichever cell the player is entering. However, after executing that code, the three "playerlocation" variables remain empty.
The way I figure it, there can only be two solutions: 1. There is a more elegant way to do this that is obvious to people who actually learned coding step by step. 2. The way I'm trying to do this is correct in principle and I'm just getting the syntax wrong.

User avatar
RicharDann
Veteran
Posts: 263
Joined: Thu Aug 31, 2017 11:47 am
Contact:

Re: Making Python take an argument or string as a variable name

#6 Post by RicharDann » Sun Feb 16, 2020 9:18 am

I haven't used dictionaries in a long time but the code seems correct.

How and where are the player_locations variables defined? And how are you checking if they are being updated?
The most important step is always the next one.

User avatar
jdlang
Newbie
Posts: 13
Joined: Wed Mar 22, 2017 2:59 pm
Contact:

Re: Making Python take an argument or string as a variable name

#7 Post by jdlang » Sun Feb 16, 2020 9:42 am

RicharDann wrote:
Sun Feb 16, 2020 9:18 am
How and where are the player_locations variables defined? And how are you checking if they are being updated?
I have a separate .rpy to set defaults for world state variables, in which I have defined them as lists with several items for test purposes. The items on these lists are correctly displayed on screens in the game. However, after the function in my last post is executed, the screens are empty. I've also tried to directly display the contents of the playerlocation variables using the display text command, and they turn up as empty lists, only showing square brackets.

User avatar
gas
Miko-Class Veteran
Posts: 838
Joined: Mon Jan 26, 2009 7:21 pm
Contact:

Re: Making Python take an argument or string as a variable name

#8 Post by gas » Sun Feb 16, 2020 8:31 pm

You can't do it. The called iterator go crazy.
Try this:

Code: Select all

def myfunc(c):
    x = cells[c]["characters"]
    y = cells[c]["objects"]
    z = cells[c]["whatever"]
    return x, y, z
$ chara, obj, what = myfunc("a")
If you want to debate on a reply I gave to your posts, please QUOTE ME or i'll not be notified about. << now red so probably you'll see it.

10 ? "RENPY"
20 GOTO 10

RUN

User avatar
jdlang
Newbie
Posts: 13
Joined: Wed Mar 22, 2017 2:59 pm
Contact:

Re: Making Python take an argument or string as a variable name

#9 Post by jdlang » Tue Feb 18, 2020 6:37 am

gas wrote:
Sun Feb 16, 2020 8:31 pm
You can't do it. The called iterator go crazy.
Try this:

Code: Select all

def myfunc(c):
    x = cells[c]["characters"]
    y = cells[c]["objects"]
    z = cells[c]["whatever"]
    return x, y, z
$ chara, obj, what = myfunc("a")
That works! Thank you so much!
Just out of curiosity, why does the called iterator go crazy?
Anyway, I'll start using classes for cells and characters. Do you have any other tips that might make things easier in the future?

User avatar
gas
Miko-Class Veteran
Posts: 838
Joined: Mon Jan 26, 2009 7:21 pm
Contact:

Re: Making Python take an argument or string as a variable name

#10 Post by gas » Tue Feb 18, 2020 8:12 am

jdlang wrote:
Tue Feb 18, 2020 6:37 am
That works! Thank you so much!
Just out of curiosity, why does the called iterator go crazy?
Anyway, I'll start using classes for cells and characters. Do you have any other tips that might make things easier in the future?
It's hard for me to explain on technical terms...
It's a rare circumstance that happen using nested (inside nested) stuff, interpolation and direct assignements on functions. Speaking common language, your statement hasn't "time" to perform both the sorting, the interpolation and the iterator to copy the list AT ONCE before the function RETURN "None", so the iterator generate the list but hasn't "time" (or any reason) to iterate and fill it up with a copy of the original.
Using a given RETURN instead force the function to compute the outcomes before returning them, step by step.
You could have solved the problem the same by using more than one statement by variable (like sorting the dictionary on a placeholder, sorting the list, then assigning it to the variable).

Any hint? I've used lists and dictionaries a lot before understanding that objects and methods are better for structured data. Using a list of Objects then list comprehensions can sort out things by logic more than math, and quite efficiently. So, for example, you can sort objects by a property ("all days with an assigned activity"), this will be very hard on cold basic types.
List comprehension, sorting and list methods like map(), zip(), max() could help you operate on structured data.
Practically speaking Python is there just for such sorting stuff...
If you want to debate on a reply I gave to your posts, please QUOTE ME or i'll not be notified about. << now red so probably you'll see it.

10 ? "RENPY"
20 GOTO 10

RUN

User avatar
jdlang
Newbie
Posts: 13
Joined: Wed Mar 22, 2017 2:59 pm
Contact:

Re: Making Python take an argument or string as a variable name

#11 Post by jdlang » Tue Feb 18, 2020 11:31 am

Well. Your suggested solution worked for the nested dictionary, but not for objects.
I've defined a class Cell, with two instances for test purposes. Neither of these works:

Code: Select all

def xnode_prep(c):

        x = getattr(c, "objects")
        y = getattr(c, "characters")
        z = getattr(c, "exits")
        return x, y, z

Code: Select all

def xnode_prep(c):

        x = c.objects
        y = c.characters
        z = c.exits
        return x, y, z
Both of these give an AttributeError: 'unicode' object has no attribute 'objects' - so Python is trying to access c itself instead of substituting the string inside c for the object name. Now, I could just create an object cells which has each individual cell as an attribute, but that seems like a crude workaround to me. Is there any way to do this more elegantly?

User avatar
RicharDann
Veteran
Posts: 263
Joined: Thu Aug 31, 2017 11:47 am
Contact:

Re: Making Python take an argument or string as a variable name

#12 Post by RicharDann » Tue Feb 18, 2020 12:10 pm

Both of these give an AttributeError: 'unicode' object has no attribute 'objects'
This might be because you're passing the c attribute, the name of the variable, as a string. You can just pass the object name directly.

Code: Select all

# Cell object definition
default cell_A = Cell(objects=[], characters=[], exits=[])

# call the function with cell_A as parameter
$ chars, objs, exits = xnode_prep(cell_A)
The most important step is always the next one.

User avatar
jdlang
Newbie
Posts: 13
Joined: Wed Mar 22, 2017 2:59 pm
Contact:

Re: Making Python take an argument or string as a variable name

#13 Post by jdlang » Wed Feb 19, 2020 3:50 pm

RicharDann wrote:
Tue Feb 18, 2020 12:10 pm

Code: Select all

# Cell object definition
default cell_A = Cell(objects=[], characters=[], exits=[])

# call the function with cell_A as parameter
$ chars, objs, exits = xnode_prep(cell_A)
Yup, that works. Wish the other approaches did as well, but oh well.
Thank you all for your help! Let's hope I don't get stuck like this again.

Post Reply

Who is online

Users browsing this forum: No registered users