Floating point to integer through nested list

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
WispoWill
Newbie
Posts: 13
Joined: Mon Mar 15, 2010 4:41 pm
Contact:

Floating point to integer through nested list

#1 Post by WispoWill »

Could someone please enlighten me as to how I am suppose to convert a floating point number from a nested list back into some format I can check the value with? I've spent quite a number of hours and have tried 10+ methods of doing this, some straight from standard Python documentation but everything seems to fail for one reason or another or another.

The if statement here is failing as the new variable is still seen as a "revertable list". I can convert the variable to a string type, but trying further to convert to a float or int type always causes errors, and trying to use the eval function to see what the program thinks the variable is causes an error that seems to be thrown by the renpy engine itself. The peculiar spelling of "revertable" rather than "revertible" makes me wonder if at least a couple of the other errors for things I've been trying aren't non-Python interpreter as well. At this place in the program I can pull a second variable and do an exact check, but later on I really do need to work with this as a true number. So, how do I do this in renpy?

I am not an object-oriented programmer, much less a python programmer, and have been frustrated by never knowing for sure whether problems I'm running into are due to my lack of understanding with modern methods, are due to documentation not matching version of Python being used, or because of limitations in the RenPy engine. I've been able to eventually resolve all problems so far, but at least two of the problems I was able to find mention of in the forums as being limitations RenPy implemented because of incompatibilities with the rollback engine. Even by passing code with the $ or python prefixes, code that runs on an interactive basis doesn't always in RenPy. Is there some way of disabling the rollback engine so that I can get results back that match standard python documentation? More effectively than $ config.rollback_enabled = False ? Or are the limitations so low-level that a recompile would be necessary? In lieau of that, is there someway of passing statements to a full-fledged interpreter instead of the limited one?

I have spent the better part of my free time for a couple of months on this personal game project and would hate to port now. There are features in RenPy that I really like, despite the fact that most of the features I don't really have a use for. However, I am starting to wonder if my project isn't really suitable for RenPy due to the limitations I keep facing.

]10 Gosub 100 : REM This a a rant and can be ignored
In the 80s I could work with games similar to Star Trek that used 8x8 matrixes which each contained an 8x8 matrix on a 1 Mhz computer with 64 KBs of memory. In the 90s I could sort mailing lists by zip code containing 5000-7000 addresses on a 60 Mhz computer with 8 MB of memory. Currently, object oriented languages seem to require that I hop up and down on a rail with one foot with a finger up my nose while whistling Yankee Doodle to accomplish what I would normally consider simple, basic tasks on my 1.8 Gigaherz computer with 3GB. I suppose a Computer Science person would think I'm just an eccentric crackpot. But, honestly here, just as an example... Python makes all division operations integer by default. In order to get sane results you have to declare your variable as floating point first. Why is this really necessary? For the 1 Mhz 6502 microprocessor there were four different methods for maintaining integrity of the floating point conversions on the binary level. There were only a handful of discrepencies, most of them known and documented, and in any case, (if my memory serves correct) the discrepencies were always less than a 10th of a decimal off.
]120 Return


I will need a copy of the variable later in the game that I can increment.


$ job_agency1 = ["Agency Assistant"], [1], [5.00], [0.0], [52], [5], [2]
$ open_agency = [None]
$ open_agency.insert(1, job_agency1) ; aj2 = 1
menu:
"Agency Openings" if ((len(open_agency)>1) and aj2) or customers:
$ i = 1 ; j = 0 ; ixtemp = 0
$ j = (len(open_agency))
if (j>1):
while (i<j):
$ ixtemp = open_agency[3][:]
if (ixtemp == 0):
$ temp = str(open_agency[0][:])
$ temp1 = str(open_agency[1][:])
"%(temp)s at level %(temp1)s at %(i)s and %(j)s"
$ i += 1
$ del ixtemp
if customers:
"You have %(customers)s"

