Skip to content

Commit

Permalink
MPA Session Calculation (#1651)
Browse files Browse the repository at this point in the history
* MPA Session Calculation

* Attempt to readd marginal familiar extra value tracking

* Formatting & Fixes

---------

Co-authored-by: neil <[email protected]>
Co-authored-by: horrible little slime <[email protected]>
  • Loading branch information
3 people authored Mar 4, 2024
1 parent 7465a57 commit d06c19e
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 250 deletions.
145 changes: 81 additions & 64 deletions packages/garbo/src/familiar/barfFamiliar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import { getExperienceFamiliarLimit } from "./experienceFamiliars";
import { getAllJellyfishDrops, menu } from "./freeFightFamiliar";
import { GeneralFamiliar, timeToMeatify, turnsAvailable } from "./lib";
import { meatFamiliar } from "./meatFamiliar";
import { setMarginalFamiliarsExcessValue } from "../session";
import { garboValue } from "../garboValue";

const ITEM_DROP_VALUE = 0.72;
Expand Down Expand Up @@ -68,7 +67,7 @@ function getCachedOutfitValues(fam: Familiar) {
avoid: $items`Kramco Sausage-o-Matic™, cursed magnifying glass, protonic accelerator pack, "I Voted!" sticker, li'l pirate costume, bag of many confections`,
},
true,
).dress();
).outfit.dress();

