While loop does an extra iteration.

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
Saltome
Veteran
Posts: 237
Joined: Sun Oct 26, 2014 1:07 pm
Deviantart: saltome
Contact:

While loop does an extra iteration.

#1 Post by Saltome » Sat Nov 01, 2014 9:56 am

So I'm fiddling here with some code, don't mind the clutter, but I noticed some suspicious behavior.
Now I could be completely out of line, and I'm just missing something obvious, but here's the thing.
The loop inside the fight function appears to repeat an additional time.
The condition says run while the value is higher than 0
Initially the value is 1 and during each iteration it's reduced by .1
So far so good, but it does not stop once the value reaches 0
The loop breaks only after the value reaches -.1
Further more, the problem does not exist if you scale up the values, hp to 10 and the decrease to 1, which leads me to believe the condition of the while loop is not resolved correctly when dealing with float.
A last minute test indicates that the problem is resolved if hp is 2 and the condition is changed to >1
The fact that the function call is in a python block might have something to do with it.
Oh, and the Rne'py version is 6.18.3

As a matter of fact earlier I had a problem where instead of 0 the string would show some random number.
Then the code was:

Code: Select all

renpy.say("", "Hit! " +str(b.hp))
Instead of:

Code: Select all

renpy.say("", "Hit! (%f)" %b.hp)
The new approach seems to format the variable to show 0 when it's supposed to be 0.
But perhaps internally the value is still messed up which confuses the actual comparison.

Code: Select all

# You can place the script of your game in this file.
#define Combatants=[]

init python:
    
    class combatant:
        name=""
        hp=1
        strength=1
        endurance=1
        dexterity=1
        agility=1
    
    
    def fight(a,b):
        while b.hp>0:
            if a.dexterity*renpy.random.random() > b.agility*renpy.random.random():
                b.hp-=.1
                renpy.say("", "Hit! (%f)" %b.hp)
            else:
                renpy.say("", "Miss!")
        return renpy.random.choice([a,b])
# Declare images below this line, using the image statement.
# eg. image eileen happy = "eileen_happy.png"

# Declare characters used by this game.
define Player = "???"


# The game starts here.
label start:
    python:
        Combatants=[]
        i=0
        while i<10:
            Combatants += [combatant()]
            Combatants[i].name=str(i)
            i+=1
#    "[Combatants]"

    Player "Ah, the arena... finally. Let's see where this gets me."
    python:
        result=fight(Combatants[0],Combatants[1])
    "[result.name]"
    return
Deviant Art: Image

User avatar
PyTom
Ren'Py Creator
Posts: 15893
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

Re: While loop does an extra iteration.

#2 Post by PyTom » Sat Nov 01, 2014 10:31 am

Ren'Py is operating normally. What looks like is happening is that your loop is running with b.hp == 0.1, which then gets .1 subtracted, and it displays 0.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
"Silly and fun things are important." - Elon Musk
Software > Drama • https://www.patreon.com/renpytom

User avatar
Saltome
Veteran
Posts: 237
Joined: Sun Oct 26, 2014 1:07 pm
Deviantart: saltome
Contact:

Re: While loop does an extra iteration.

#3 Post by Saltome » Sat Nov 01, 2014 11:22 am

Nope, in the last iteration it shows -.1 that means it thinks that the conditions are met even when the value should be 0.
Ahem, did you notice the fact that I am returning the combatant then printing it's "name" to the screen, that happens to be 0 or 1. So the last number the game shows is not the value of b.hp, it's either b or a's name.

Anyway I did another test with a counter, it proves that it loops 11 times instead of 10.

Let's make sure there is no confusion. I cut out everything non-essential.

Code: Select all

label start:
    python:
        value=1
        count=0
        while value>0:
            count+=1
            value-=.1
            renpy.say("", "Count: " + str(count)+" Value: "+str(value))
    return
This code loops 11 times not 10. Note the Value on the 10th iteration.
Deviant Art: Image

