Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Floating-Point Arithmetic workaround and money conversion problems (Sugarcube 2)

I would like to make a little slotmachine for a bigger twine game.
First I thought, this would be no problem, and wrote the code below. But there is a problem with the money. Gambling, you know... ^^ Just kidding.... it's much more bad: Floating-Point Arithmetic!!! /o\
Sometimes some calculations like "5.23 dollars - 0.01 dollars" are not that easy. Not if you use this simple code below. For some reasons the answer of it is 5.220000000000001 dollars.

I have two Questions, now:
1. Is there some kind of workaround for this problem?
2. I would like to convert cent to dollars at some point in my game, but I don't know how, exactly. It shouldn't be very difficult. I think "print" could be one part of the answer for both problems. But I tried it and failed. Any ideas?




THE SLOTMACHINE

<<set $slot1 to either ("Lemon", "Apple", "Strawberry")>>
<<set $slot2 to either ("Lemon", "Apple", "Strawberry")>>
<<set $slot3 to either ("Lemon", "Apple", "Strawberry")>>
The machine makes a strange noise then it shows these symbols:

<<timed 1s>><<print $slot1>><</timed>>
<<timed 2s>><<print $slot2>><</timed>>
<<timed 3s>><<print $slot3>><</timed>>

<<if $slot1 is "Lemon" and $slot2 is "Lemon" and $slot3 is "Lemon">><<set $Win to 1>>

<<elseif $slot1 is "Apple" and $slot2 is "Apple" and $slot3 is "Apple">><<set $Win to 1>>

<<elseif $slot1 is "Strawberry" and $slot2 is "Strawberry" and $slot3 is "Strawberry">><<set $Win to 1>>

<<else>><<set $Win to 0>><</if>>

<<timed 4s>><<if $Win is 1>>YOU WIN!<<set $Money to $Money +0.25>><</if>><</timed>>

<<timed 4s>><<if $Win is 0>>YOU LOOSE!<<set $Money to $Money -0.01>><</if>><</timed>>

Your Money now: <<timed 5s>><<print $Money>> Dollars<</timed>>

Once again!->THE SLOTMACHINE
Stop gambling!

