Variables and math

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
It sounds like you want to use the current Focus value as a modifier to whatever you're trying to do with Corruption.

So (pseudo code):

Corruption = Corruption + ( Value - Focus )

So if you add 2 to Corruption, but Focus is 1.. then you want to add 1, not 2.
So if you add 2 to Corruption, but Focus is 2.. then you want to add 0, not 2?

Likewise..
So if you add 3 to Corruption, but Focus is 2.. then you want to add 1, not 3?

That sort of thing?

(Trying to get the question clear in my head, before Anne or Rich beats me to an answer :) )
Exactly!
I should have been able to make it that clear!
Do you read minds? If so, then please don't take everything you see too literal! ;)

Cheers - Kaffekop
 

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
It sounds like you want to use the current Focus value as a modifier to whatever you're trying to do with Corruption.

So (pseudo code):

Corruption = Corruption + ( Value - Focus )

So if you add 2 to Corruption, but Focus is 1.. then you want to add 1, not 2.
So if you add 2 to Corruption, but Focus is 2.. then you want to add 0, not 2?

Likewise..
So if you add 3 to Corruption, but Focus is 2.. then you want to add 1, not 3?

That sort of thing?

(Trying to get the question clear in my head, before Anne or Rich beats me to an answer :) )

Obvious flaw... what if Focus is higher then the value you're trying to apply?
... like what if you want do something like Corruption += 2, but Focus is 3? Does Corruption go down? Or does it just stay the same?
Whoops ... you edited the post.
Corruption should go down and eventually stop at 0 (zero). Didn't think of that, though ... hmmm

Cheers - Kaffekop
 

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
Something like this ?
Python:
init python:
    def addCorruption( step=1 )
        step = step - Focus
        if step < 0: return
        store.Corruption += step

default Corruption = 0
default Focus = 0

label start:
    # If not set, the the value added will be 1
    $ addCorruption()
    "Corruption [Corruption] / Focus [Focus]"
    $ addCorruption( 2 )
    "Corruption [Corruption] / Focus [Focus]"
    $ Focus += 1
    $ addCorruption( 2 )
    "Corruption [Corruption] / Focus [Focus]"
    $Focus += 2
    $ addCorruption( 2 )
    "Corruption [Corruption] / Focus [Focus]"
    $ addCorruption( 4 )
    "Corruption [Corruption] / Focus [Focus]"
    "END"
Tried this but I get a "parsing" error - whatever that may be.

Cheers - Kaffekop
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,620
15,601
Tried this but I get a "parsing" error - whatever that may be.
Oops, my bad. It should be def addCorruption( step=1 ): with the final ":".

It will teach me to not validate at least the parsing "because it's something so basic..."
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,620
15,601
So if you add 2 to Corruption, but Focus is 1.. then you want to add 1, not 2.
That's also what I understood, and so what I did in my example.


(Trying to get the question clear in my head, before Anne or Rich beats me to an answer :) )
I should have did that, it would have avoided the parsing error :D


Obvious flaw... what if Focus is higher then the value you're trying to apply?
Personally I assumed that you can't be less corrupted than you actually are, which seem the more natural answer to the problem ; you're facing a possibly corrupting situation, not praying in a convent lost in the middle of nowhere, so you can't be more pure after it.
Therefore, if the corruption factor minus the actual focus is negative, there's just no change of the corruption.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,602
2,242
Well, first things first... as much as people tend to use = with += or -=, you don't need to keep the calculation a simple "add simple number", "take away a simple number". You can do a full calculation, not dissimilar to my pseudo code above...

I tried this and I think it works more or less how you want it to...

Python:
default Corruption = 0
default Focus = 0
default prevCorr = 0

label start:
    scene black
    "Start: Corruption = [Corruption], Focus = [Focus]"

    $ prevCorr = Corruption

    $ Corruption = Corruption + 2 - Focus     # <-- This is the only line you really need, the rest is just proving the point.

    "[prevCorr] +2: Corruption = [Corruption], Focus = [Focus]" # display the results

    $ prevCorr = Corruption

    $ Focus = 1
    $ Corruption = Corruption + 2 - Focus

    "[prevCorr] +2: Corruption = [Corruption], Focus = [Focus]" # display the results

    $ prevCorr = Corruption

    $ Corruption = Corruption + 4 - Focus

    "[prevCorr] +4: Corruption = [Corruption], Focus = [Focus]" # display the results

    $ prevCorr = Corruption

    $ Focus = 2
    $ Corruption = Corruption + 2 - Focus

    "[prevCorr] +2: Corruption = [Corruption], Focus = [Focus]" # display the results

    "*** END FOCUS TEST ***"
I've use an addition variable "prevCorr" just to make the displaying easier.

