Ren'Py Trying to convert dynamic variables (containing strings) into dynamics lists

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,717
2,723
So I have something similar to this - a bunch of variables named : var_A, var_B, var_C, and so on. Then there's var2_A,var2_B, etc... and var3_A,var3_B, etc..

so for example var_A = "This is A"
var_B = "This is B"
var2_A = "This is the 2nd A"
var3_A = "This is the 3rd A"

and so on.


Later on I have a screen as such

Code:
]screen screenname(name):
    text "[{}]".format("var2_"+name)
    text "[{}]".format("var3_"+name)
    text "[{}]".format("var_"+name)

So, the above works perfectly - if I pass A to the screen it shows "This is the 2nd A", "This is the 3rd A", "This is A", same for B, C, etc - whatever "name" I pass to the screen.

However what I want to do is turn var_A, var_B, etc into lists:

so

var A = ["This is A1","This is A2","This is A3"]


I don't want to change var2_A, or var3_A, those would still continue to be strings - but I want var_A to be a list of strings.

The problem I'm running into now is so far everything I've tried either #1 errors out, or #2 pasts the entire text of the list. I can't figure out how to isolate any particular list index without re-writing everything (Which is a lot of code, I'm using functionally equivalent examples for the sake of brevity) -

so for example, without changing the above screen at all it prints:

"This is the 2nd A"
"This is the 3rd A"
"["This is A1","This is A2","This is A3"]"


I can't figure out how to get it to work properly and only print "This is A1" or whatever index I want - like I said, every attempt so far either prints the entire list, or just errors out because I'm doing it wrong.






Edit: Still messing around with it, though I admit I have no clue wtf I'm doing (I'm mostly just trying different shit to see what happens)

The cloest I've come so far is this:

if I do

text "[{}[0]]".format("var_"+name)

Then it will print whatever is in the 0 index of "var_A". Hooray!

