finding/fixing a lost tps

I've been going around in circles with this for quite a while now, and am hoping for some suggestions from the other scripters out there.

a script can easily be written to answer the question "do I have a tiny plastic sword?" and even locate it for you.

Code:
record ItemPairs
  {
  Item Item1;
  Item Item2;
  };

item[int] TPSItems;
item[int] possessed_items;
item[int] drinks;
item[int] skewers;
item[stat] StatSkewers;
ItemPairs[stat] TPSStatDrinks;

void initTPSItems()
{
  StatSkewers[$stat[muscle]] = $item[skewered lime];
  StatSkewers[$stat[moxie]] = $item[skewered jumbo olive];
  StatSkewers[$stat[mysticality]] = $item[skewered cherry];

  TPSStatDrinks[$stat[muscle]].item1 = $item[grogtini];
  TPSStatDrinks[$stat[muscle]].item2 = $item[bodyslam];
  TPSStatDrinks[$stat[moxie]].item1 = $item[vesper];
  TPSStatDrinks[$stat[moxie]].item2 = $item[dirty martini];
  TPSStatDrinks[$stat[mysticality]].item1 = $item[cherry bomb];
  TPSStatDrinks[$stat[mysticality]].item2 = $item[sangria del diablo];

  skewers[0] = $item[skewered jumbo olive];
  skewers[1] = $item[skewered lime];
  skewers[2] = $item[skewered cherry];

  drinks[0] = $item[dirty martini];
  drinks[1] = $item[grogtini];
  drinks[2] = $item[cherry bomb];
  drinks[3] = $item[vesper];
  drinks[4] = $item[bodyslam];
  drinks[5] = $item[sangria del diablo];

  int count;
  TPSItems[count] = $item[tiny plastic sword];

  foreach key in skewers
    {
    count = count + 1;
    TPSItems[count] = skewers[key];
    }
  foreach key in drinks
    {
    count = count + 1;
    TPSItems[count] = drinks[key];
    }
  }

boolean drinkviatps(stat: PrefStat)
  {
  //drink based on the data obtained
  }

int TPSItemCount;
void possesseditemadd(item toadd)
  {
  possessed_items[TPSItemCount] := toadd;
  TPSItemCount := TPSItemCount + 1;
  }

boolean HaveTPS()
  {
  boolean Results;
  Results = false;
  //clear the array for subsequent calls
  Clear(possessed_items);
  foreach key in TPSItems 
    {
    if(closet_amount(TPSItems[key]) > 0) {take_closet(closet_amount(TPSItems[key]), TPSItems[key]);}
    if(item_amount(TPSItems[key]) > 0)
      {
      possesseditemadd(TPSItems[key]);
      Results = true;
      }
    }
  return Results;
  }

Boolean DrinkAway()
  {
  if(usetps)
    {
    //only call HaveTPS if usetps is true
    //if the above if statement were "if(usetps && haveTPS())" then
    //havetps() would be called when not needed
    //re-call haveTPS each time to re-load the map of items. 
    while(HaveTPS() && (my_inebriety() < inebriety_limit() - 7))
     {
     //needs some change, use a function to return the preferred stat instead of my_primestat
     drinkviatps(my_primestat());
     }
    }
  }

//this function call is intentionally sitting outside of a function.
//I use the space right before void main() as an initialization section
//to avoid the need for forward declaration of functions.
initTPSItems();

void main()
  {
  DrinkAway();
  }

Now notice boolean drinkviatps(stat: PrefStat) does not contain the code for actual drinking? Well, I may have a skewered lime, but be wanting to drink a sangria which requires a skewered cherry. The solution is to go ahead and drink a skewered lime drink instead. I want the script to focus on using any TPS item other than the actual TPS first, then if no other TPS item is found, create a drink using the actual TPS.

I call drinkviatps with the (my_primestat()) parameter here only for example. The actual call in the end will be with the parameter (my_preferredstat()).

Code:
boolean balance_stats  = true;

