First (real) attempt to make an ash script: Sewer.ash

me259259

Member
I was in the middle of writing a grate/valve check. I was hoping to set it so that if less than 20 grates/valves have been opened/turned, the script would be helpful and open/turn the grates/valves, and then take the tunnels when all the grates/valves have been opened/turned. Since I want to try to work it out for myself (because it's easier for me to learn this way), I'm not going to look at your code until I finish the code I'm working on for that section.

For the record, complicated is good. Challenge is great. The more I have to learn, the better :D

I think I'll try the visit_url() and run_combat() thing you mentioned. I wasn't really happy with the way the script handled the actual adventuring (especially if you have to gnaw through the cage). I have a feeling that's going to make a lot of things a lot easier...
 
Code:
int grates_open()
{    
    print( "Checking grates" );
    int count = 0;
    int open_grates = 0;
    string parsed_grates = visit_url( "clan_raidlogs.php" );
    if( contains_text( parsed_grates , "sewer grate" ) )
    {
        string p = substring(parsed_grates , index_of( parsed_grates, "sewer grate" ));

        string leftover(string s)
        {
            p = substring(s, index_of( s, "sewer grate" ));
            p = substring(p, index_of(p, "("));
            int i = to_int(substring( p, 0, index_of( p , " turn" ) ));
            count = count + i;
            return p;
        }
    
        while( contains_text( p, "sewer grate" ) )
        {
            leftover(p);
        }
    }
    open_grates = count;
    print( count + " open grates" , "blue" );
    return open_grates;
}

Seems like a matcher would be a lot easier. Like the following:
PHP:
int grates(){
    int grates;
    matcher parser = create_matcher("sewer grates? \\((\\d+) turns?", visit_url("clan_raidlogs.php"));
    while(parser.find()) grates += parser.group(1).to_int();
    return grates;
}
I don't know a ton about regexes, but I started using matchers last year when I wrote a hobopolis script and they make parsing the raidlogs rather trivial.
 

Grotfang

Developer
That looks good. I shall check it out and then make the change. Many thanks :)

To be honest, this code was written some time ago and, since it works, I haven't tried to improve it. The file it is a part of is rather large, and I avoid spending time rewriting to replicate existing functionality.
 

StDoodle

Minion
I don't know a ton about regexes, but I started using matchers last year when I wrote a hobopolis script and they make parsing the raidlogs rather trivial.

Indeed! Or at least, trivial compared to the alternatives. ;) I'd played a little with regex before, but when the Hobelfs came around and I was one of those in charge of distro for HCN (#2 total Crimbo runs in KoL, #1 Whiskers) I pretty much had to write a script to help out. It parsed a forum post with a line for each player, their equipment total & desires, and their running totals of turns, and then parsed the logs for turns spent in sewers & elf alley. There was quite a bit of regex in that thing; but as complicated as it was to put together, I can't imagine having done it with sub_string() and index_of().
 

me259259

Member
Ok, in my spare time today, I've been fiddling with the whole adventuring part of the script. This is what I had before:

PHP:
adventure(100 , $location[a maze of sewer tunnels] );

Which was nice... unless you get trapped in the c.h.u.m. cage multiple times, in which case 100 adventures may not be enough. Instead of just increasing the number to 1000, I wanted to add some support for cage victims... as well as find an easy way to change choice adventure settings to turn valves and open grates while you're adventuring.

Here's what I have so far:

PHP:
//sewer url
string sewers = visit_url ("adventure.php?snarfblat=166") ;

repeat 
	{
	// if it's a combat adventure, obey the custom combat script
	if (contains_text( sewers , "combat"))
		run_combat();
	// if you gnaw through the C.H.U.M. cage, make a note of it
	if (contains_text( sewers ,"Do you think you're popular enough") && waitForRescue == false && poorteeth < toothTolerance )
		{
		poorteeth +=1 ;
		print ("Ok, you just had to gnaw your way out of the cage.  Let's wait 30 seconds and let someone else be cage bait");
		wait (30);
		}
	if (contains_text( sewers ,"Do you think you're popular enough") && waitForRescue == false && poorteeth == toothTolerance)
       //toothTolerance is a user defined term for what is the max number of times they want to gnaw out of the cage
		abort ("you already wasted" + toothTolerance * 10 + " turns gnawing out of the cage.  Wait an hour and run this script again, unless you want to waste even more adventures");
	// if the sewer is flooded while the script is running... abort
	if (contains_text( sewers , "terrible ghastly torrent of raw sewage"))
		abort ("Sewers are being reflooded.  Wait until the dungeon is open again");
	}
until (contains_text( sewers ,"onward and... downward!"))

I spent a good long while looking at the wiki, and I have a few questions:

