What are you worth? networth.ash will tell you.

EdFox

Member
There is a small looping bug that occurs when your store is completely empty, as mine is because I'm lazy:

Code:
> call scripts\networth.ash

Already updated from http://zachbardon.com/mafiatools/updateprices.php?action=getmap in this session.
Appraising inventory...
Requesting store inventory...
Store inventory request complete.
Requesting store inventory...
Store inventory request complete.
Requesting store inventory...
Store inventory request complete.
Requesting store inventory...
KoLmafia declares world peace.
 

Spiny

Member
Not sure what would be causing a number format exception from this script, but any time I run this script (dj_d's version from 9/30 or Zarqon's recent revision), the script runs thru to completion but spews out the following error report:

From dj_d's 9/30 version:
Code:
class java.lang.NumberFormatException: have any of your                        
java.lang.NumberFormatException: have any of your                              
        at net.sourceforge.kolmafia.utilities.StringUtilities.parseInt(StringUtilities.java:701)
        at net.sourceforge.kolmafia.textui.DataTypes.parseIntValue(DataTypes.java:218)          
        at net.sourceforge.kolmafia.textui.RuntimeLibrary.to_int(RuntimeLibrary.java:1508)      
        at sun.reflect.GeneratedMethodAccessor23.invoke(Unknown Source)                         
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)                                     
        at net.sourceforge.kolmafia.textui.parsetree.LibraryFunction.execute(LibraryFunction.java:119)                                                                                              
        at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:156)  
        at net.sourceforge.kolmafia.textui.parsetree.FunctionReturn.execute(FunctionReturn.java:95)                                                                                                 
        at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)      
        at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:127)                                                                                      
        at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:156)  
        at net.sourceforge.kolmafia.textui.parsetree.Operator.applyTo(Operator.java:279)          
        at net.sourceforge.kolmafia.textui.parsetree.Expression.execute(Expression.java:111)      
        at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:101)  
        at net.sourceforge.kolmafia.textui.parsetree.Operator.applyTo(Operator.java:279)          
        at net.sourceforge.kolmafia.textui.parsetree.Expression.execute(Expression.java:111)      
        at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:101)  
        at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)      
        at net.sourceforge.kolmafia.textui.Interpreter.executeScope(Interpreter.java:234)         
        at net.sourceforge.kolmafia.textui.Interpreter.execute(Interpreter.java:191)              
        at net.sourceforge.kolmafia.textui.Interpreter.execute(Interpreter.java:184)              
        at net.sourceforge.kolmafia.textui.command.CallScriptCommand.call(CallScriptCommand.java:193)                                                                                               
        at net.sourceforge.kolmafia.textui.command.CallScriptCommand.run(CallScriptCommand.java:63)                                                                                                 
        at net.sourceforge.kolmafia.KoLmafiaCLI.executeCommand(KoLmafiaCLI.java:473)              
        at net.sourceforge.kolmafia.KoLmafiaCLI.executeLine(KoLmafiaCLI.java:366)                 
        at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.handleQueue(CommandDisplayFrame.java:191)                                                                       
        at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.run(CommandDisplayFrame.java:172)

From Zarqon's version:
Code:
class java.lang.NumberFormatException: any of your
java.lang.NumberFormatException: any of your
        at net.sourceforge.kolmafia.utilities.StringUtilities.parseInt(StringUtilities.java:701)
        at net.sourceforge.kolmafia.textui.DataTypes.parseIntValue(DataTypes.java:218)
        at net.sourceforge.kolmafia.textui.RuntimeLibrary.to_int(RuntimeLibrary.java:1508)
        at sun.reflect.GeneratedMethodAccessor21.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at net.sourceforge.kolmafia.textui.parsetree.LibraryFunction.execute(LibraryFunction.java:119)
        at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:156)
        at net.sourceforge.kolmafia.textui.parsetree.Assignment.execute(Assignment.java:89)
        at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
        at net.sourceforge.kolmafia.textui.Interpreter.executeScope(Interpreter.java:234)
        at net.sourceforge.kolmafia.textui.Interpreter.execute(Interpreter.java:191)
        at net.sourceforge.kolmafia.textui.Interpreter.execute(Interpreter.java:184)
        at net.sourceforge.kolmafia.textui.command.CallScriptCommand.call(CallScriptCommand.java:193)
        at net.sourceforge.kolmafia.textui.command.CallScriptCommand.run(CallScriptCommand.java:63)
        at net.sourceforge.kolmafia.KoLmafiaCLI.executeCommand(KoLmafiaCLI.java:473)
        at net.sourceforge.kolmafia.KoLmafiaCLI.executeLine(KoLmafiaCLI.java:366)
        at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.handleQueue(CommandDisplayFrame.java:191)
        at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.run(CommandDisplayFrame.java:172)