WispoWill
Newbie
Posts: 13
Joined: Mon Mar 15, 2010 4:41 pm
Contact:

Re: Floating point to integer through nested list

#2 Post by WispoWill »

I finally got this to work by changing my list into a tuple before nesting. It is beyond my comprehension why it makes a difference to Python whether the data was nested as a list or a tuple when all I wanted to do was read the numerical value. I further discovered that issuing a statement such as $ a = [1], [2], [3], [4]. [5] makes the interpreter think I just issued a tuple, when in fact you can clearly see that it is formatted as a list. If one of the numbers above were to be a string, Python would see the statement correctly as a list. I don't know whether this is a bug or whether it's a Broken by Design moment, but this sort of thing seems totally irrational to me.

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: Floating point to integer through nested list

#3 Post by Jake »

Firstly, I'll say that I tried to read your code yesterday to see if I could answer your query (I had about five minutes spare before leaving the house) but I couldn't follow it easily 'cause all the indentation had been lost. I could just guess, I suppose, but for future reference, if you use the 'code' tags (there's a button for them at the top of the editor, between 'Quote' and 'List', if you don't want to type them out) the indentation will be preserved and reading your code will be a lot easier. Or, since Python has significant whitespace, a lot more possible. ;-)
WispoWill wrote:I further discovered that issuing a statement such as $ a = [1], [2], [3], [4]. [5] makes the interpreter think I just issued a tuple, when in fact you can clearly see that it is formatted as a list.
Um... actually, (aside from the fact that I'm assuming the '.' after the '[4]' should have been a comma) I can clearly see that it is formatted as a tuple. What do you think you're writing? That's a tuple consisting of five elements, each of which is a single-element list containing a single integer each.

If you want to write a list, you need to contain it in square brackets, so:

Code: Select all

    # The following is a tuple of five integers:
    t1 = 1, 2, 3, 4, 5
    # The following is a tuple of five lists, each of which contains a single integer:
    t2 = [1], [2], [3], [4], [5]
    # The following is a list of five integers:
    l1 = [1, 2, 3, 4, 5]
    # The following is a list of five lists, each containing a single integer:
    l2 = [[1], [2], [3], [4], [5]]
Tuples and Lists are both sequence types, which means they support some common operations. It's not hard to become confused when you start out with Python, it certainly does some things very differently to other languages for no discernible reason.
WispoWill wrote: If one of the numbers above were to be a string, Python would see the statement correctly as a list.
Are you sure? What exactly do you mean, syntactically? If you put square brackets around the whole thing, Python should see it as a list... but if you replace one of the numbers with a string, I would expect both of the following to be tuples:

Code: Select all

    t1 = [1], [2], [3], "4", [5]
    t2 = [1], [2], [3], ["4"], [5]
With regard to this:
WispoWill wrote: The if statement here is failing as the new variable is still seen as a "revertable list".
If you tried:

Code: Select all

    myVar = ["a"], ["b"], ["c"], ["d"], ["e"]
    if myVar[3] == "d":
        # Do some stuff
Then indeed - 'myVar[3]' refers to the 4th item (sequences are zero-indexed) in the tuple, which is indeed a one-element list containing "d" ("myVar[3] == ["d"]" is True), which isn't the same as a string "d". If you wanted to structure your data like that, then to get at the value itself you'd need to do this:

Code: Select all

    myVar = ["a"], ["b"], ["c"], ["d"], ["e"]
    if myVar[3][0] == "d":
        # Do some stuff
- to break it down:

Code: Select all

    x = myVar[3]   # x == ["d"]
    x[0]    # Get the first element in the list ["d"], which is "d".
WispoWill wrote: The peculiar spelling of "revertable" rather than "revertible" makes me wonder if at least a couple of the other errors for things I've been trying aren't non-Python interpreter as well.
I'm pretty sure that yes, as you suspect, the 'RevertableList' type is one defined by Ren'Py to replace regular lists with a version that participates in RollBack.
However, I've also used them many times myself for regular 'normal' list operations and never had any problems with them not working the same way as I'd expect a regular Python list to.
WispoWill wrote: Even by passing code with the $ or python prefixes, code that runs on an interactive basis doesn't always in RenPy. Is there some way of disabling the rollback engine so that I can get results back that match standard python documentation?
I believe - although I could be wrong - that if you put your code in a .py file instead of a .rpy file, then Ren'Py won't molest it. But on the other hand, it's a little trickier to use in your script, IIRC you have to give it a namespace (possibly inherited from the filename? It's been a while since I played with this notion) and import it explicitly into your Python blocks.
WispoWill wrote: In lieau of that, is there someway of passing statements to a full-fledged interpreter instead of the limited one?
Again I could be wrong, but I think that all Ren'Py does is munge all the Ren'Py script into Python instructions (e.g. turn a dialogue line into a renpy.say() call) and then fiddle with the Python statements and blocks to use Rollback types and then pass the whole thing off to the Python interpreter as a Python script anyway. I think the only difference you're facing, in a Python line or block, is the rollback types. Certainly I would expect a Python statement to at least be run in eval(), as plain Python code straight to the Python interpreter.
WispoWill wrote: In the 80s I could work with games similar to Star Trek that used 8x8 matrixes which each contained an 8x8 matrix on a 1 Mhz computer with 64 KBs of memory. In the 90s I could sort mailing lists by zip code containing 5000-7000 addresses on a 60 Mhz computer with 8 MB of memory. Currently, object oriented languages seem to require that I hop up and down on a rail with one foot with a finger up my nose while whistling Yankee Doodle to accomplish what I would normally consider simple, basic tasks on my 1.8 Gigaherz computer with 3GB. I suppose a Computer Science person would think I'm just an eccentric crackpot.
I don't think you're an eccentric crackpot, I think you're someone who's just not had Object Oriented programming properly introduced to them, and thus misses the fundamental details which would help you to understand it. (Although, that said, there are lots of people working as OO programmers who understand the syntax and the grammars but not the principles, soo...)