But I still can't get it to iterate along the list. I've tried a ton of different options (including dropping the .format entirely a few different ways) and everything else either prints: The entire list, produces an error, or prints the variable name (So I'll get it to print "var_A" instead of the contents of var_A)

when the above code printed at least a single index of the list, I tried a bunch of different ways to attempt to iterate through it, but none of them worked (the majority ran me into "List indices must be integers or slices, not str" errors) - I know *WHY* it was giving me that error, unfortunately I don't know how to get it to do what I want.


Ultimately I want something like:

for i in len(var_A):
text var_A

which seems simple... except because I'm arriving at var_A dynamically I can't figure out how to get it to do that. Note: That's why I was originally using .format to begin with - when the variable was just a single string it worked perfectly, but Idk how to get it to work if it's a list of strings instead.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,611
2,258
Unfortunately, by using equivalent examples - you lose a lot of the context.

One thing you might be looking for is eval().

To use your most basic example, with simple variables rather than lists:

Python:
default A1 = "Test1"
default A2 = "Test2"
default A3 = "Test3"
default newval = None

label start:

    $ newval = eval("A" + "2")
    "[newval]"

    return

Another might be getattr():

Python:
default A1 = "Test1"
default A2 = "Test2"
default A3 = "Test3"
default newval = ""

label start:

    # where "store" is the default bucket where standard variables are stored.
    $ newval = getattr("store", "A" + "2")
    "[newval]"

    return

Both these examples can be expanded to use more complex variables (like lists).

Reading your post, the implication is that "A", "B", "C", etc are character names. So "anne", "becca", "chloe". Except a list anne[] doesn't make much sense to me - so I'm struggling to think what solution might fit best.

Depending on your goal, one idea that might fit would be to use dictionary rather than a list. Or a dictionary combined with a list.

Python:
define mydict = {"anne": ["Anne 1", "Anne 2", "Anne 3"],
     "becca": ["Becca 1", "Becca 2", "Becca 3"],
     "chloe": ["Chloe 1", "Chloe 2", "Chloe 3"]}

default newval = ""

label start:


    $ newval = mydict["becca"][1]
    "[newval]"

    return

A reminder that lists start at zero (0) not one.

As for some of the other incidentals...

One way to avoid going out of bounds on your list arrays is to use for ... in ... loops.
RenPy doesn't really support looping well. But python can do it. Just know that things like rollback, etc might have a hard time.

In practical terms, loops are usually okay when used as part of a screen - but probably shouldn't be used like this:

Python:
define mydict = {"anne": ["Anne 1", "Anne 2", "Anne 3"],
     "becca": ["Becca 1", "Becca 2", "Becca 3"],
     "chloe": ["Chloe 1", "Chloe 2", "Chloe 3"]}

default newval = ""

label start:

    python:
        for each x in mydict["chloe"]:
            renpy.say(None, x)

    return

Another way is to know how big the list is, using len(). Perhaps something based on:

Python:
default anne = ["Anne 1", "Anne 2", "Anne 3"]

default mylength = 0

label start:

    $ mylength = len(anne)
    "Anne's list is [mylength] elements long."

    return

All of which is mostly me trying to guess at what you are trying to do and hoping that one of these solutions (or a combination of them) will fit your needs.

You might also benefit from using a custom class, or a custom class which uses a list... or something.
(My .sig has a link to a post I wrote about using lists and dictionaries and stuff - as a bit of further reading).

btw. I use define for any variable that will NOT change while the game is running (i.e. shouldn't be saved as a part of a save file) and default for a variable that the player actions will mean it's value changes. In my examples, I've mixed and matched the two for no good reason, except to highlight there is a difference.

I haven't tested any of this code. It probably works.
 
Last edited:

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,717
2,723
Unfortunately, by using equivalent examples - you lose a lot of the context.

One thing you might be looking for is eval().

To use your most basic example, with simple variables rather than lists:

Python:
default A1 = "Test1"
default A2 = "Test2"
default A3 = "Test3"
default newval = None

label start:

    $ newval = eval("A" + "2")
    "[newval]"

    return

Another might be getattr():

Python:
default A1 = "Test1"
default A2 = "Test2"
default A3 = "Test3"
default newval = ""

label start:

    # where "store" is the default bucket where standard variables are stored.
    $ newval = getattr("store", "A" + "2")
    "[newval]"

    return

Both these examples can be expanded to use more complex variables (like lists).

Reading your post, the implication is that "A", "B", "C", etc are character names. So "anne", "becca", "chloe". Except a list anne[] doesn't make much sense to me - so I'm struggling to think what solution might fit best.

Depending on your goal, one idea that might fit would be to use dictionary rather than a list. Or a dictionary combined with a list.

Python:
define mydict = {"anne": ["Anne 1", "Anne 2", "Anne 3"],
     "becca": ["Becca 1", "Becca 2", "Becca 3"],
     "chloe": ["Chloe 1", "Chloe 2", "Chloe 3"]}

default newval = ""

label start:


    $ newval = mydict["becca"][1]
    "[newval]"

    return

A reminder that lists start at zero (0) not one.

As for some of the other incidentals...

One way to avoid going out of bounds on your list arrays is to use for ... in ... loops.
RenPy doesn't really support looping well. But python can do it. Just know that things like rollback, etc might have a hard time.

In practical terms, loops are usually okay when used as part of a screen - but probably shouldn't be used like this:

Python:
define mydict = {"anne": ["Anne 1", "Anne 2", "Anne 3"],
     "becca": ["Becca 1", "Becca 2", "Becca 3"],
     "chloe": ["Chloe 1", "Chloe 2", "Chloe 3"]}

default newval = ""

label start:

    python:
        for each x in mydict["chloe"]:
            renpy.say(None, x)

    return

Another way is to know how big the list is, using len(). Perhaps something based on:

Python:
define mydict = {"anne": ["Anne 1", "Anne 2", "Anne 3"],
     "becca": ["Becca 1", "Becca 2", "Becca 3"],
     "chloe": ["Chloe 1", "Chloe 2", "Chloe 3"]}

default mylength = 0

label start:

    $ mylength = len(mydict["anne"])
    "Anne's list is [mylength] elements long."

    return

All of which is mostly me trying to guess at what you are trying to do and hoping that one of these solution (or a combination of them) will fit your needs.

I haven't tested any of this code. It probably works.

I'm going to read through and try to comprehend what you wrote in a minute, but I used ENTIRELY functional equivalents that contained all necessary context in my mind - it might not read that way to you, idk but I did name the variables according to how they're done in the actual code. I didn't post the actual code, because it's just very very long.

You're correct in assuming "A", "B", "C" are character names - but the variables are literally formatted as "var_A" "var_B" etc.

Specifically the variables are "age_name" "status_name" and "bios_name" - (replace name with the character name).

So my screen has the lines:

text "[{}]".format("age_"+name)
text "[{}]".format("status_"+name)
text "[{}]".format("bios_"+name)


a single name is passed to the screen. We'll use MC for the sake of it. so the screen with the above code - when "MC" is passed to it - displays the contents of the 3 variables "age_MC", "status_MC", and "bios_MC" - which in this case is "20" "student" "long string of text". Every character has those 3 variables - so going of what you said, there would be "age_Anne" "age_Becca" "age_Chloe" and so on. Yes I originally could have created everything as a dict (or bunch of dicts) and avoided this problem in the first place, but I didn't do that originally which is why I'm here.

My main goal here is simply to convert the variable bios_MC (and all the bios_name variables) to lists instead of a single string - and then on demand pull up whatever index I want (and to be able to iterate through the list, so I can display all the indexes of it)

edit: To clarify, the goal is to be able to pass just "name" to the screen, and then be able to correctly pull up the appropriate index of "bios_MC" if name="MC"



My main reason for wanting to do this, is because the code is quite long and quite intertwined with everything else - and so I'm trying to avoid re-writing ALL of it. I can already think of a few ways to re-write it all to work exactly how I want, (probably using dicts) but simply the amount of code I'd have to re-do to accomplish that is why I'm looking for the alternative that I am.



edit: and to comment on my previous edit:

Currently, if I convert the "bios_name" var into a list, and then do

text "[{}[1]]".format("bios_"+name) style "biostext" xalign 0.5

It correctly prints the 2nd entry in that list. Unfortunately nothing I can figure out will allow me to interate through that list the way I have it set up - so for example

text "[{}[i]]".format("bios_"+name) style "biostext" xalign 0.5

doesn't work (In that example just to test I did something like for i in range(0,4) - which is where the list indices must be intgers error came from originally.


So the TL;DR here is yes, if I converted everything to dicts this would be easy, and I'd be able to do this no problem. The issue comes in the fact that it'll take me days to re-write everything to accomadate that, and I'm trying to come up with a hacky but working solution in hours instead of spending days on it.


Final edit:

Okay. After reading through what you wrote, eval() works perfectly, thanks!

I just set it up so the screen has $tmp = eval("bios_"+name)

then:

Code:
for i in range(0,len(tmp)):
    text tmp[i]
and that does exactly what I was looking for - so TY, problem solved.

Now I just have to work on properly formatting everything.
 
Last edited:
  • Like
Reactions: 79flavors

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,611
2,258
So the "bios" change over time, and will progress as the player progresses?

Sort of "I just met her" -> "Went for a coffee" -> "Had a nice meal" -> "She moved in" sort of deal.

But I presume the number of indexes within the list can change depending on the person. So Anne may have 5 steps (and expand to 8 in future releases) and Becca might have 4.

Right now, I'm leaning towards some sort of "get_npc_bios" function, based on a sort of progress_anne type variables.

(btw, getattr() is probably the way to go)
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,717
2,723
So the "bios" change over time, and will progress as the player progresses?

Sort of "I just met her" -> "Went for a coffee" -> "Had a nice meal" -> "She moved in" sort of deal.

But I presume the number of indexes within the list can change depending on the person. So Anne may have 5 steps (and expand to 8 in future releases) and Becca might have 4.

Right now, I'm leaning towards some sort of "get_npc_bios" function, based on a sort of progress_anne type variables.

Yes, basically that. It changes over time. At the moment the way I had everything set up, the "new" info replaces the old, I wanted to change it so that old entries are kept (so I can simply .append new entries to it) - so I can display the old and new entries instead of re-writing them over time.

but As mentioned in my final edit - using eval() actually allows me to do exactly what I want - now I'm just working on formatting it all so that it displays nicely.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,233
My main goal here is simply to convert the variable bios_MC (and all the bios_name variables) to lists instead of a single string - and then on demand pull up whatever index I want (and to be able to iterate through the list, so I can display all the indexes of it)

edit: To clarify, the goal is to be able to pass just "name" to the screen, and then be able to correctly pull up the appropriate index of "bios_MC" if name="MC"
And how will you know what is the appropriate index ?



My main reason for wanting to do this, is because the code is quite long and quite intertwined with everything else - and so I'm trying to avoid re-writing ALL of it.
By making it even more complex and less understandable even by you ?

Switching to a dictionary, (one by character, "age", "bios", etc as key), would lead to something less complex and more readable.
Plus, with a basic search/replace all through your text/code editor, to replace any occurrence of "age_anne" into anne["age"], it would have been already completely done. This even if the said search/replace have to be done manually for every one of the variables name.

Would rest the screen that would have to be manually updated, but it's already the case with what you try to achieve.


edit: and to comment on my previous edit:

Currently, if I convert the "bios_name" var into a list, and then do

text "[{}[1]]".format("bios_"+name) style "biostext" xalign 0.5
What doesn't match the "be able to correctly pull up the appropriate index of "bios_MC" if name="MC"" you wrote above.

You started your post saying that you want bios = [ bios_anne, bios_mc, ... ].
But when you present a practical example in your last edit, it become bios_anne = [age_anne, bios_anne, ... ].

Writing code is 80% thinking, 20% writing. A 99% writing, 1% what the fuck it don't works I'll write something else, approach will never works as expected, never...


text "[{}[i]]".format("bios_"+name) style "biostext" xalign 0.5

doesn't work (In that example just to test I did something like for i in range(0,4) - which is where the list indices must be intgers error came from originally.
Would you had effectively took the time to read what 79flavors wrote, you would have already figured the way to do this:
Code:
for i in range( 0, 4):
    text "{}".format( getattr( store, "bios_"+name)[i] )
But, obviously, this do no solve the main issue, that is the lack of prior thinking.
You have a blurry idea to solve what you see as an issue (many lines of code), but even in your head that idea chance over time. Therefore, whatever you'll came up with in the end, while be partly broken.


So the TL;DR here is yes, if I converted everything to dicts this would be easy, and I'd be able to do this no problem. The issue comes in the fact that it'll take me days to re-write everything to accomadate that, and I'm trying to come up with a hacky but working solution in hours instead of spending days on it.
And you were ready to dedicate a whole day or more to come up with this solution...


Now I just have to work on properly formatting everything.
Well, like the solution you came with isn't proper, nor really formatted, good luck with that...
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,717
2,723
You started your post saying that you want bios = [ bios_anne, bios_mc, ... ].
But when you present a practical example in your last edit, it become bios_anne = [age_anne, bios_anne, ... ].
Obviously you completely misunderstood what I said, because that's not even close to correct in either case.


Would you had effectively took the time to read what 79flavors wrote, you would have already figured the way to do this:
Actually your solution doesn't work, since the range is variable.




And here's the thing: I understand EXACTLY what my code does, how, and why. There is nothing in it that I don't "understand".

The only thing I don't understand is a lot of the fundamentals of the language - which leads to a lot of "Throw shit at the wall and see what sticks". When it comes to logic, I know exactly what I'm doing. When it comes to the actual code, I just keep trying stuff until it works - and you know what? Despite your insistence it DOES work!

And you know what? I honestly don't care. Lose your shit all you want. Complain and bitch and spout utter nonsense about all the different ways you don't like it - go fucking nuts dude. Have at it. Don't expect me to give a shit though if all you're gonna do is bitch and be bitter.

At the end of the day it works, it does exactly what I want, and how I want, so I'm thrilled. You on the other hand seem pretty bitter off someone doing things in a way you don't like. I'd talk to a therapist about that if I was you - it seems like an issue that should be addressed.

But, obviously, this do no solve the main issue, that is the lack of prior thinking.
You have a blurry idea to solve what you see as an issue (many lines of code), but even in your head that idea chance over time. Therefore, whatever you'll came up with in the end, while be partly broken.
Actually the real issue is I had perfectly functional working code that did exactly what I wanted, and published multiple updates with it - but then someone gave me an idea I hadn't though of, so I was trying to find ways to implement it into the existing code, and now I have so success! And guess what? It ain't broken - it just isn't all pretty like you want it to be.

And you know what else? Maybe one day a couple years from now someone will give me another idea I haven't thought of that I like. And you know what I'll do then? I'll figure out a way to implement it on top of the code you hate so much - maybe even get some help doing so!
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,233
And here's the thing: I understand EXACTLY what my code does, how, and why. There is nothing in it that I don't "understand".
Yet, your are totally unable to explain it...


and you know what? Despite your insistence it DOES work!
In the little range of variation used for your test...


[...] if all you're gonna do is bitch and be bitter.
Say the guy who need insults to explain how much he don't care...


[...] on top of the code you hate so much [...]
You used this notion a lot, totally missing the effective meaning of the word "proper". Would you not loose your calm so easily over nothing at all, perhaps would you'd understood that a code that isn't "proper" is also a code that isn't bug free.
 

noping123

Well-Known Member
Game Developer
Jun 24, 2021
1,717
2,723
Yet, your are totally unable to explain it...
Your inability to properly understand my explanation of the code does not constitute my inability to understand the code. Don't confuse the two.

In the little range of variation used for your test...
Ah yes. The *checks notes* entire game. I mean I guess I could just write way more code than I need to make sure it works in every possible case scenario including those I'll never use!

Say the guy who need insults to explain how much he don't care...
The two aren't mutually exclusive.

You used this notion a lot, totally missing the effective meaning of the word "proper". Would you not loose your calm so easily over nothing at all, perhaps would you'd understood that a code that isn't "proper" is also a code that isn't bug free.
Do you know what a bug is? It's when things don't work correctly. Believe it or not "It might not work in this possible case scenario that will likely never exist" isn't a bug.

Look at it like this. If you have an elevator rated for 1000kg - that's fine. Until 3000kg of weight is put on it. But you don't say the elevator is flawed - because it wasn't designed to handle 3000kg in the first place. You're approaching it from the standpoint of "Well, you *might* want to put 3000kg on it some day, so you should prepare for that possibility!" - and frankly, I disagree. I don't ever anticipate that being an issue, so I'm not going to waste time preparing for it. Maybe that creates a problem in the future? But probably not.

That's your biggest issue - you want things to be futureproofed for every conceivable possible scenario - I consider that a waste of time (and overall, I would have spent WAY more time trying to do that). I want things to work for the scenarios I consider realistic - really don't care about anything else.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,233
Believe it or not "It might not work in this possible case scenario that will likely never exist" isn't a bug.
:FacePalm:

Believe it or not, but everyone who have for profession to write code will tell you that this claim is pure bullshit.

Computer science wouldn't have bothered to come to exceptions handling, and languages wouldn't have bothered to massively adopt that approach starting the 80's, if everything you discard wasn't an effective and real concern when it come to both code writing and effective software use.
All modern languages are designed precisely for their code to be future proofed and to handle scenario that will likely never exist. This while most already existing languages have seen their specification updated to do the same.

Funnily enough, Python is the language that goes the further in that approach, being entirely designed around exceptions, and having a Python 2.x library entirely dedicated to the use of code that only exist in the Python 3.x specifications.


That's your biggest issue [...]
Well, if my biggest issue is that I know what I'm talking about and how to properly do my job, I guess that I can live with it.