Edit: Using mafia v7928, I learned what was causing the exception. It's line 61 of the script.

Code:
int storage_meat = excise(visit_url("storage.php"),"have "," meat").to_int();

The irony is that the code is a cleaner version of something I submitted a while back. The problem is that I have no meat in storage and what is being parsed is the words "any of your" from "Hagnk doesn't have any of your meat."

Clearly these words cannot be an int. So a tweak is in order.

I don't have a way of testing to see if this causes problems for accounts with meat in storage, but it is allowing the script to run without any exceptions or bombing while I have no meat in storage.

Code:
print("Sorting....","blue");
sort all by (value.price * value.amount);
int storage_meat = 0;
string storageparse = excise(visit_url("storage.php"),"have "," meat").to_string();
  if (storageparse == "any of your") storage_meat = 0;
    else storage_meat = excise(visit_url("storage.php"),"have "," meat").to_int();
 
Last edited:

zarqon

Well-known member
This is yet another example of why the change to to_int() actually makes the function stupider. There's no reason for to_int() to treat an arbitrary string with no numeric characters as anything but 0.
 

Bale

Minion
If there is anything between "1" and "2" it probably should not be treated as 12, but I feel that "I have 1 apple" could be treated as 1 without any error. I'd rule that extraneous characters before or after the number is okay, but not extraneous characters inside the number. Naturally, that is only if Veracity or holatuwol feel like putting in the effort to make it so.

Of course zarqon was complaining about the extremely simple case of no number inside the string being a 0.
 

Veracity

Developer
Staff member
I'm so used to using matchers to pull out what I want that it never occurs to me that the int parser should be looser to compensate for sloppy patterns. For example, to get storage meat, I would do this:

Code:
int storage_meat = 0;
matcher m = create_matcher( "You have ([\\d,]+) meat in long-term storage", visit_url("storage.php") );
if ( m.find() )
    storage_meat = m.group(1).to_int();

print( "You have " + storage_meat + " meat in storage" );

You find the pattern that (potentially) has the integer you want, and pull out exactly the integer. About the only way it could clearer, to my eye, is if we could do:

Code:
matcher m = create_matcher( "You have ([\\d,]+) meat in long-term storage", visit_url("storage.php") );
int storage_meat = m.find() ? m.group(1).to_int() : 0

I don't know "excise". I assume it finds stuff between other strings? If so, then the following is the equivalent:

Code:
int storage_meat = 0;
string str = excise(visit_url("storage.php"),"have "," meat").to_string();
if ( str != "any of your" )
    storage_meat = str.to_int();

It would probably be clearer, in that case, if we provided "is_numeric( string)", which will return true or false, depending on whether the argument would pass muster for to_int() or not. As well as the ternary conditional operator I suggested for my own example.

I disagree with zarqon's characterization of code that narrows its attention to the actual numeric part of a string as "stupider" than sloppy code which shrugs and passes any-old-crap-string to the ASH runtime library and expects THAT to do the smart thing. That seems exactly the bass-ackwards way to characterize it.

I am also sure that I do not want to_int to pre-process a string and search out a single region of digits as a potential number, and reject the string if there are more than one such region.

I don't really want to be the one defending this, since it was Hola's decision, but, since we've all been dealing with the repercussions for 3 weeks or more now, I've come to agree that it was the philosophically correct decision. It also caused pain within KoLmafia core code, since that, too, was using sloppy patterns and depending on lots of extra code in the shim function that took strings and passed them on to Java's integer conversion - which is very strict, as you are now seeing, given all the NumericFormat exceptions.

I'll submit is_numeric( string ) to the ASH runtime library. See if it helps.

Edit: I just have to comment on this particular statement:

There's no reason for to_int() to treat an arbitrary string with no numeric characters as anything but 0.
Really? There is "no reason" for it to behave other than what you suggest? "No reason"?