Comments

  • First. Please use the code tag when posting code—it's C on the editor bar.


    Do not use floating point math for currency as it will bite you in the arse—especially, if you do not have a firm grasp of floating point math. I would recommend either keeping track of the smallest unit of currency which will be available in your project—e.g. cents—or keeping track of the major divisions of currency separately—e.g. dollars and cents.

    Personally, I'd say just track cents and use widgets to make things easy on yourself.

    For example. Create a new passage, tag it with widget, then paste the following into it:
    <<widget "money">>
    \<<if $args.length is 0>>
    \$$<<print Number($Money + "e-2").toFixed(2)>>
    \<<else>>
    \<<set $Money to $Money + Number($args[0] + "e+2")>>
    \<</if>>
    \<</widget>>
    
    That will create a <<money>> widget macro which is used thus:
    → Print the current value of $Money (format: $#.##)
    <<money>>
    
    → Modify the current value of $Money in dollars
    <<money 100>>    → Add $100
    <<money -50>>    → Subtract $50
    <<money 0.25>>   → Add $0.25
    <<money -0.10>>  → Subtract $0.10
    
    You could separate the widget into two—one for printing, one for changes—if you wanted.


    OTHER TIPS:
    1. You should be controlling your line breaks better—whitespace in general, really. Your current code generates large quantities of unnecessary line breaks.
    2. You're using many <<timed>> macros, when one would be sufficient.
    3. If you simply want to check if all three slot reels are equal, then simply do that once. You do not need three separate clauses to do so.
    4. If you need boolean logic, then use real booleans.
    5. Use temporary variables for values which you do not need to track from passage to passage—e.g. the slot reels and win state should probably be temporary variables.
    6. Do not put whitespace between a function's name and it's invocation/parameter list parenthesis—i.e. either(…) rather than either (…).
    For example. Let's assume your StoryInit special passage contains the following:
    /*
    	$Money — Tracks money (in cents to avoid FP math shenanigans)
    
    	Initialize $Money to 0 cents and then use the <<money>> widget to add $100.
    */
    <<set $Money to 0>>
    <<money 100>>
    

    Here's what I'd suggest for your THE SLOTMACHINE passage:
    <<silently>>
    <<set _slot1 to either("Lemon", "Apple", "Strawberry")>>
    <<set _slot2 to either("Lemon", "Apple", "Strawberry")>>
    <<set _slot3 to either("Lemon", "Apple", "Strawberry")>>
    
    <<if _slot1 is _slot2 and _slot2 is _slot3>>
    	<<set _win to true>>
    	<<money +0.25>>
    <<else>>
    	<<set _win to false>>
    	<<money -0.10>>
    <</if>>
    <</silently>>
    \The machine makes a strange noise then it shows these symbols:
    
    <<timed 1s>>_slot1
    <<next>>_slot2
    <<next>>_slot3
    <<next>>
    YOU <<if _win>>WIN<<else>>LOOSE<</if>>!
    
    Your money now: <<money>>
    
    [[Once again!->THE SLOTMACHINE]]
    [[Stop gambling!]]
    <</timed>>
    
  • Great! A big THANKS for that. This is really helpful. :)
    Do not use floating point math for currency as it will bite you in the arse
    I really didn't want to use it, but I didn't know another way, till now.
    whitespace
    use the code tag
    Okay. The whitespace was for the readability (in this forum not in my game) - it isn't the originally passage. Now, that you told me about "C" I won't do it again.... that looks much better.
    If you need boolean logic, then use real booleans.
    I'm no "real" programmer, as you maybe see, so I really don't know what that means. Can you tell me about that, please?
    Use temporary variables for values which you do not need to track from passage to passage
    I will. But how and why would I do that?
  • edited December 2016
    art-star wrote: »
    Can you tell me about that, please?
    A Boolean variable is one that can only have a value of true or false.

    In your original example you had a $Win variable and if it was true that the player had won you set the variable to 1, otherwise if it was false that the player had won you set it to 0. So a better way to represent that situation using a Boolean variable would be:
    If they lost: <<set $Win to false>>
    If they won: <<set $Win to true>>
    
    To check if a variable is true: <<if $Win>>You won<<else>>You lost<</if>>
    To check if a variable is false: <<if not $Win>>You lost<<else>>You won<</if>>
    
    note: you don't have to include an <<else>> macro when checking a Boolean variable.


    art-star wrote: »
    But how and why would I do that?
    Variables that start with a dollar sign like $variable are added to the story's History whenever the reader traversals to another passage. (eg. via clicking on a link).

    There are times when you want to use a variable but you don't want it's value to be remembered after the current passage, in these cases you can use a temporary variable which is one who's name starts with an underscore instead.
    A normal variable which will be added to History:
    <<set $normal to "value">>
    
    A temporary variable which will be forgotten once the current passage is done.
    <<set _temporary to "value">>
    
    note: a temporary variable can be set to other value type than String, I just used that as an example.
  • @greyelf
    Thank you! That helps me! :)
  • edited December 2016
    Additionally, you might want to look at the SugarCube 2 documentation, in general, and its TwineScript document, specifically, which covers both story and temporary variables.

    It was covered fairly well by greyelf, however, you might find something insightful within the docs.
  • I would suggest, if you wish to track pennies... Use that as the "root value". We simplify money into hundredths, for human ease. In programming, you need to use the unsimplified root, pennies not dollars as the root.

    125 displayed as a two-decimal number, after dividing by 100 = $1.25

    Most games remove cents/pennies, and just append ".00" to the whole-dollar amount.
Sign In or Register to comment.