run_combat will handle when the adventure is a combat adventure. Is there a command to select X choice in a choice adventure? Or a function that will automatically pick the choice you have set in your preferences?

Is this the right syntax for contains_text? Is there a better way to say "check the current page for this text"?
 
Last edited:

Winterbay

Active member
I think for choice adventures you will have to use the appropriate visit_url for that choice adventure. I don't think there is a specific command for it. In order to find the correct URL to call you can use the Mafia mini browser to navigate to the adventure in question.
 

Grotfang

Developer
Two things:

Firstly, that will loop (I think). You need to call your visit_url again within the repeat loop to redefine your string, otherwise your contains_text( sewers , ... ) will always return the same result.

Secondly, choices need to be handled separately. Use a run_choice function (or something similar). A small example:

Code:
string run_choice( string page_text )
{
	matcher m_choice = create_matcher( "whichchoice value=(\\d+)" , page_text );

	while( contains_text( page_text , "choice.php" ) )
	{
		m_choice.reset( page_text );
		m_choice.find();
		string choice_adv_num = m_choice.group( 1 );
		
		string choice_adv_prop = "choiceAdventure" + choice_adv_num;
		string choice_num = get_property( choice_adv_prop );
		
		if( choice_num == "" ) abort( "Unsupported Choice Adventure!" );
		if( choice_num == "0" )
		{
			exit;
		}
		
		string url = "choice.php?pwd&whichchoice=" + choice_adv_num + "&option=" + choice_num;

		page_text = visit_url( url );
	}
	
	if( contains_text( page_text , "Combat" )) run_combat();
	return page_text;
}

This worked a couple of months ago. Hasn't been tested in a while. Note, I use exit on encountering a choice of 0. This allows the script to gracefully finish if it finds a grate, for example, without aborting. Just a thought.
 

Theraze

Active member
This is what I use... it's from Rinn's quest pack, specifically in the Find Adventure file:
PHP:
string run_choice( string page_text )
{
 while( contains_text( page_text , "choice.php" ) )
 {
  ## Get choice adventure number
  int begin_choice_adv_num = ( index_of( page_text , "whichchoice value=" ) + 18 );
  int end_choice_adv_num = index_of( page_text , "><input" , begin_choice_adv_num );
  string choice_adv_num = substring( page_text , begin_choice_adv_num , end_choice_adv_num );
  
  string choice_adv_prop = "choiceAdventure" + choice_adv_num;
  string choice_num = get_property( choice_adv_prop );
  
  if( choice_num == "" ) abort( "Unsupported Choice Adventure!" );
  
  string url = "choice.php?pwd&whichchoice=" + choice_adv_num + "&option=" + choice_num;
  page_text = visit_url( url );
 }
 return page_text;
}
 

Theraze

Active member
One potential issue with Grotfang's run_choice... since it also has a check for combat, it will try to run combat on the Blackberry Cobbler (Blackberry Combat Boots) as well the Space Trip Combat choice adventure.
 

Grotfang

Developer
That's true. It will. But this is to be used in the sewers, and the combat check means it will run_combat() on the monsters encountered if you are grating and have "2" in your preferences for all the non-grate non-combats.
 

Theraze

Active member
True... tricky combat non-combats. Vicious of them. Suppose there could be a manual check if the page url has changed, but... probably overworking it if you're just using this in the sewers, not as part of your standard set.
 

Fluxxdog

Active member
Code:
buffer run_choice(buffer input,string where){
	while(contains_text(input,"action=choice.php")){
		setupchoice(where);
		int ch_adv=to_int(excise(input,"whichchoice value=","><"));
		if(ch_adv==0) bad_exit_print("Error finding choice adventure number!");
		if(prop_count("choiceAdventure"+ch_adv)<1) good_exit_print("Full stop for manual action!");
		input=visit_url("choice.php?whichchoice="+ch_adv+"&option="+prop_count("choiceAdventure"+ch_adv)+"&pwd");}
	return input;}
buffer run_choice(buffer input){ return run_choice(input,my_location());}
I wrote this bit after frustration with the Spooky Forest. setupchoice() changes the choices for adventures based on current circumtances. For example, to get the spooky sapling, it'll keep adventuring until:
1) First sell you bar skin(s)
2) Buy a sapling if you don't have one
3) Get the heck out of there

The problem is, there's no way to access the Louvre mapper, Hunt the Wumpus, or the Violet Fog solvers that are built in to mafia without learning how the preferences are set up and parse the hell out of them.. And I've not seen anything on the wiki aboutit. Seriously, search for louvre in the wiki and you get nothing. I would have been happy with louvre(), wumpus(), and violet_fog() functions.

... Now I got to run to the FReq board.