Try telling that to the Java runtime library. Give a string with no numeric characters to Integer.parseInt(). The specification of that function says "The characters in the string must all be decimal digits, except that the first character may be an ASCII minus sign '-' ('\u002D') to indicate a negative value." Given that, there's obviously "no reason" that it should return anything but 0 if it is given a string containing nothing but non-numeric characters, is there?

Oh, wait. That's wrong. It throws a NumericFormatException if you try to do that.

ASH's to_int() is a wrapper for Java's Integer.parseInt(). We trim off white space. We remove commas. Therefore, we relax the input expectations - somewhat - for you. We used to do a whole bunch more cpu and memory allocation intensive string processing, before passing the string along. We no longer do this.

But, you say, there is "no reason" that to_int() shouldn't be doing such-and-such additional work for you? There is "no reason"?

That seems sort of arrogant of you.

Here's a function for you:

Code:
int sloppy_coder_version_of_to_int( string arg )
{
    if ( is_numeric( arg ) )
        return arg.to_int();
    return 0;
}
 
Last edited:

Bale

Minion
Seriously, just saying that java doesn't work that way and you expect coders to put in the work to make sure they actually have integers would have been quite enough. Some people might think you just threw a tantrum. ;) Nobody intended to attack you or your work and I am truly sad if felt that way. As I said, "Naturally, that is only if Veracity or holatuwol feel like putting in the effort to make it so." Your answer is clearly no.

Personally I doubt I'll ever use is_numeric(), since if it returns false, I've obviously failed to find the integer I'm looking for. It is designed to deal with failure, but it won't promote success. Thanks anyway, for the sake of those who really do need it.

Sadly, zarqon was bitten by a regexp as a child and has been afraid of them ever since, so he'll have trouble adjusting.
 

Veracity

Developer
Staff member
Personally I doubt I'll ever use is_numeric(), since if it returns false, I've obviously failed to find the integer I'm looking for. It is designed to deal with failure, but it won't promote success. Thanks anyway, for the sake of those who really do need it.
If you use matchers, you should never need is_numeric(); m.find() returning false tells you all you need to know. It's intended for people using other methods - like excise() or substring() or whatever - for pulling out numbers from strings.

Your comment, Bale, about "if we want to make the effort to do such and such" referred to searching within a string for the single section that would pass is_numeric() and failing if more than one passed, right? Let's see:

If there is anything between "1" and "2" it probably should not be treated as 12, but I feel that "I have 1 apple" could be treated as 1 without any error. I'd rule that extraneous characters before or after the number is okay, but not extraneous characters inside the number.

Yes. You essentially suggested iterating over the characters of the string, remembering the start and end of the substring that represents a "number", failing if there are more "numbers" following the end of the first substring, and then MAKING a substring (memory allocation!) to pass to the integer parser.

The issue is just how much iterating over the string and memory allocation to copy around parts of it is appropriate for the runtime library. My answer is "as little as possible, since with just a little bit of care, the user program can pass in exactly what is expected."
 

zarqon

Well-known member
Sorry, I meant "no defensible reason according to my personal philosophy." I come from the school that thinks that well-designed functions should always return their return type no matter what. Therefore, a to_int() function should do its best to make an integer out of whatever is supplied, and if it can't, return 0. Empty string? Return 0. Non-numeric string? Return 0. String like "76.54"? Return 77. String like "I have 1 and you have 2"? Return 12, since it's the only result that makes any sense at all given the supplied string.

As always, it's the script's job to provide the proper data. The change just makes it more involved to supply that correct data -- moving the work that mafia used to do out into every script that needs to parse numbers from strings. Which is trickier for most scripters, particularly given ASH's relatively limited string-parsing functions and the steep learning curve for using regexes. I have already accepted the recent change, but in the interest of accessible scripting, I dislike it. And now I'm done -- since it seems that making a further case for the old functionality is unlikely to be well-received. I'll just have to write a wrapper to return ASH to its former ease of use, and is_numeric() will probably help with that.
 

Veracity

Developer
Staff member
The to_int() function used to to be much looser. It was capable of failing, though, in which case it threw a Runtime Error, complete with ASH filename and line number, just like dividing by zero, say.

Hola then changed the core function to try to convert using the strict rules and if that failed, to print a stack trace and try to convert again using the old rules (and presumably print another stack trace if that failed...)

