My personal Meat farming script

Veracity

Developer
Staff member
I posted this a while ago, but I have developed it considerably since then. In particular, it works for my main (who has all the skills and items) as well as my various multis, who farm Meat for no other reason than to have as much as they might need to test KoLmafia.

I have a bunch of hard-coded food, booze, and spleen choices, and I suppose I could use Somebody Else's Code to make better choices, but I just don't care. These are Good Enough.

I post this because it has a few things that might make other peoples' lives simpler. For example:

- I combine filling my spleen (with twinkly wads, which are not optimal, but I have, literally, 10s of thousands of them on every character) with numberology.
- Here is working code to take the "greasing the tracks" quest (when available) in Dinseylandfill at Barf Mountain - where I would go anyway - and fulfilling and redeeming it.

And so on. This does, literally, everything that I care about to use my turns, use my Daily Deeds, and so on, with no thought, for my main and all my multis.

As I said, this will suit nobody's needs other than my own, but parts of it may very well help others, so, enjoy!

Edit: Uploaded newest version. It is much more of a "real script" now:

- main_function call tasks in individual functions, making it easier to adapt/eclude/edit
- configuration variables at top.

and a whole bunch more functionality, documented at top.

Edit 2: Uploaded newest version.

- Heavily commented to, one hopes, make it easy to figure out how to configure.
- All sorts of little bugs fixed.
- Additional functionality.

Edit 3: Uploaded newest version

- Bug fixes
- Flush out wanderers before going into Deep Machine Tunnels
- Initial support for Sweet Synthesis
- Cumulative records: total meat and total turns spent running this script

Edit 4: Now on SVN!

(No longer supported that way)

Edit 5: Now on GIT!

Code:
git checkout Veracity0/meat-farm

It will put VeracityMeatFarm.ash into your scripts folder.

- Bug fixes
- Support for portable Mayo clinic
- If you really want to use your Hobo Monkey when you have a Tot, you can configure that
 

Attachments

(I moved this over to "Turn-Burning Scripts")

I have continued to refine this and now it will bust ghosts, as appropriate. This was my introduction to combat filters. Here is the code:

Code:
string bust_ghost_filter( int round, monster mon, string page )
{
    boolean [string] subtypes = mon.sub_types;

    if ( subtypes contains "ghost" ) {
	switch ( round ) {
	case 0:
	    return "skill Summon Love Gnats";
	case 1:
	case 2:
	case 3:
	    return "skill shoot ghost";
	case 4:
	    return "skill trap ghost";
	}
    }

    return "";
}

