Super Newbie questions!

BlitzMax Forums/BlitzMax Beginners Area/Super Newbie questions!

xuchuang(Posted 2006) [#1]
Hi,

This is the first piece of code I wrote in BMax, it's supposed to read a preference txt file which contains settings:

--------------------------------------------------------------------------------------------
Strict

Global SettingsFile$ = "test.txt"
Global User_Credits%, Base_Points%, Talent_Amount#, ReGen_Cost%, Break_Min#, Break_Max#, Traits_Total% '
Global file:TStream = ReadFile(SettingsFile)

If Not file RuntimeError "could not open file " + SettingsFile

While Not Eof(file)
Local CurrentLine$ = ReadLine(file) 'Reads a line from the file
'Local Label$ 'How come the code didn't work (No error message) if I decleared label here instead of inside the loop?

If CurrentLine [0..1] <> "'" Then
Local BeginPos = CurrentLine.Find(":")+1 'Find the : mark then increase the postition by 1

Local Label$ = CurrentLine [0..CurrentLine.Find(":")] 'Cut the string from the start of the string til the ':' character, not including the ':'

Select Label
Case "User Credits" User_Credits = Int CurrentLine[BeginPos..CurrentLine.Length]
Case "Base Points" Base_Points = Int CurrentLine[BeginPos..CurrentLine.Length]
Case "Talent Amount" Talent_Amount = Float CurrentLine[BeginPos..CurrentLine.Length]
Case "ReGen Cost" ReGen_Cost = Int CurrentLine[BeginPos..CurrentLine.Length]
Case "Break Min" Break_Min = Float CurrentLine[BeginPos..CurrentLine.Length]
Case "Break Max" Break_Max = Float CurrentLine[BeginPos..CurrentLine.Length]
Case "Traits Total" Traits_Total = Int CurrentLine[BeginPos..CurrentLine.Length]

End Select


End If

Wend

Print User_Credits
Print Base_Points
Print Talent_Amount
Print ReGen_Cost
Print Break_Min
Print Break_Max
Print Traits_Total

CloseStream file
--------------------------------------------------------------------------------------------
The settings file is like this:
'Test Settings file
User Credits: 10000
Base Points: 150
Talent Amount: .15
ReGen Cost: 200
Break Min: .45
Break Max: .55
Traits Total: 9


My question is:

1. Why all three float numbers end up having not quite accurate values, for example Break_Min, instead of .45, it got 0.449999988
2. how do I round numbers to two decimals? In VBA I'd use ROUND but I think it's something different in Bmax?
3. There are better ways to do this right?

I'm no programmer at all, have only been doing VBA scripts for 2 weeks and bought Bmax just yesterday, so please go easy on me ;D

I'm a game artist, been doing that for many years, but want to get into actually design some games myself hahaha, dunno if I could ever do that, being able to illustrate my ideas with code would be cool in enough!

Thanks!


Dreamora(Posted 2006) [#2]
1. Because floats are not precise enough, you need to use double for more accurate values.

2. There is no function for that. The simplest way might be
function round:string(value:double,digit:int)
  ' return type must be string otherwise it will have more than 2 digit
  value :* (10:double)^digit
  local valuei:int = int(value)
  local result:string = string(valuei)
  result = left(result, result.length - digit) + "." + right(result,digit)
  return result
end function


3. Better is a thing of definition. You could use some kind of an ini file and parsing, would perhaps be cleaner. Or if you know the order in which the data are in the file, you can simply assign them. (I do that with my language file, only the content, no key names in front etc. Allows straight reading into a string reference)


Chris C(Posted 2006) [#3]
check out maXML module, think you'll find a link on module tweeks forum, idea for config files make things real easy from the programming point of view


xuchuang(Posted 2006) [#4]
Hi thanks for the quick response! I just tried double, still got a long number. I'll try to use your function.. but hmm I don't understand what you are doing in there so will probably take me a while ;0

thanks again..


VP(Posted 2006) [#5]
What Dreamora was aiming for was:
Function round:String(value:Double,digit:Int)
  ' return type must be string otherwise it will have more than 2 digit
 
  ' value = the number you want to be rounded.
  ' digit = the number of digits to round to.

  value :* 10!^digit
 
  ' so, if value = 0.45 and digit = 2, value will now be 0.45*100 = 45

  Local valuei:Int = Int(value) ' Converts 'value' to an integer.
  Local result:String = String(valuei) ' 'result' is now a string containing "45"
 
  result = Left(result , result.length - digit) + "." + Right(result , digit)
  ' this just formats the string to display the correct decimal value. 

  Return result
End Function

Print round(.45!,2)


Make sure you feed the function a double (put a ! on the end of the values you send it, or make sure you use a variable of type double).

There's a better way of doing this, I'm sure.


Dreamora(Posted 2006) [#6]
oops yeah my stupidness, pressed * instead of ^. corrected that.


VP(Posted 2006) [#7]
A better way:
Function round2:String(value:Double , digit:Byte)
  Local result:String = String(value)
 
  result = Left(result , digit + Instr(result , "." , 1) )

  If digit = 0
    result = Left(result , Len(result) - 1)
  EndIf
	
  Return result
End Function


Print round2(.45! , 2)
Print round2(10! , 2)
Print round2(15.3333! , 2)
Print round2(2.2! , 5)
Print round2(1234.5678!,1)
Print round2(99.99!,0)

This will always round down, so round2(55.59,1) will yield "55.5".


Dreamora(Posted 2006) [#8]
There is a problem with that: Different language versions of Windows have different handling for the digit point which is why this will fail. (you could use a replace(result,",",".") before the result = left thought)


VP(Posted 2006) [#9]
Good point, I didn't even think of that. Thanks Dreamora :)