I saw complaint after complaint after complaint from people that their old scripts were now printing stack traces - and the stack traces they emailed to me were completely useless in determining where the problem was in the script; no filename or line number, just big nested function calls in the ASH interpreter. Now, to_int was returning the old value - 0, usually, we assume - but there was never going to be an end to the stack traces.

Therefore, I made to_int trap the Numeric Format exception thrown by the internal function and throw a useful ASH runtime error. No stack trace, file and line number info - but it DID stop the script. As a result of this, various scripts have, in fact, been tightened up.

I suppose a compromise approach could be considered. The to_int() function could, as now, trap the exception. If it gets one:

1) PRINT a helpful error with file and line number but do not stop script execution
2) Return whatever the old method would have returned.

There will still be complaints: the extra messages will be seen as messing up the output. Which is true, I guess, although they are intended to help people fix their scripts. With that exception, however, the scripts would run as well (or as poorly) as they did before, even if they only previously worked by accident...

You, I gather, would refer 2 without 1; your harsh words about the "regression" of to_int(), and there being "no reason" that to_int() couldn't behave the old way make it very clear that you want the responsibility for cleaning up sloppiness to be in the runtime library, not in user code.

I don't really feel like doing anything more about this, at this point. I'd prefer that Hola weigh in, if he wishes, to debate whether the ASH runtime library should be strict (like that of every other programming language I know) or loose (like, perhaps, some scripting languages). I don't intend to say - or do anything more about Numeric Exceptions in ASH scripts.
 

zarqon

Well-known member
Actually, I would prefer 1 and 2. That would help people that care about the CLI "clutter" to make their scripts provide correct data, without breaking older scripts that relied on the previous functionality.

You have convinced me that there is a reason where I said "no reason" -- which is why I had to clarify that phrase above.

I had written a lengthy post inviting reconsideration and proposing solutions back when this change was first implemented -- but then I was cursed by Bale and couldn't post anything for several weeks -- and it was frustrating trying to play my characters amid holiday craziness while many of my scripts were broken. This was possibly the only time when I felt as though a change to mafia was regress rather than progress, but your explanations here have helped me to understand better the reasoning behind these changes. As I'd understood it, this was just a fix to processing floats expressed in scientific notation, which seemed like more of a special case.

When I could finally post again, I guess I was voicing opinions which had been simmering for a while so my language was stronger than necessary. I didn't mean anything personal by it and apologize if offense was given.
 

Veracity

Developer
Staff member
It was originally to solve the floats issue, but Hola decided to tighten up number parsing in general - and there were wide-ranging repercussions.

OK, I have implemented 1 + 2.

badnum.ash:
Code:
to_int( "1234" );
to_int( "3 blind mice" );
to_int( "+2345" );
to_int( "nonsense" );
to_int( "-6789" );
to_int( "-1 if by land, 2 if by sea" );
to_int( "12-23" );
yields:
> badnum.ash

The string "3 blind mice" is not an integer; returning 3 (badnum.ash, line 2)
The string "nonsense" is not an integer; returning 0 (badnum.ash, line 4)
The string "-1 if by land, 2 if by sea" is not an integer; returning -12 (badnum.ash, line 6)
The string "12-23" is not an integer; returning 0 (badnum.ash, line 7)
Notice that the script itself prints nothing; all the output is to_int() complaining.
 
Last edited:

zarqon

Well-known member
Indeed, this is excellent -- doesn't break old scripts, while letting them know that there has been a change. This allows scripters with many scripts (*cough* like myself) to remain operational, yet locate the problems and fix their scripts in their own time.

Thanks much for this.
 

Geno

New member
Apparently I could double my liquid meat if I sold everything off.

Liquid meat (eww): 28,233,249
Total: 52,146,576

Edit: I'm an idiot. >_>
 
Last edited:

namol

New member
You have 1 flaming juggler's balls worth 999,999,998 each for a total of 999,999,998
The string "have 1,175,459" is not an integer; returning 1175459 (networth.ash, line 53)
Liquid meat (eww): 1,189,818
Total: 1,143,040,574
 

mikey

New member
I'm new to using scripts and have no idea what this means. Can anyone help?

Undefined reference to function 'modifier_eval' (zlib.ash, line 120)
 
Top