Edit: Oh, the whole reason I put this up is after run_choice() finishes, you go right in to a check for a combat. There are several places you can get in to fights after a choice. Spooky Forest, "Fun" House, the Barrr, Cyrpt mid-bosses, and that's just off the top of my head.
 
Last edited:

me259259

Member
Ineptitude

Hmm... problem. I am determined to understand every part of sewer.ash. However, my very limited time looking at code has caught up with me. Remember, while I'm starting to delve into computer programming, I'm still very new at this. I have quite a few questions this time around... and they probably have to deal with conventional programming language syntax that I should know, but haven't yet.

These questions are about Grotfang's code:

PHP:
string run_choice( string page_text )

Ok, this thing. Is this defining strings? Why doesn't it need a semi-colon? What exactly is this doing? I tried looking it up on the wiki but there isn't a run_choice page like there is a run_combat page.

PHP:
matcher m_choice = create_matcher( "whichchoice value=(\\d+)" , page_text );

What is (\\d+)? It's just a placeholder for a number right? Or is there more to it?

PHP:
m_choice.reset

What does the "." do? Does it tie reset to m_choice, so that there's no confusion on what it resets? Is there any more to it than that?

PHP:
string choice_adv_num = m_choice.group( 1 );
		
		string choice_adv_prop = "choiceAdventure" + choice_adv_num;
		string choice_num = get_property( choice_adv_prop );
		
		if( choice_num == "" ) abort( "Unsupported Choice Adventure!" );
		if( choice_num == "0" )
		{
			exit;
		}
		
		string url = "choice.php?pwd&whichchoice=" + choice_adv_num + "&option=" + choice_num;

I think I know what this does, but I don't think I know how it does it. Does this make all choice adventures take option 1?

The code Theraze brought up is much easier to understand... and while this works in the sewers for most people... It's probably be better for me in the long run to try and understand Grotfang's suggestion.
 

Theraze

Active member
Okay... first, the string run_choice( string page_text) part is defining the function. It says that there is a function, that will be called using a string (page_text), and that will return a string (the string run_choice) when it ends. So print(run_choice(<some page>)) would return whatever the run_choice function figures out of the <some page> contents. Specifically, it should return the text from the choice page it visited next.

Second, the create_matcher. It's looking for where you have (in the text) whichchoice value=<number>. The (\\d+) means to match some amount of numbers, somewhere in the page, immediately after the text "whichchoice value=" occurs.

Yes, it means that you're resetting m_choice and if you search again, it will do a new match.

m_choice.group( 1 ) means to return the first match... if you have multiple bits matching, you may have higher numbers in your group, but... no, it doesn't mean all get choice one. :)
 

slyz

Developer
Ok, this thing. Is this defining strings? Why doesn't it need a semi-colon? What exactly is this doing? I tried looking it up on the wiki but there isn't a run_choice page like there is a run_combat page.
Actually, this is a function definition. He is creating a function called run_choice, which takes a string as argument (the part between parenthesis), and returns a string. More details here

The rest of your questions are answered here: Grotfang is using Regular Expressions (RegEx).
 

mredge73

Member
1) it is defining a function that is of string type, in other words it is a function that returns a string
2) matchers are complicated and tricky. Simple explanation, the (//d+) is matches all integers following the text.
3) matchers are complicated and tricky. Dot notation is not so tricky, some call it "java style", way of calling a function. Reset is a matcher function.
4) that part shows how to manually construct the url to take a choice. Most choice adventures use the same format. This function will work even when the choice adventure hasn't been supported by mafia yet.
For example, choice 2 for adventure number 211 is not supported by mafia. So in order to advance to choice 212 you have to manually construct the url: "choice.php?pwd&whichchoice=211&option=2"

edit: damn, double ninja
 

Winterbay

Active member
m_choice.reset is the same as reset(m_choice) which I think is much clearer but that other people feel is less clear :)
m_choice.reset(string) would then be reset(m_choice, string) (see further here)

The function definition needs no semi-colon in the same way that if(expression) doesn't need a semi-colon. You do need the curly braces though {}.
 

Grotfang

Developer
The code Theraze brought up is much easier to understand... and while this works in the sewers for most people... It's probably be better for me in the long run to try and understand Grotfang's suggestion.

They actually do much the same thing. There are three main differences:

a) I use matchers while Theraze digs out what choice number you are on by hand (sorta)
b) I exit gracefully out of choices set to 0. This means the script ends if you set something to 0 (normally it would open in a browser window). Note, it does not abort. It exits/returns. Removing my "if( choice_num == "0" ) { exit; }" lines will give you the mini browser again.
c) I check to see if you are back in a combat at the end of the choice code, and kill anything I find. This is sewer-specific, and Theraze (sensibly for most other contexts) assumes you would make this check later in your script so his function stays more generic.
 
Top