item protonic_accelerator_pack = $item[ protonic accelerator pack ];
item talisman_o_namsilat = $item[ Talisman o' Namsilat ];

void bust_ghost()
{
    location paranormal = get_property( "ghostLocation" ).to_location();
    if ( paranormal != $location[ none ] ) {
	slot back = $slot[ back ];
	item back_item = equipped_item( back );
	if ( back_item != protonic_accelerator_pack ) {
	    equip( back, protonic_accelerator_pack );
	}

	location palindome = $location[ Inside the Palindome ];
	slot acc3 =  $slot[ acc3 ];
	item acc3_item = equipped_item( acc3 );
	if ( paranormal == palindome && acc3_item != talisman_o_namsilat ) {
	    equip( acc3, talisman_o_namsilat );
	}

	try {
	    adv1( paranormal, 0, "bust_ghost_filter" );
	}
	finally {
	    equip( back, back_item );
	    equip( acc3, acc3_item );
	}
    }
}

void do_adventure( int turns, location loc )
{
    if ( available_amount( protonic_accelerator_pack ) != 0 ) {
	while ( turns > 0 ) {
	    bust_ghost();
	    adventure( 1, loc );
	    turns -= 1;
	}
    } else {
	adventure( turns, loc );
    }
}
The Wiki says that when you bust a ghost, 50 turns later, you are given another opportunity - and my session logs confirm that. I don't know if we have a setting to track that -> lastBustedGhostTurn, or something. If so, it would be easy enough to bulk-adventure the exact number of turns needed to get the next notification, rather than doing 1 adventure at a time. No increase in server hits doing one at a time; it would simply cut down the processing time to validate the location. So, no big deal. I might look into it, by and by.
 

Attachments

I have continued to refine this and now it will bust ghosts, as appropriate. This was my introduction to combat filters. Here is the code:

That makes sense considering your recent interests.

Incidentally, my solution to the problem was to add code in my afterAdventureScript to detect if I have a ghost to bust. That way it fires regardless of what I'm doing to farm. Then for fighting the ghost instead of a function for the combat filter I use a macro:

Code:
adv1(ghostLocation, -1, "while hasskill Shoot Ghost; skill Shoot Ghost; if hasskill Trap Ghost; skill Trap Ghost; endif; endwhile;");

The ability to add any arbitrary BALLS macro in place of a combat filter is kinda amazing.


The Wiki says that when you bust a ghost, 50 turns later, you are given another opportunity - and my session logs confirm that. I don't know if we have a setting to track that -> lastBustedGhostTurn, or something. If so, it would be easy enough to bulk-adventure the exact number of turns needed to get the next notification, rather than doing 1 adventure at a time. No increase in server hits doing one at a time; it would simply cut down the processing time to validate the location. So, no big deal. I might look into it, by and by.

Mafia tracks nextParanormalActivity which is the turn on which it will happen so...
Code:
int turnsToGo = to_int(get_property("nextParanormalActivity")) - total_turns_played();
 
So... If your last adventure of the day reports paranormal activity, your afterAdventureScript will attempt to bust a ghost - which takes no turn, but you have to have a turn available?

I don't think that's quite right.

Also, I am attempting to have no scripts - except for my CCS and this script - needed to configure in order to Farm my Daily Meat.

I'm now working on Witchess and a combat filter which will handle the Hobo Monkey better.
 
Here is the Hobo Monkey filter:

Code:
string hobo_monkey_filter( int round, monster mon, string page )
{ 
    static boolean gave_meat = false;

    if ( round == 0 ) {
	gave_meat = false;
	switch ( my_class() ) {
	case $class[ Accordion Thief ]:
	case $class[ Disco Bandit ]:
	    return "pickpocket";
	}
    }

    if ( my_familiar() == $familiar[ hobo monkey ] ) {
	if ( !gave_meat ) {
	    gave_meat = page.contains_text( "where did he find that?" );
	}

	return gave_meat ? "attack" : "item seal tooth";
    }

    return "";
}

void do_adventure( int turns, location loc )
{
    if ( available_amount( protonic_accelerator_pack ) != 0 ) {
	while ( turns > 0 ) {
	    bust_ghost();
	    adventure( 1, loc, "hobo_monkey_filter" );
	    turns -= 1;
	}
    } 
    adventure( turns, loc, "hobo_monkey_filter" );
}
Takes from 1 to (however many, at a 1/3 chance per round) to get the "familiar steals Meat" message and then just attacks.
 
Last edited:
Code:
adv1(ghostLocation, -1, "while hasskill Shoot Ghost; skill Shoot Ghost; if hasskill Trap Ghost; skill Trap Ghost; endif; endwhile;");
If it takes 0 turns for busting ghosts, shouldn't that -1 (calculate turns normally) actually be set to 0 (adventure doesn't take a turn)?
 
I suppose it could. Though I'm still pretty sure that you actually need a turn to bust a ghost even though the turn isn't spent. So shouldn't I let mafia calculate it normally?
 
Isn't it one of the newishfangled "free if you win, but costs a turn if you lose" things, which is what requires that one remaining turn?
 
You need a turn to be allowed to enter all the ghost zones, to trigger the combat, I'm fairly sure that was the same before the change. Pretty much the same as the game requiring you to have a turn to enter any location to trigger a digitized witchess piece, I think.
 
You need a turn to be allowed to enter all the ghost zones, to trigger the combat, I'm fairly sure that was the same before the change. Pretty much the same as the game requiring you to have a turn to enter any location to trigger a digitized witchess piece, I think.

Isn't it one of the newishfangled "free if you win, but costs a turn if you lose" things, which is what requires that one remaining turn?

As far as I'm aware, these changes were intended to eliminate certain bugs related to free fights (mostly infinite meat/certain items, iirc). Ronin-breaking not so much because that was "fixed" in a way that led to turn-saving exploits requiring 0 adv remaining (e.g. crafting in underworld, fighting NS with 1 adv remaining).
 
Actually, now that I think of it, ghost zones are just adventure.php - so you always need that 1 turn simply because you never know what you will encounter. You might be expecting a free fight, but can meet a (newer?) turn-taking thing, teleportation, whatever.
 
I worked on this all weekend and just uploaded a new version to the first post.

- There is now a main() function which calls separate functions to do each "task". Bale might even approve. ;)
- There is a "configuration" section at the top.
- There are now combat filters for busting ghosts, adventuring in the DMT, stasis with a Hobo Monkey, and a default
- Summon mayflies at Barf Mountain to get more FunFunds.
- Use free fights at witchess, snojo, and dmt
- And I have a To Do list:

// Get Witchess buff
// bust_ghost_filter should not depend on Summon Love Gnats
// Barrel Prayer for food if Turtle Tamer
// Better use of Dinsey foodcone and jumping horseradish
// When cheating deck, don't ask for unavailable cards
// Secondary buffbot for when primary is offline
// Better configuration of clip art, shower, Witchess
// Use adventure() to go to next paranormal activity
// Time-Spinner pranks?

This is addicting.

----

Issues with Combat Filters:

- If a combat filter returns "item X", it tries to Funksling two of them. There is an open bug report discussing this and I, too, am not happy with the status quo.

- If a combat filter returns "" with the intent that KoLmafia will continue in the CCS, that does, in fact, happen. However, it starts the CCS from the beginning. OK. It is not the case that a CCS has lines that correspond to "round 1" "round 2" and so on, such that you could skip to the current round, so, it really has to do that.

But this does mean that your CCS needs to be able to start at the beginning even in the middle of a fight. KoLmafia will turn your CCS into a macro and submit it, and it will not macrofy skills that are not currently available, I believe, but for some reason it will macrofy "steal" even if you already stole this combat. It is, sometimes, possible to steal twice (DB with the Bling of the New Wave, for example), but our way of detecting whether you can still steal is to look at the text of the fight page and see if it offers you the chance.

Perhaps the Macrofier should do that too.
 
I do approve actually. I like doing that, even if it is just so that I can easily comment out functions that I don't currently want and still find them later. It really is all around nicer.

One question: You do know that you can static {lots of declarations} instead of having to put static in front of every declaration, right? I mean, I know you created it, but I wonder if you remember that fact. Do you just like the way it looks to have each static variable individually declared that way?

PS. Since you have timepranks on your list, I do it something like this...
Code:
	if(get_property("kingLiberated").to_boolean() && available_amount($item[Time-Spinner]) > 0) {
		int pranks() { return 10 - get_property("_timeSpinnerMinutesUsed").to_int() - (get_property("_timeSpinnerReplicatorUsed").to_boolean()? 0: 2);}
		if(pranks() > 0) {
			print("Pranks remaining: "+pranks());
			foreach friend in $strings[Veracity, hardcoded list deleted for privacy]
				if(pranks() > 0) {
					string msg;
					switch(random(5)) {
					case 0: msg = "Why are there so many "+friend+"s in my timestream?"; break;
					case 1: msg = "Tag! You're it!"; break;
					case 2: msg = "Ha, ha! I have stolen your nose."; break;
					case 3: msg = "Nothing personal, but I must kill you before you go back in time to kill Hitler or else all causalty in this Universe may be destroyed!"; break;
					case 4: msg = "Our tentacled masters demand I implant this tentacle in your head. Just hold still now..."; break;
					}
					print("Pranking "+friend+": "+msg);
					cli_execute("timespinner prank " + friend + " msg=" + msg);
					string temp = visit_url("choice.php?pwd&whichchoice=1198&option=2"); // Clear the choice in case the timestream was clogged.
				}
		}
	}

I considered adding to my list with who_clan(), but a hardcoded list suits me better.
 
But this does mean that your CCS needs to be able to start at the beginning even in the middle of a fight. KoLmafia will turn your CCS into a macro and submit it, and it will not macrofy skills that are not currently available, I believe, but for some reason it will macrofy "steal" even if you already stole this combat. It is, sometimes, possible to steal twice (DB with the Bling of the New Wave, for example), but our way of detecting whether you can still steal is to look at the text of the fight page and see if it offers you the chance.

Perhaps the Macrofier should do that too.

CCS needs to be able to start properly in the middle of a fight anyway. I can start a fight in relay browser, do a few rounds, then press the AUTO (or is it SCRIPT?) button.
I can also have an /autoattack that doesn't finish the fight, after which mafia's ccs can take over.

I am also pretty sure that specifically macrofied pickpocket has been fixed way ago (kol side) to not trigger monster attack when useless. It will say you can't pickpocket anything, but not waste an action. At least that's how it seems to work in the native macros that I use all the time. I don't even think it advances the round counter, but I am not nearly 100% confident on that.
 
Yes, it works KoL side. And it does not advance the round counter. The presence of "It is round 4 but KoL thinks it is round 3" messages in the gCLI is what alerted me to this.
 