But to muddy the waters a little more, I'd have to say that Python isn't what I would call an Object-Oriented language; objects aren't really first-class language features in Python, they're something extra you can use if you want to. I don't know if there's an official term for it (which probably makes me a lousy computer scientist; my Programming Languages lecturer was on sabbatical the year I was eligible for the course, annoyingly) but I would call Python a type-oriented language. Its emphasis is not so much on objects, but on the rich types built into the language itself. Smalltalk, Java, C#... these are proper Object-Oriented languages, where it's not possible to write anything without first writing some objects/classes.
WispoWill wrote: But, honestly here, just as an example... Python makes all division operations integer by default. In order to get sane results you have to declare your variable as floating point first. Why is this really necessary?
This is really just a modern-script-language-feature thing, it's not anything to do with OO programming. Python tries to be convenient and if you use two integer values in a division, it presumes you want an integer result, because if you're working entirely with integers, that would be most convenient for you. If you want a float result, then convert to floats before you start (there's a handy float() method which does just that. There's also an int() that goes the other way, and a str() which... well, you'll get it.). In fact, only one operand needs to be a float to get a float result.

Also also:
WispoWill wrote:

Code: Select all

$ open_agency = [None]
Just in case you're unaware: I'm pretty sure this will give you a list with a single item in it, which is 'None'. So when you later insert a single item to the list and check the length, len will give you '2'. An empty list would have been:

Code: Select all

$ open_agency = []
WispoWill wrote:

Code: Select all

$ i = 1 ; j = 0 ; ixtemp = 0
I don't think you can break statements up with the semicolon like that in Python, but even if you can, it's worth knowing that the more-Pythony way of doing this would be:

Code: Select all

$ i, j, ixtemp = 1, 0, 0

Lastly, if you're writing a lot of Python in your Ren'Py script, then you can avoid having to put a '$' at the beginning of each line with a 'python:' block, which you can do anywhere:

Code: Select all

  e "This is the middle of a regular Ren'Py script"
  python:
    x, y = 10, 3
    f = float(x)/y
    if f > 3 and f < 4:
        renpy.say(e, "It works!")
Server error: user 'Jake' not found

WispoWill
Newbie
Posts: 13
Joined: Mon Mar 15, 2010 4:41 pm
Contact:

Re: Floating point to integer through nested list

#4 Post by WispoWill »

Thank you VERY much for your reply. It helped me in several ways, not the least of which was to help me see how cranky I sounded in the main message. I apologize for that.
What do you think you're writing? That's a tuple consisting of five elements, each of which is a single-element list containing a single integer each.
All Python documentation that I've seen so far have always shown tuples being defined as a = (1, 2, 3, 4, 5) . I had assumed that the parenthesis were both necessary and definitive to the tuple as a data type, just as the brackets were to list as a data type. I see from your clear examples this was a fundmental error in my thinking.
- to break it down:

Code: Select all

x = myVar[3] # x == ["d"]
x[0] # Get the first element in the list ["d"], which is "d".
Actually, in my original code the variable was coming off of a nested link. So in the above example wouldn't I want to use the following instead?

Code: Select all

x = myVar[3][:]
When I first started my project I spent several days trying to figure out how to construct muti-dimension array. I started with a tuple, discovered they were immutable, then when looking into lists saw a number of complaints on how that nested lists in Python create symbiotic links to the original data. Slicing with empty assignments seemes to be the universally recommended way to force the variable to do a copy instead of link.

The developers have stated that this is Python's correct behavior. I really can't agree with this behavior, but grudgingly admit that its the developers right to do what they wish with a language. It certainly seems non-obvious to me though.
I believe - although I could be wrong - that if you put your code in a .py file instead of a .rpy file, then Ren'Py won't molest it. But on the other hand, it's a little trickier to use in your script, IIRC you have to give it a namespace (possibly inherited from the filename? It's been a while since I played with this notion) and import it explicitly into your Python blocks.
Interesting. If that works like described it could be the answer to some ideas I've had. However, the whole project is taking a great deal longer than anticipated due to the many roadblocks I've had in learning to use Python correctly and the likelihood of my trying to actually implement those ideas continues to sink.
Again I could be wrong, but I think that all Ren'Py does is munge all the Ren'Py script into Python instructions (e.g. turn a dialogue line into a renpy.say() call) and then fiddle with the Python statements and blocks to use Rollback types and then pass the whole thing off to the Python interpreter as a Python script anyway. I think the only difference you're facing, in a Python line or block, is the rollback types. Certainly I would expect a Python statement to at least be run in eval(), as plain Python code straight to the Python interpreter.
I haven't been able to get For... Next loops to function in any meaningful way in RenPy. Another post on the forum led me to believe that this had been at least one thing that was disabled. Using the While statement works fine, but due to While not being a part of my early programming experience I habitually always use For.. Next loops.