const outfit = outfitSlots.map((slot) => equippedItem(slot));
const bonuses = bonusGear(BonusEquipMode.EMBEZZLER, false);
Expand Down Expand Up @@ -132,34 +131,35 @@ function totalFamiliarValue({
return expectedValue + outfitValue + familiarAbilityValue(familiar);
}

function turnsNeededForFamiliar(
{ familiar, limit, outfitValue }: MarginalFamiliar,
function turnsNeededFromBaseline(
baselineToCompareAgainst: MarginalFamiliar,
): number {
switch (limit) {
case "drops":
return sum(
getAllDrops(familiar).filter(
({ expectedValue }) =>
outfitValue + familiarAbilityValue(familiar) + expectedValue >
totalFamiliarValue(baselineToCompareAgainst),
),
({ expectedTurns }) => expectedTurns,
);

case "experience":
return getExperienceFamiliarLimit(familiar);

case "none":
return 0;

case "special":
return getSpecialFamiliarLimit({
familiar,
outfitValue,
baselineToCompareAgainst,
});
}
): (option: MarginalFamiliar) => number {
return ({ familiar, limit, outfitValue }: MarginalFamiliar) => {
switch (limit) {
case "drops":
return sum(
getAllDrops(familiar).filter(
({ expectedValue }) =>
outfitValue + familiarAbilityValue(familiar) + expectedValue >
totalFamiliarValue(baselineToCompareAgainst),
),
({ expectedTurns }) => expectedTurns,
);

case "experience":
return getExperienceFamiliarLimit(familiar);

case "none":
return 0;

case "special":
return getSpecialFamiliarLimit({
familiar,
outfitValue,
baselineToCompareAgainst,
});
}
};
}

function calculateOutfitValue(f: GeneralFamiliar): MarginalFamiliar {
Expand All @@ -172,19 +172,41 @@ function calculateOutfitValue(f: GeneralFamiliar): MarginalFamiliar {

return { ...f, outfitValue, outfitWeight };
}
export function barfFamiliar(): Familiar {
if (timeToMeatify()) return $familiar`Grey Goose`;
if (get("garbo_IgnoreMarginalFamiliars", false)) return meatFamiliar();

function extraValue(
target: MarginalFamiliar,
meat: MarginalFamiliar,
jellyfish: MarginalFamiliar | undefined,
) {
const targetValue = totalFamiliarValue(target);
const meatFamiliarValue = totalFamiliarValue(meat);

const jellyfishValue = jellyfish
? garboValue($item`stench jelly`) / 20 +
familiarAbilityValue(jellyfish.familiar) +
jellyfish.outfitValue
: 0;
return Math.max(targetValue - Math.max(meatFamiliarValue, jellyfishValue), 0);
}

export function barfFamiliar(): { familiar: Familiar; extraValue: number } {
if (timeToMeatify()) {
return { familiar: $familiar`Grey Goose`, extraValue: 0 };
}

if (get("garbo_IgnoreMarginalFamiliars", false)) {
return { familiar: meatFamiliar(), extraValue: 0 };
}

const meat = meatFamiliar();

const fullMenu = menu({
canChooseMacro: true,
location: $location`Barf Mountain`,
includeExperienceFamiliars: false,
}).map(calculateOutfitValue);

const meatFamiliarEntry = fullMenu.find(
({ familiar }) => familiar === meatFamiliar(),
);
const meatFamiliarEntry = fullMenu.find(({ familiar }) => familiar === meat);

if (!meatFamiliarEntry) {
throw new Error("Something went wrong when initializing familiars!");
Expand All @@ -196,50 +218,38 @@ export function barfFamiliar(): Familiar {
);

if (viableMenu.every(({ limit }) => limit !== "none")) {
const turnsNeeded = sum(viableMenu, (option: MarginalFamiliar) =>
turnsNeededForFamiliar(option, meatFamiliarEntry),
const turnsNeeded = sum(
viableMenu,
turnsNeededFromBaseline(meatFamiliarEntry),
);

if (turnsNeeded < turnsAvailable()) {
const shrubAvailable = viableMenu.some(
({ familiar }) => familiar === $familiar`Crimbo Shrub`,
);
return shrubAvailable ? $familiar`Crimbo Shrub` : meatFamiliar();
return {
familiar: shrubAvailable ? $familiar`Crimbo Shrub` : meat,
extraValue: 0,
};
}
}

if (viableMenu.length === 0) {
setMarginalFamiliarsExcessValue(0);
return meatFamiliar();
return { familiar: meat, extraValue: 0 };
}

const best = maxBy(viableMenu, totalFamiliarValue);

// Because we run marginal familiars at the end, our marginal MPA is inflated by best.expectedValue - meatFamiliarValue every turn
// Technically it's the nominalFamiliarValue, which for now is the max of meatFamiliar and jellyfish (if we have the jellyfish)
const jellyfish = fullMenu.find(
({ familiar }) => familiar === $familiar`Space Jellyfish`,
);
const jellyfishValue = jellyfish
? garboValue($item`stench jelly`) / 20 +
familiarAbilityValue(jellyfish.familiar) +
jellyfish.outfitValue
: 0;
const excessValue = Math.max(
best.expectedValue +
best.outfitValue +
familiarAbilityValue(best.familiar) -
Math.max(meatFamiliarValue, jellyfishValue),
0,
);
setMarginalFamiliarsExcessValue(excessValue);

const familiarPrintout = (x: MarginalFamiliar) =>
`(expected value of ${x.expectedValue.toFixed(
const familiarPrintout = ({
expectedValue,
familiar,
outfitValue,
}: MarginalFamiliar) =>
`(expected value of ${expectedValue.toFixed(
1,
)} from familiar drops, ${familiarAbilityValue(x.familiar).toFixed(
)} from familiar drops, ${familiarAbilityValue(familiar).toFixed(
1,
)} from familiar abilities and ${x.outfitValue.toFixed(1)} from outfit)`;
)} from familiar abilities and ${outfitValue.toFixed(1)} from outfit)`;

print(
`Choosing to use ${best.familiar} ${familiarPrintout(best)} over ${
Expand All @@ -248,7 +258,14 @@ export function barfFamiliar(): Familiar {
HIGHLIGHT,
);

return best.familiar;
const jellyfish = fullMenu.find(
({ familiar }) => familiar === $familiar`Space Jellyfish`,
);

return {
familiar: best.familiar,
extraValue: extraValue(best, meatFamiliarEntry, jellyfish),
};
}

function getSpecialFamiliarLimit({
Expand Down
10 changes: 7 additions & 3 deletions packages/garbo/src/outfit/barf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,19 @@ const POINTER_RING_SPECS: (
const trueInebrietyLimit = () =>
inebrietyLimit() - (myFamiliar() === $familiar`Stooper` ? 1 : 0);

export function barfOutfit(spec: OutfitSpec = {}, sim = false): Outfit {
export function barfOutfit(
spec: OutfitSpec = {},
sim = false,
): { outfit: Outfit; extraValue: number } {
cleaverCheck();
validateGarbageFoldable(spec);
const outfit = Outfit.from(
spec,
new Error(`Failed to construct outfit from spec ${toJson(spec)}!`),
);

outfit.familiar ??= barfFamiliar();
const { familiar, extraValue } = barfFamiliar();
outfit.familiar ??= familiar;

if (outfit.familiar === $familiar`Jill-of-All-Trades`) {
outfit.equip($item`LED candle`);
Expand Down Expand Up @@ -165,5 +169,5 @@ export function barfOutfit(spec: OutfitSpec = {}, sim = false): Outfit {
parka: "kachungasaur",
});

return outfit;
return { outfit, extraValue };
}
Loading

0 comments on commit d06c19e

Please sign in to comment.