One question: You do know that you can static {lots of declarations} instead of having to put static in front of every declaration, right? I mean, I know you created it, but I wonder if you remember that fact. Do you just like the way it looks to have each static variable individually declared that way?
I do know that. It's a matter of indentation: when you "static {}", it looks like the {} is a block - although it isn't, really - and my editor wants to indent the enclosed declarations. To my eye, that is unclear: you CAN declare variables in a block, and their scope is that block - but for static{}, the scope is actually not restricted. It's just a way of grouping declarations, just as we can do the same thing with cli_execute {}.

I did implement it, but I don't like using it. Maybe I just need to fix my editor. :)

Another "style" thing: I think I will capitalize the names of constants, just as I do in Java, to make it clear they are not regular variables.

PS. Since you have timepranks on your list, I do it something like this...

... code...

I considered adding to my list with who_clan(), but a hardcoded list suits me better.
Thanks! I'll look at it and use some of it, I am sure.

I want to do pranks because this is an aftercore script and I have lots of extra Time-Spinner minutes.

Also on my list is looking again at the "timespinner" command and making sure that if a prank fails, it tells you, and leaves you out of the choice.

In any case, one of the goals of this weekend's work was to let you (me) run it having done (whatever) first - start it, abort it, restart it, for example. Several of the items on the "To Do" list are more of the same - checking deck cards, for example.
 
Thanks for indulging my curiosity on that cosmetic issue.


Also on my list is looking again at the "timespinner" command and making sure that if a prank fails, it tells you, and leaves you out of the choice.

Yeah. I've got a line in that code specifically to exit the choice after a failed prank. Which now that I think about it, I should have placed outside of that loop.


Several of the items on the "To Do" list are more of the same - checking deck cards, for example.

My aftercore cheating code:

Code:
	if(available_amount($item[Deck of Every Card]) > 0) {
		string [int] cards;
		if(hippy_stone_broken())
			cards[ count(cards) ] = "X of Clubs";
		foreach card in $strings[Island, Ancestral Recall, 1952 Mickey Mantle]
			cards[ count(cards) ] = card;
		foreach x,card in cards
			if(get_property("_deckCardsDrawn").to_int() < 11 && !get_property("_deckCardsSeen").contains_text(card))
				cli_execute("cheat " + card);
	}
 
I can also have an /autoattack that doesn't finish the fight, after which mafia's ccs can take over.
Yes, and there is funky code in FightRequest.updateCombatData:

Code:
		// We've started a new round
		++FightRequest.currentRound;

		// Sanity check: compare our round with what KoL claims it is
		Matcher m = ONTURN_PATTERN.matcher( responseText );
		if ( m.find() )
		{
			int round = StringUtilities.parseInt( m.group(1) );
			if ( round != FightRequest.currentRound )
			{
				RequestLogger.printLine( "KoLmafia thinks it is round " + FightRequest.currentRound + " but KoL thinks it is round " + round );
			}
		}

		// *** This doesn't seem right, but is currently necessary for
		// *** CCS scripts to behave correctly. FIX
		if ( autoAttacked )
		{
			++FightRequest.preparatoryRounds;
			++FightRequest.currentRound;
		}
Considering that I want to add a line following the "KoLmafia thinks it is round x" message:

Code:
			// Synchronize with KoL
			FightRequest.currentRound = round;
in order to show the line ONCE - which is useful for spading, actually, in that it tells you that you just did something which did not actually advance the round number. But seeing it for all the rest of the rounds in the fight is not useful.

I will investigate exactly what autoattack does to the round number and see if I can "FIX" this.
 
Revision 17473 fixes the "off by one" round reporting.

Well, if you "steal" in the Relay Browser and then say "script" it will start your CCS - and if the first action of that is "steal", it will submit it again. In which case, we'll see the "off by one" message, but will reset our round counter to agree with KoL, and everything thereafter will be fine.

I tried with an autoattack of "steal" and - with another adjustment, it works fine.

I did notice:

consult scripts take a "round #" - which starts at 1 and goes up.
combat filters want a "round index" - which starts at 0 and goes up.

So, that is not actually the same as a consult script, regardless of documentation.

I can change combat filters to agree with consult scripts - which will break them all - or leave it as is.

Thoughts?
 
My first thought is that it is too important a question to be kept in an thread without a relevant title to draw everyone's eye. This is a thread about how Veracity does some daily automation. Not everyone who is invested in combat scripting will read it.

My second thought is that you shouldn't break consult scripts. Breaking combat filters to make them work as reported is probably for the best since consistency is also important and pretty much all you'll break is bumcheekascend.
 
Last edited:
Back
Top