There was at least one or two other commands I could never get to work under RenPy, but don't remember off the top of my head what they were.
Python tries to be convenient and if you use two integer values in a division, it presumes you want an integer result, because if you're working entirely with integers, that would be most convenient for you. If you want a float result, then convert to floats before you start (there's a handy float() method which does just that. There's also an int() that goes the other way, and a str() which... well, you'll get it.). In fact, only one operand needs to be a float to get a float result.
It's a bit scary that that actually made sense. ^_^
WispoWill wrote:

Code: Select all

$ open_agency = [None]
jake wrote: Just in case you're unaware: I'm pretty sure this will give you a list with a single item in it, which is 'None'. So when you later insert a single item to the list and check the length, len will give you '2'. An empty list would have been:

Code: Select all

$ open_agency = []
Unfortunately, empty lists kicks an "IndexError: list assignment index out of range" when trying to assign values from a variable rather than a constant. I initially started declaring the list with None so that I could use variables at will, then discovered that it is also quite useful when doing loops based on index values.

Code: Select all

i=1 ; j=len(open_agency)
while (i<j):
    $ temp = open_agency[i][0]
    i += 1
Since j is exclusive in the above, I don't have to be concerned with boundary errors in either direction if open_agency[0] is padded with nonfunctional data.
WispoWill wrote:

