Bug Weird variable parsing when calling JS functions

Semenar

New member
This was noticed first when Pantocyclus said that their JS script was failing to find any locketable monsters. It appeared that taking a number and incrementing it (i++), then calling toMonster with it as an argument always yields None.

During some prodding, the following was determined. The function toMonster fails because it thinks that the input variable is a float, so it is parsed to a string like "3.0", and of course there is no monster with ID "3.0". I modified the function and recompiled Mafia to return the type of input variable; the results are below. The listed variable is substituted as an argument for the toMonster function, like toMonster(3).

Int:
3
3.0
2.0 + 1.0
2.5 + 0.5
0.5 * 6
1.0 * 3
3 as a variable
Float:
3.0000001
2++ (a variable with the value of 2 is incremented before it is substituted)
Math.round(3)
Math.floor(3)
2.5 as a variable + 0.5 as a variable
1.0 as a variable * 3
1 as a variable * 3
2 as a variable + 1 as a variable
2 as a variable + 2 as a variable
2 as a variable + 2
1
0
-1

There are several weird results. For instance, -1, 0 and 1 are apparently floats, but any other integer is correctly identified as an integer. Additionally, using any operation on variables guarantees that the result will be a float, even if it should not be the case. This affects all functions that work with ID inputs, not only toMonster().
 
Last edited:

xKiv

Active member
Perhaps unnecessary - but a gentle reminder that javascript as designed does not have normal non-float [1] numbers. (there's BigInt but that's not very interoperable with other numbers).
What we are seeing here might be that 1) literal integers are somehow represented by an internal value that knows it's an integer (but now when it's 0, 1, or -1 ... I wouldn't be suprised if those went through boolean coercion first.
2) any arithmetic done at compile time knows what the result is before deciding whether to flag the value as an integer
3) runtime arithmetic always happens as number (operator) number = number, and so the result is always just float

[1] IIRC technically it's double (I am used to thinking of float as functionally identical to single)

I am not sure how kolmafia can handle this. Options I see:
1) if the called function can have int arguments in some of its arguments, don't just check value type but also whether the value itself can be converted to an int without loss of precision. Disadvantage: the code cannot call any float versions of such functions if the argument would be int-able.
2) add special (differently named) versions of selected functions, with int arguments only, and make sure that their parameters are always coerced to int (or error out if they can't). Disadvantage: a lot of work and name pollution.
3) add a flag that can somehow be set from the js side, and would force the behavior from point 1 ... for the next funtion call. Disadvantages: possible spooky action at distance (the flag can be set (or not) by a completely independent part of code which doesn't event know that whatever follow will call a function for which the flag matters), that's not how anything should be done, code bloat, js coders have to remember that this is how you handle it specifically in kolmafia
4) pass the values as BigInt anyway, like "var t=0; to_monster(BigInt(++t));" [2]. Disadvatages: code bloat, most people don't even know it exists (but at least it's standard js feature), kolmafia would have to learn how to unpack a js BigInt into a mafia int.

[2] note: t++ would still pass a value of 0 into the function before incrementing, that's why it's called the *post*increment operator. You want the preincrement operator ++t to pass the value 1 here ...
 
Top