You might want to make the whole thing a function, as Anne suggested.
That way, you can add in something that checks if the value goes below zero and stop it doing so.
(although honestly, I just tried to do a test program with a function... and learnt I really don't understand functions).
 
Last edited:
  • Like
Reactions: Kaffekop

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
@anne O'nymous
Code:
init python:

   def iCorr():
       t = Corruption - Focus
       if t < 0: return 0
       #  No need for the "else" here. If it's negative you already quit the function
       # with the "return" above.
       return t

   def sCorr():
       #  You already have a function which do the computation and validation,
       # so you just need to turn its value into a string.
       return str( iCoor() )
You made this for me before I went belly up ... shouldn't this do what I'm looking for?
And if so, how come it doesn't?

Cheers - a very confused Kaffekop
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,602
2,242
You made this for me before I went belly up ... shouldn't this do what I'm looking for?
And if so, how come it doesn't?
Yeah... this is more or less what I was messing with when I said I didn't understand functions either...

My variation was this:

Python:
default Corruption = 0
default Focus = 0

init python:

    def modifyCorruption( parm ):
        t = Corruption + parm - Focus
        if t > 0:
            return t
        else:
            return 0

label start:

    $ Corruption = modifyCorruption (+2)

    # blah, blah, more code.
It could of course also check if parm > Focus and do a slightly different calculation. Depending on how you want to treat negative results.

But this is where my knowledge failed me... since the value of Corruption never gets altered, and I didn't understand why.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,620
15,601
shouldn't this do what I'm looking for?
And if so, how come it doesn't?
No, because it's post processing. It do Y = Corruption - Focus, while what you want is Corruption = Corruption + ( X - Focus )
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,620
15,601
Python:
init python:
    def modifyCorruption( parm ):
        t = Corruption + parm - Focus
        if t > 0:
            return t
        else:
            return 0
But this is where my knowledge failed me... since the value of Corruption never gets altered, and I didn't understand why.
Er, the Corruption is correctly altered with your code (I just tested it). The only default (at least for my vision of the problem) is that it can decrease the value of Corruption.

On a side note, in case like this one, I recommend to beginners to split the computation, whatever they plan to effectively do.
Firstly, it decompose the process in basic operations, which help to found a logical error.
Secondly, it's easier to test. You can control the value after each step of the computation and found where the value isn't the one you expected. X = Y + Z... Why is X still increased while Z is higher than Y ? Oh, my bad, it should be a substraction.
Thirdly, because it's easier to change your mind. You just have to change basic operations, instead of rewriting a complex thing. Bonus, by just commenting out the previous code, you still have something that works, just in case.

So, something like :
Python:
init python:
    def modifyCorruption( parm ):
        parm -= Focus
        if parm > 0:
            return Corruption + parm
        else:
            return Corruption
Then, if you want that the corruption can decrease if Focus is higher than parm just do this :
Python:
init python:
    def modifyCorruption( parm ):
        parm -= Focus
#        if parm > 0:
        return Corruption + parm
#        else:
#            return Corruption
Personal note:
And that's when I hate the mandatory indentation of Python. With other languages, you can let the line return Corruption + parm at its different level of indentation. I don't count the number of time I forgot to change the indentation when doing this :(
 

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
Holy crap!
I just fiddled a bit on my own and ended up with something like this:
Code:
def iCorr():
        t = Corruption + (whatever - Focus)
        if Focus < 1:
            t = Corruption + whatever
        if t < 0: return 0
        #  No need for the "else" here. If it's negative you already quit the function
        # with the "return" above.
        return t

    def sCorr():
        #  You already have a function which do the computation and validation,
        # so you just need to turn its value into a string.
        return str( iCorr() )
It appears to be working!
I'll do some more testing when I've had dinner.

Cheers - Kaffekop
 
Last edited:

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
Dammit!
I thought I had nailed it!

If I do:
$ whatever = 1

It actually works, but only the first time I do this! When I get to the next time I want to do $ whatever = 1 it doesn't change anything.
And I thought I was on the verge of understanding something about this *sigh*

Cheers - Kaffekop
 

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
Hep guys. Thank you so much for trying to help me out here. I've been fiddling and cannot get this to work the way I want it to. This is what I've ended up with:

Code:
init python:

    def iCorr( parm ):
        parm -= Focus
        if parm > 0:
            return Corruption + parm
        else:
            return Corruption

    def sCorr():
        #  You already have a function which do the computation and validation,
        # so you just need to turn its value into a string.
        return str( iCorr( parm ) )


default Corruption = 0
default Focus = 0
default parm = 0


# The game starts here.

label start:

    e "Okay this is interesting."
    e "I'll try and see if Focus changes and is displayed correctly now."

    $ Focus += 1

    if Focus >= 1:
        e "Yay, it worked!"
    else:
        e "Argh, shit!"
        e " Back to the drawing board!"

    e "Now I'll introduce some Corruption"

    $ parm += 4

    e "Now, the Corruption should read as: 3 instead of the 4 I've added."

    if iCorr( parm ) < 4:
        e "Heya ... It bloody worked!"
    else:
        e "Shit! What the hell went wrong?"

    e "Hmmm, thinking again."
    e "What If we try and add some more Corruption ... like 5 more."
    e "As I see it, it should then read 7 because 5 minus 1 is 4 and 4 + 3 equals 7. No?"

    $ parm += 5

    if iCorr( parm ) <= 7:
        e "Fuck yeah! It works!!!"
    else:
        e "Dammit ... where did this go wrong?"



    e "You've created a new Ren'Py game."


    # This ends the game.

    return
If you feel you have the time try and give this a spin, I'd be grateful. If not, I understand completely.
It seems to me, that I am missing something fundamental when it comes to how math works or how to explain things.

Cheers - Kaffekop
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,602
2,242
Almost there...

Firstly, you don't need default parm = 0 ... parm is a local variable within the function. (By local, I mean it only exists as part of that function... I think RenPy actually calls it something else. But I tend to think in terms of local and global variables. Local ones are almost temporary, global ones exist everywhere. Most variables a novice like me would use would be my "global" variables. Though granted, my definition isn't perfect.

Secondly is how you're thinking about how the values are passed to/from function.
When you define a function with def iCorr( parm ):...
... you're effectively saying "create a function called iCorr, which has a parameter passed to it called parm".
That parameter is passed to the function by sticking it's value in the brackets when it's called.

Python:
init python:

    def iCorr( parm ):
        return            # for the purposes of this example, do nothing.

    def aFunc1 ():        # A function called "aFunc1" which doesn't use any values passed to it.
        return

    def aFunc2 (a):       # A function called "aFunc2" which is passed a value, that value is temporarily stored in local variable "a"
        return

    def aFunc3 (a, b):    # A function called "aFunc3" which is passed two values, stored temporarily as "a" and "b".
        return

    def aFunc4 (x):       # A function called "aFunc4" which is passed a value, stored temporarily as "x".
        return

label start:

    $ iCorr (+1)          # Use the iCorr function, pass it a value of +1

    $ aFunc1 ()           # Use the aFunc1 function, it doesn't have parameters, so none are passed to it.

    $ aFunc2 (+6)         # Call aFunc2, pass it +6

    $ aFunc3 ("a string", -6)    # The values passed to it don't need to be numbers and they don't need to be positive.

    $ aFunc4 (+6)         # Call aFunc4, same as aFunc2 - except the local variable will be "x" rather than "a"

    return
Now personally, I don't like calling the function's variables "a" or "x" or "n", because you might have other variables (like character objects) also using those same names. It probably doesn't matter, I think RenPy keeps them in separated variable spaces... but duplicated variables is something I've got in the habit of avoiding from other languages. That said... I've no problem defining lots of function that all use the passed variable as "parm" or "parm1, parm2", etc.


Finally, the function iCorr as it stands right now doesn't actually change the value of Corruption. All it does is return a value calculated using Corruption, Focus and the value passed to the function.

I think you might also be misunderstanding what actually happens with the value passed back from the function using return.
Essentially, if you do return parm, the value currently stored in "parm" is sent back as the return value.
But that return value needs to be stored somewhere or used somehow.
The next function that's called overwrites (well sort of), the last return value. Again, think of it as a temporary variable.

So, using iCorr as it exists right now...
Python:
# [...] blah, blah.

label start:

    $ iCorr (2)              # Call iCorr, returned value isn't used at all.

    if iCorr (2) >= 5:       # Call iCorr, the returned value is compared with 5, but not stored anywhere.

    $ tempCorr = iCorr (2)   # Call iCorr, store the returned value in "tempCorr"

I think what you actually want is a function that directly alters the value of Corruption, based on the value you passed to it and the value of Focus at that moment.
Python:
default Corruption = 0
default Focus = 0

init python:

    def iCorr( parm ):
        parm -= Focus
        if parm > 0:
            store.Corruption += parm
        return

label start:

    "Okay this is interesting."
    "I'll try and see if Focus changes and is displayed correctly now."

    $ Focus += 1

    if Focus >= 1:
        "Yay, it worked!"
    else:
        "Argh, shit!"
        "Back to the drawing board!"

    "Now I'll introduce some Corruption"

    $ iCorr (+4)

    "Now, the Corruption ([Corruption]) should read as: 3 instead of the 4 I've added."

    if Corruption < 4:
        "Heya ... It bloody worked!"
    else:
        "Shit! What the hell went wrong?"

    "Hmmm, thinking again."
    "What if we try and add some more Corruption ... like 5 more."
    "As I see it, it should then read 7 because 5 minus 1 is 4 and 4 + 3 equals 7. No?"

    $ iCorr (+5)

    if Corruption <= 7:
        "Fuck yeah! It works!!!  ([Corruption])"
    else:
        "Dammit ... where did this go wrong?  ([Corruption])"

    "*** END Corruption test ***"
And no, I've no clue why picking up the value of "Focus" within the function is fine, but altering the value of Corruption requires the store.Corruption.

But in this case, iCorr no longer returns a value. Instead it directly alters the value of Corruption when used.

Personally, I would rename iCorr to be something like addCorruption or something like that, which is essentially what it is doing now.
 
Last edited:

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
I,m too high right now to do anything other than write tis on a tablet.
Will check it out later when I return to normal.

Cheers - Kaffekop
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,620
15,601
Firstly, you don't need default parm = 0 ... parm is a local variable within the function.
That's why his first attempt don't worked :
Code:
def iCorr():
        t = Corruption + (whatever - Focus)
        [...]
whatever is an external value that need to be changed everytime.
Code:
def iCorr( whatever ):
        t = Corruption + (whatever - Focus)
        [...]

label blabla:
    $ Corruption = iCorr( 1 )
would have worked.


(By local, I mean it only exists as part of that function... I think RenPy actually calls it something else.
It should also call it local. Python redefined some terms, but local/global kept the same meaning.


But I tend to think in terms of local and global variables. Local ones are almost temporary, global ones exist everywhere.
More precisely it's a question of context.
Imagine your code like a house. You have many context in it. The first context is "whole house", then you have the context "kitchen", the context "living room", and so on.
Global variables are in the "whole house" context, so can be used anywhere on the house. This while local variables are in the "kitchen", "living room", or whatever context, and can only be used in the right room.
The difference is important because there's a whole world outside of the house (Python modules, Ren'py's core modules, ...). A world that create new contexts and where you can also found variables.
But it's relatively out of context, so I'll not go further.


It probably doesn't matter, I think RenPy keeps them in separated variable spaces...
It's Python which do it. It create a "namespace" (not sure it's the term for Python) for the function, and define the parameter inside it as local (to the function) variable.
In short, parameters are local to the function that any variable defined inside the body of this function.


That said... I've no problem defining lots of function that all use the passed variable as "parm" or "parm1, parm2", etc.
It's a matter of understanding more than anything. The parameter should have a name that will talk to you. Which is why I don't like this much your "parm". Here, "value" would be more appropriated, or "step" like I used myself.
So, you have JavaScript and other prototype based languages that use things like 'a', 'b' as parameter, because they are seen as anonymous variables. The order of the parameter depend of the letter. And you have other language that have more explicit names because the parameter are seen as real variables inside the body of the function.

Basically speaking, you should be able to see the declaration of the function and know what value is assigned to what parameter :
def increase( target, step ): First parameter is the name of the variable to increment, and the second is the value to add.
This versus def increase( parm, parm1 ): where you don't know if you should put the name first, then the value, or do the opposite. You'll need to look at the body of the function to see this.


Finally, the function iCorr as it stands right now doesn't actually change the value of Corruption. All it does is return a value calculated using Corruption, Focus and the value passed to the function.
Yeah. I haven't reread the thread, but I should have initially defined it to be used in a screen to display a computed value instead of the direct value.


And no, I've no clue why picking up the value of "Focus" within the function is fine, but altering the value of Corruption requires the store.Corruption.
Because Python don't know what you want to do.
When you wirte if Focus == 1, Python look at the local variable, don't found one named Focus, so it look in the global variable, and use the one it found here.
But when you write Corruption = 1, is it an assignation to the global variable named Corruption or the creation of a local variable named Corruption ? There's no way to know, so Python fallback to what seem logical, and it create a local variable named Corruption.
And finally, when you write store.Corruption = 1, you don't refer to a variable named Corruption but to an attribute named Corruption and part of the store object. So, Python look at the local variable, and like it don't found an object named store, it search in the global variables.
That's the trap with Ren'py. What we call "global" variables aren't in fact this global, they are attribute of the store object. It works fine like this, until the moment we need to use them in functions.
 

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
Thank you sooooo much for your patience and time.

def iCorr( whatever ): t = Corruption + (whatever - Focus) [...] label blabla: $ Corruption = iCorr( 1 )
That last part: $ Corruption = iCorr( 1 ), made it work

You've saved my day. If I'm ever in your neighborhood I owe both of you a beer!

Cheers - Kaffekop
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,620
15,601
You've saved my day. If I'm ever in your neighborhood I owe both of you a beer!
You talked about having dinner, at a time where I normally awake for works. So, I assume that you're more on the opposite side of the world, than near to me ;)
 

Kaffekop

Member
Game Developer
Jul 23, 2017
442
3,155
Well, you never know where ones travels might take you.
I'm from Denmark btw.

Cheers - Kaffekop