Code: Select all

$ i = 1 ; j = 0 ; ixtemp = 0
jake wrote: I don't think you can break statements up with the semicolon like that in Python, but even if you can, it's worth knowing that the more-Pythony way of doing this would be:

Code: Select all

$ i, j, ixtemp = 1, 0, 0
Actually, I only started doing this a few days ago after seeing the 2.6.5 documentation. It works just fine. Most languages I've been exposed to use the colon for this, but the first thing I learned about Python was that it uses the colon exclusively for blocks. I'm glad to know the "more Python way" though.

Thanks again for the reply. I'll be saving this page for reference for the next little while.

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: Floating point to integer through nested list

#5 Post by Jake »

WispoWill wrote: All Python documentation that I've seen so far have always shown tuples being defined as a = (1, 2, 3, 4, 5) . I had assumed that the parenthesis were both necessary and definitive to the tuple as a data type, just as the brackets were to list as a data type.
Yeah... I'd prefer if it was necessary, myself, it'd be neater - although I guess it's probably necessary for some other thing Python does. Quite possibly the multiple assignment, even! In case you're unaware, there's one other weird thing about tuples: an empty tuple is

Code: Select all

()
and a tuple with two items is

Code: Select all

(item1, item2)
but a tuple with only one item is

Code: Select all

(item1,)
- the comma is necessary, otherwise Python doesn't interpret it as a tuple, rather - IIRC - a straight reference to the variable or literal contained within the brackets. Again, I guess this because it's needed to work that way elsewhere.
WispoWill wrote: Actually, in my original code the variable was coming off of a nested link. So in the above example wouldn't I want to use the following instead?

Code: Select all

x = myVar[3][:]
Depends how much you care about not having the same reference, but yes - as you note, the slice with no params will make a simple copy of your list. In the context of the example, of course, they'll both return the same values.
WispoWill wrote: It certainly seems non-obvious to me though.
Oh, I have this reaction to about a third of the things I learn about Python. ;-)
WispoWill wrote: I haven't been able to get For... Next loops to function in any meaningful way in RenPy.
I've been using for loops quite extensively inside Python blocks inside Ren'Py script, but I wouldn't be at all surprised if they were a bit funny in python statements preceeded by a dollar sign.

(Of course, Python doesn't have a 'next', and you specify the bounds of your for loop differently to many other languages, but still...)
WispoWill wrote: Unfortunately, empty lists kicks an "IndexError: list assignment index out of range" when trying to assign values from a variable rather than a constant.
I'm not sure I follow what you mean - certainly in the context of the example code you posted in your first post, shouldn't you have been able to use append quite happily on an empty list? I'm pretty sure I've done that with no problems at all...
WispoWill wrote: I initially started declaring the list with None so that I could use variables at will, then discovered that it is also quite useful when doing loops based on index values.

Code: Select all

i=1 ; j=len(open_agency)
while (i<j):
    $ temp = open_agency[i][0]
    i += 1
For this kind of thing, could you not just use:

Code: Select all

python:
    for temp in open_agency:
        # temp == each item in list 'open_agency' in turn for each loop of the for
        # etc.
?

Unless your point was that an empty first item means you don't have to think in 0-indexed terms? It would seem to me a shame to lose out on conveniences like the one above just to avoid learning to index your lists by 0...
WispoWill wrote: Most languages I've been exposed to use the colon for this
Out of interest, what are those languages? I don't think I remember seing anything that uses colon like that, all the ones I can think of which allow you to write more than one statement on a line use the semicolon...
Server error: user 'Jake' not found

Post Reply

Who is online

Users browsing this forum: Bing [Bot]