stat my_preferredstat()
  {
  if(balance_stats)
    {
    if(mus < mox)
      {
      if(mus < myst){return $stat[muscle];}
        else{retun $stat[mysticality];}
      }
      else
      {
      if(mox < myst){return $stat[moxie];}
        else{retun $stat[mysticality];}
      }
    }
    else 
    {return my_primestat();}
  }

The purpose is to want to drink a tps drink which boosts the stat passed to the function but actually drink a tps drink of any stat type when it's not possible. This means that in the event of a failure for any reason, the tps could be lost in just about any tps item form possible.

So how would you go about finishing the function drinkviatps?
 

Nightmist

Member
While I'm a bit fuzzy on the details I guess if I was to write a script that was to attempt to create a TPS drink to boost a certain stat and then if it is unable to do so, resort to non-requested stat boosting TPS drinks.

I would... run through a item_amount for the end drink of the preferred stat, then the skewered form of the preferred stat and then item_amount for plain TPS's, should all of the above checks return 0 as the item_amount then I would do the same loop but for the non-preferred. (Excluding the plain TPS check of course, since that shouldnt change.)
Of course the major flaw in this concept as I have described it is that it doesn't have a "createable_amount" check. Which heavily depends on the characters ability to gather the resources needed to complete a whole cocktailcrafting cycle and depending on the lengths one is willing and able to go for in order to gain access to those resources. (Fruits and booze)

Hmmm yeah... not sure if that helps or not but I'm interested. One of my scripts gutted out on a TPS drink creation and it took me AGES to figure out that I still had a skewered fruit in my inventory. >>.
 
When I get to that point, createable_amount actually was not in the plans. The reason is that when you let kolmafia handle item acquisition during creation, it will commonly buy a sangria to make a sangria del diablo. The cost of a sangria is on average 1000 more than it's components. This info comes from a year of logging what was spent to make drinks, and counts the bartender usage and item drops on explosion. I don't have the data anymore, but I do remember that part.

If I call createable_amount, and it returns 0, then I end up calling item_amount twice anyway. I find it more simple to just call item_amount.

I'm still plugging my old hard drive once or twice a day to my USB port on my PC hoping that it will manage to read the directory tree one last time, and allow me to copy the files. I'm having no luck there.
 

Metraxis

Member
How about something like this?

Code:
stat [item] TPSDrinks;
TPSDrinks[$item[grogtini]] = $stat[Muscle];
TPSDrinks[$item[bodyslam]] = $stat[Muscle];
TPSDrinks[$item[cherry bomb]] = $stat[Mysticality];
TPSDrinks[$item[sangria del diablo]] = $stat[Mysticality];
TPSDrinks[$item[vesper]] = $stat[Moxie];
TPSDrinks[$item[dirty martini]] = $stat[Moxie];

stat [item] TPSSkewers;
TPSSkewers[$item[skewered lime]] = $stat[Muscle];
TPSSkewers[$item[skewered cherry]] = $stat[Mysticality];
TPSSkewers[$item[skewered jumbo olive]] = $stat[Moxie];

boolean drinkviatps(stat preference) {
	foreach Drink in TPSDrinks {
		if(TPSDrinks[Drink] == preference && item_amount(Drink) > 0) {
			drink(1,Drink);
			return true;
		}
	}
	foreach Skewer in TPSSkewers {
		if(TPSSkewers[Skewer] == preference && item_amount(Skewer) > 0) {
			foreach Drink in TPSDrinks {
				if(TPSDrinks[Drink] == TPSSkewers[Skewer]) {
					create(1,Drink);
					drink(1,Drink);
					return true;
				}
			}
		}
	}
	if(item_amount($item[tiny plastic sword]) > 0) {
		foreach Drink in TPSDrinks {
			if(TPSDrinks[Drink] == preference) {
				create(1,Drink);
				drink(1,Drink);
				return true;
			}
		}
	}
	foreach Drink in TPSDrinks {
		if(item_amount(Drink) > 0) {
			drink(1,Drink);
			return true;
		}
	}
	foreach Skewer in TPSSkewers {
		if(item_amount(Skewer) > 0) {
			foreach Drink in TPSDrinks {
				if(TPSDrinks[Drink] == TPSSkewers[Skewer]) {
					create(1,Drink);
					drink(1,Drink);
					return true;
				}
			}
		}
	}
	return false;
}