User avatar
Milkymalk
Miko-Class Veteran
Posts: 752
Joined: Wed Nov 23, 2011 5:30 pm
Completed: Don't Look (AGS game)
Projects: KANPEKI! ★Perfect Play★
Organization: Crappy White Wings
Location: Germany
Contact:

Re: While loop does an extra iteration.

#4 Post by Milkymalk » Sat Nov 01, 2014 11:37 am

I think it has to do with the way floats work. When you subtract 0.1, you are really subtracting, say, 0.09999999999999653445. So after subtracting that from 1.0 ten times, you are left with a number > 0.

Always check loops against an integer.

Just curious, why do you even need fractions of hit points instead of only integer numbers?
Crappy White Wings (currently quite inactive)
Working on: KANPEKI!
(On Hold: New Eden, Imperial Sea, Pure Light)

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: While loop does an extra iteration.

#5 Post by xela » Sat Nov 01, 2014 11:56 am

This is not a correct forum to discuss pure python implementations. If you're curious about what's going on (and how to fix the issue), read this:

https://docs.python.org/2/library/decimal.html

Otherwise this is from a IDLE (Pure Python 2.8.7):

Code: Select all

Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> v = 1
>>> c = 0
>>> while v > 0:
	c += 1
	v -= 0.1
	print c, v

	
1 0.9
2 0.8
3 0.7
4 0.6
5 0.5
6 0.4
7 0.3
8 0.2
9 0.1
10 1.38777878078e-16
11 -0.1
and you issue has got nothing to do with Ren'Py.
Like what we're doing? Support us at:
Image

User avatar
Saltome
Veteran
Posts: 237
Joined: Sun Oct 26, 2014 1:07 pm
Deviantart: saltome
Contact:

Re: While loop does an extra iteration.

#6 Post by Saltome » Sat Nov 01, 2014 1:26 pm

This is a more reasonable response. I'm new to both Ren'py and Python so I can't say where the cause of the specific problem lies but I've never had so much trouble with floats before. I am aware that float numbers can have some funny side effects dealing with odd numbers, but I find it surprising that there is an uneven remainder when calculating even fractions.

As for the question why floating numbers. Well, it's just what I got used to. Originally I would use them as a level up system for stats, but then I just started using the exact value to calculate things more precisely. I could use int but the precision of an integer scale is limited to the base integer itself, while floats calculate things in higher precision without inflating the number unnecessarily.

PS: That still doesn't explain the inconsistency. If subtracting .1 ten times from 1 is > than 0 why is subtracting it from 2 is not higher than 1?
Deviant Art: Image

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: While loop does an extra iteration.

#7 Post by xela » Sat Nov 01, 2014 6:40 pm

Saltome wrote:This is a more reasonable response. I'm new to both Ren'py and Python so I can't say where the cause of the specific problem lies but I've never had so much trouble with floats before. I am aware that float numbers can have some funny side effects dealing with odd numbers, but I find it surprising that there is an uneven remainder when calculating even fractions.
I was under the impression that most programing languages including very popular once like C/Java worked in the same way and required intervention just like Python does when comparing floats that don't have exact binary equivalents under normal operations.
Saltome wrote:PS: That still doesn't explain the inconsistency. If subtracting .1 ten times from 1 is > than 0 why is subtracting it from 2 is not higher than 1?
http://www.johndcook.com/blog/2009/04/0 ... nt-number/

You can google for better or simpler examples but this article does sufficiently explain the topic.

I think the simplest possible solution for your case is:

Code: Select all

>>> v = 1
>>> c = 0
>>> while round(v, 1) > 0:
	v -= 0.1
	c += 1
	print c, v

	
1 0.9
2 0.8
3 0.7
4 0.6
5 0.5
6 0.4
7 0.3
8 0.2
9 0.1
10 1.38777878078e-16
>>> 
You can encounter some limitation if you round to second decimal point (not first) but those are less common.
Like what we're doing? Support us at:
Image

Post Reply

Who is online

Users browsing this forum: TioNick