boolean HaveTPS() {
	foreach Drink in TPSDrinks {
		if(item_amount(Drink) > 0 { return true; }
	}
	foreach Skewer in TPSSkewers {
		if(item_amount(Skewer) > 0 { return true; }
	}
	if(item_amount($item[tiny plastic sword]) > 0) { return true; }
	return false;
}

I don't use my own closet very much, except for moving things into 'permanent inventory' at the end of a run, but the apropos closeting functions could be added wherever item_amount() appears with little difficulty.
 

Nightmist

Member
Hmmm the lack of any ability to verify ones ability to purchase the required ingredients should they need to be bought from the mall is the major flaw in any scripted attempted creation of any item.
So really we can either resort to a "Just use the booze//fruit we KNOW we can get, such as inventory//closet and attempt creation depending on those results" or simply "attempt to create a drink and if it errors out skip and continue", which can result in spare basic booze and other items.
Since to avoid wastage one must factor in buying the booze AND buying the fruit, which means a boolean check of a buy function is not sufficient since you may be able to purchase the booze but then the remaining meat might not be enough for the purchase of the fruit which leaves you with a basic booze and no fruit or TPS drink and with the current functionality the calculation of the total cost cannot be done, and I don't expect it to be able to be done as it means mallbot functionality, which if it is possible, I believe Hola would patch it. (Of course this only really happens to very low meat characters, which probably isn't one of the environments your planning to be using this script in, I mean if you have a TPS chances are you will have enough meat to mall purchase the booze and fruit >>. I guess I just like to cover the possibility that you might be unable to afford them and so avoid purchasing unnecessary items.)
 
[quote author=Metraxis link=topic=670.msg3119#msg3119 date=1167233920]
How about something like this?
Code:
code snipped
I don't use my own closet very much, except for moving things into 'permanent inventory' at the end of a run, but the apropos closeting functions could be added wherever item_amount() appears with little difficulty.
[/quote]

What that script does comes very close to what I wanted to do. So close in fact that I think I will build on it.
I will toss in there that the following:
Code:
	if(item_amount($item[tiny plastic sword]) > 0) {
		foreach Drink in TPSDrinks {
			if(TPSDrinks[Drink] == preference) {
				create(1,Drink);
				drink(1,Drink);
				return true;
			}
		}
	}
is needed, but in the wrong place. It should be the last possibility. The reason is I would want the script to use anything other than the TPS first....that is to say that if I have a skewered cherry, and a tiny plastic sword, use the skewered cherry first even if the preferred stat is muscle. once the script is complete, it will convert any tps item into it's native tiny plastic sword, then once all items that are created using a tiny plastic sword are used then start creating drinks with tiny plastic sword(s), reducing the likelihood of someone saying "zomg I had 10 tiny plastic swords, and you stole 9 of them" when they have become off stat drinks/skewers, and the script didn't notice because it found a tps first.

Thanks, the final version will be posted later when I post a zip file containing a package of scripts which will work together, but be able to be called separately on special days when the player wants to deviate and adventure manually, but still script eating and drinking.
 
oops, wasn't paying attention and double posted. here's the previous post which I deleted.
The funny thing is that a script mess up led to a character having over 30,000 bottles of rum, and no meat. Well it wasn't a script mess up entirely, it was a new item problem. The new item had stopped kolmafia from reading inventory properly, and after buying the item was a inv refresh statement, then create. inv refresh failed, thus create failed, and drink failed of course and the while my_inebriety statement held it in a repeat state till the script bought up all the rum the character could afford. bye bye meat.

just a little update on where I am at:
Code:
stat[item] TPSDrinks;
stat[item] TPSSkewers;

record TPS_recipe
  {
  item base_booze;
  item base_fruit;
  item base_mixed;
  item skewered_fruit;
  item EQuiv_TPS_Drink;
  };
TPS_recipe[item] TPS_recipes;

void TPS_set_recipe(item toset, item equal, item skewer, item fruit, item mixed, item bottle)
{
TPS_recipes[toset].EQuiv_TPS_Drink = equal;
TPS_recipes[toset].skewered_fruit = skewer;
TPS_recipes[toset].base_fruit = fruit;
TPS_recipes[toset].base_mixed = mixed;
TPS_recipes[toset].base_booze = bottle;
}

void TPS_init_vars()
{
TPSDrinks[$item[grogtini]] = $stat[Muscle];
TPSDrinks[$item[bodyslam]] = $stat[Muscle];
TPSDrinks[$item[cherry bomb]] = $stat[Mysticality];
TPSDrinks[$item[sangria del diablo]] = $stat[Mysticality];
TPSDrinks[$item[vesper]] = $stat[Moxie];
TPSDrinks[$item[dirty martini]] = $stat[Moxie];
TPSSkewers[$item[skewered lime]] = $stat[Muscle];
TPSSkewers[$item[skewered cherry]] = $stat[Mysticality];
TPSSkewers[$item[skewered jumbo olive]] = $stat[Moxie];

TPS_set_recipe($item[grogtini], $item[bodyslam], $item[skewered lime], $item[lime], $item[grog], $item[bottle of rum]);
TPS_set_recipe($item[bodyslam], $item[grogtini], $item[skewered lime], $item[lime], $item[tequila with training wheels], $item[bottle of tequila]);
TPS_set_recipe($item[cherry bomb], $item[sangria del diablo], $item[skewered cherry], $item[cherry], $item[old-fashioned], $item[bottle of whiskey])
TPS_set_recipe($item[sangria del diablo], $item[cherry bomb], $item[skewered cherry], $item[cherry], $item[sangria], $item[boxed wine])
TPS_set_recipe($item[vesper], $item[dirty martini], $item[skewered jumbo olive], $item[jumbo olive], $item[dry vodka martini], $item[bottle of vodka])
TPS_set_recipe($item[dirty martini], $item[vesper], $item[skewered jumbo olive], $item[jumbo olive], $item[dry martini], $item[bottle of gin])
}

Item TPS_Closest_to_creating(item drink)
  {
  //returns either the drink passed as an arguement, or the stat matching drink
  //based on inventory haveing the base booze or mixer (not ability to buy items)
  //prefers the drink passed, and returns it if neither base_booze or mixer is available

  //if we have the base booze or base mixed booze for the drink, return the drink
  if(item_amount(TPS_recipes[drink].base_booze) > 0 || item_amount(TPS_recipes[drink].base_mixed) > 0)
    {return drink;}
  //if we have the base booze or base mixed booze for the matching drink, return the matching drink
  if(item_amount(TPS_recipes[TPS_recipes[drink].EQuiv_TPS_Drink].base_booze) > 0 
        || item_amount(TPS_recipes[TPS_recipes[drink].EQuiv_TPS_Drink].base_mixed) > 0)
    {return TPS_recipes[drink].EQuiv_TPS_Drink;}
  //all else fails return the drink
  return drink;
  }

boolean TPS_Create_and_drink(item drink)
  {
  create(1,drink);
  drink(1, drink);
  }
  

boolean TPSdrinkviaitem(stat preference)
  {
  foreach Skewer in TPSSkewers
    {
    if(TPSSkewers[Skewer] == preference && item_amount(Skewer) > 0)
      {
      foreach Drink in TPSDrinks
        {
        if(TPSDrinks[Drink] == TPSSkewers[Skewer])
          {
          return TPS_Create_and_drink(TPS_Closest_to_creating(drink));
          }
        }
      }
    }

  foreach Drink in TPSDrinks
    {
    if(TPSDrinks[Drink] == preference && item_amount(Drink) > 0)
      {
      drink(1,Drink);
      return true;
      }
    }

  foreach Skewer in TPSSkewers
    {
    if(item_amount(Skewer) > 0)
      {
      foreach Drink in TPSDrinks
        {
        if(TPSDrinks[Drink] == TPSSkewers[Skewer])
          {
          return TPS_Create_and_drink(TPS_Closest_to_creating(drink));
          }
        }
      }
    }

  foreach Drink in TPSDrinks
    {
    if(item_amount(Drink) > 0)
      {
      drink(1,Drink);
      return true;
      }
    }

  if(item_amount($item[tiny plastic sword]) > 0)
    {
    foreach Drink in TPSDrinks
      {
      if(TPSDrinks[Drink] == preference)
        {
        return TPS_Create_and_drink(TPS_Closest_to_creating(drink));
        }
      }
    }

  return false;
  }

boolean TPSHaveItem()
  {
  foreach Drink in TPSDrinks{if(item_amount(Drink) > 0{ return true; }}
  foreach Skewer in TPSSkewers{if(item_amount(Skewer) > 0{ return true; }}
  if(item_amount($item[tiny plastic sword]) > 0){ return true; }
  return false;
  }

Boolean TPSDrinkAway()
  {
  if(usetps && TPSHaveItem()){while(my_inebriety() < inebriety_limit() - 7){TPSdrinkviaitem(my_preferredstat());}}
  }

//implicitly call TPS_init_vars() when imported
TPS_init_vars();

void main()
{
//when imported into another script call TPSDrinkAway

TPSDrinkAway();
}

every function starts with TPS which when imported along with several other scripts into a main script will allow me to easily track down bugs and such. This also means that I shouldn't accidentally over-ride another function by using the same name.

Where I have "create(1,drink);" is the location of the next modification. It is also the reason for "TPS_recipe[item] TPS_recipes;". I plan to be more detailed about the item creation process. That is I want to over-ride some internal user-defined settings in kolmafia to avoid having to set them. Those settings are: "create and install new box servant after explosion" and "buy items from the mall whenever needed". I do not want to change these settings, just over-ride them for this purpose. This is simple to do, but I've got other things to do right now. I'll have to check my old icypeak.ash script, and may snag some code from it for this.

edit: eeps!!! variable re-naming!!! "drink" is a built in function in kolmafia it's also a common variable name in this script in progress. I foresee possible problems arising (hopefully they wont) so now I have to go through the entire script and change that variable name to something else.
 
[quote author=Metraxis link=topic=670.msg3136#msg3136 date=1167421963]
ASH considers foo and foo() to be seperate in my experience, so you shouldn't need to rename the variable.
[/quote]
That's the way it is right now. later, I may be modifying kolmafia for some reason and mess that up, or it may get messed up in the release but I doubt it. I'm just overly careful. (yes I modify kolmafia for my own use from time to time as the need presents itself)

Anyway on with the interesting stuff. The part of my script set which handles drinking tps drinks is almost complete. "Almost" because the little bit of inebriety that is left over for the maximum safe drinking is not yet handled. That's a whole different ball park though.

files:
misc.ash holds/will hold functions which may be used by multiple scripts in the group
Options.ash holds/will hold changeable options to make the scripts work differently. When the complete adventuring script is written, the return values of the various options will be different for that script only as a normal day.
Pre-AdventureDrinking.ash holds/will hold a wrapper for drinking in various ways (for trophies, tps, super human, adv cocktail to name a few) Calling Pre-AdventureDrinking.ash direct from kolmafia will (once finished) cause it to act as a special day and drink for gaining the most adventures. When used in the unwritten adventuring script it will act as a normal day, and use more of the settings in options.ash.
drinkingviatps.ash the script Metraxis wrote the basis for. It handles that pesky TPS getting lost. will also be able to be called directly to only handle drinking tps drinks.

In looking at these scripts you might notice it looks like I am trying to fail the functions out, and return false. What I am really trying to do is make it so that if drinking fails
A: I will not get stuck in a near infinite loop buying stuff
B: I will be able to go adventure for a while then try drinking and adventuring again.

These scripts are yet untested, and probably contain various typos, and errors.
Next task superhuman drinking which should be easier.
 

Attachments

  • drinkingviatps.ash
    7.1 KB · Views: 33
  • misc.ash
    7.5 KB · Views: 32
  • Options.ash
    1.7 KB · Views: 32
  • Pre-AdventureDrinking.ash
    658 bytes · Views: 33
Top