Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Environment tracking for CMC #914

Merged
merged 11 commits into from Jul 22, 2022
1 change: 1 addition & 0 deletions src/data/defaults.txt
Expand Up @@ -780,6 +780,7 @@ user lastChanceThreshold 100
user lastChessboard
user lastChasmReset -1
user lastColosseumRoundWon 0
user lastCombatEnvironments xxxxxxxxxxxxxxxxxxxx
user lastCopyableMonster
user lastCouncilVisit 0
user lastCounterDay -1
Expand Down
1 change: 1 addition & 0 deletions src/net/sourceforge/kolmafia/KoLmafiaCLI.java
Expand Up @@ -576,6 +576,7 @@ public static void registerCommands() {
new CliRefCommand().register("help").register("which");
new ClosetCommand().register("closet");
new CoinmasterCommand().register("coinmaster");
new ColdMedicineCabinetCommand().register("cmc");
new ColorEchoCommand().register("colorecho").register("cecho");
new ComparisonShopCommand().register("cheapest").register("expensive");
new CompleteQuestCommand()
Expand Down
1 change: 1 addition & 0 deletions src/net/sourceforge/kolmafia/objectpool/ItemPool.java
Expand Up @@ -3356,6 +3356,7 @@ public class ItemPool {
public static final int HOMEBODYL = 10828;
public static final int EXTROVERMECTIN = 10829;
public static final int BREATHITIN = 10830;
public static final int FLESHAZOLE = 10831;
public static final int GOOIFIED_ANIMAL_MATTER = 10844;
public static final int GOOIFIED_VEGETABLE_MATTER = 10845;
public static final int GOOIFIED_MINERAL_MATTER = 10846;
Expand Down
Expand Up @@ -279,6 +279,7 @@ public class Preferences {
"lastAnticheeseDay",
"lastBeardBuff",
"lastColosseumRoundWon",
"lastCombatEnvironments",
"lastCopyableMonster",
"lastCouncilVisit",
"lastZapperWandExplosionDay",
Expand Down
44 changes: 31 additions & 13 deletions src/net/sourceforge/kolmafia/request/FightRequest.java
Expand Up @@ -3387,12 +3387,17 @@ else if (responseText.contains("Your unicorn horn shrivels")) {
Preferences.setString("lastCopyableMonster", monsterName);
}

final boolean free = responseText.contains("FREEFREEFREE");

if (free) {
String updateMessage = "This combat did not cost a turn";
RequestLogger.updateSessionLog(updateMessage);
KoLmafia.updateDisplay(updateMessage);
} else {
trackEnvironment(location);
}

if (!won) {
if (responseText.contains("FREEFREEFREE")) {
String updateMessage = "This combat did not cost a turn";
RequestLogger.updateSessionLog(updateMessage);
KoLmafia.updateDisplay(updateMessage);
}
QuestManager.updateQuestFightLost(responseText, monsterName);
} else {
if (responseText.contains("monstermanuel.gif")) {
Expand Down Expand Up @@ -4132,14 +4137,8 @@ else if (responseText.contains("a horrible grinning clown head emerges")) {
Preferences.decrement("breathitinCharges", 1, 0);
}

if (responseText.contains("FREEFREEFREE")) {
String updateMessage = "This combat did not cost a turn";
RequestLogger.updateSessionLog(updateMessage);
KoLmafia.updateDisplay(updateMessage);
} else {
if (responseText.contains("playing on your SongBoom")) {
Preferences.increment("_boomBoxFights");
}
if (!free && responseText.contains("playing on your SongBoom")) {
Preferences.increment("_boomBoxFights");
}

if (IslandManager.isBattlefieldMonster(monsterName)) {
Expand Down Expand Up @@ -4213,6 +4212,25 @@ else if (responseText.contains("a horrible grinning clown head emerges")) {
}
}

private static void trackEnvironment(final KoLAdventure location) {
var environment = location != null ? location.getEnvironment() : "none";

var symbol =
switch (environment) {
case "outdoor" -> "o";
case "indoor" -> "i";
case "underground" -> "u";
case "underwater" -> "x";
default -> "?";
gausie marked this conversation as resolved.
Show resolved Hide resolved
};

// Make sure the value is padded to handle malformed preferences
var environments = "x".repeat(20) + Preferences.getString("lastCombatEnvironments") + symbol;

Preferences.setString(
"lastCombatEnvironments", environments.substring(environments.length() - 20));
}

// <p>You see a strange cartouche painted on a nearby wall.<div style='position: relative;
// display: inline-block; z-index 0;'><img src=/images/otherimages/cartouche.gif><div
// style='position: absolute; left: 15; top: 30; z-index 1;'><img
Expand Down
1 change: 1 addition & 0 deletions src/net/sourceforge/kolmafia/textui/DataTypes.java
Expand Up @@ -762,6 +762,7 @@ public static final Value makeItemValue(String name) {
}

public static final Value makeItemValue(final AdventureResult ar) {
if (ar == null) return DataTypes.ITEM_INIT;
int num = ar.getItemId();
String name = ItemDatabase.getItemDataName(num);
return DataTypes.makeNormalizedItem(num, name);
Expand Down
11 changes: 11 additions & 0 deletions src/net/sourceforge/kolmafia/textui/RuntimeLibrary.java
Expand Up @@ -148,6 +148,7 @@
import net.sourceforge.kolmafia.session.VotingBoothManager;
import net.sourceforge.kolmafia.swingui.widget.InterruptableDialog;
import net.sourceforge.kolmafia.textui.AshRuntime.CallFrame;
import net.sourceforge.kolmafia.textui.command.ColdMedicineCabinetCommand;
import net.sourceforge.kolmafia.textui.command.ConditionalStatement;
import net.sourceforge.kolmafia.textui.command.EudoraCommand;
import net.sourceforge.kolmafia.textui.command.SetPreferencesCommand;
Expand Down Expand Up @@ -2672,6 +2673,11 @@ public static FunctionList getFunctions() {

params = new Type[] {DataTypes.INT_TYPE};
functions.add(new LibraryFunction("pick_pocket", DataTypes.BOOLEAN_TYPE, params));

// Cold Medicine Cabinet support
params = new Type[] {};
functions.add(
new LibraryFunction("expected_cold_medicine_cabinet_pill", DataTypes.ITEM_TYPE, params));
}

public static Method findMethod(final String name, final Class<?>[] args)
Expand Down Expand Up @@ -9627,4 +9633,9 @@ private static Value makeIndexedText(int pocket) {
}
return value;
}

public static Value expected_cold_medicine_cabinet_pill(ScriptRuntime controller) {
var nextPill = ColdMedicineCabinetCommand.nextPill();
return DataTypes.makeItemValue(nextPill);
}
}
@@ -0,0 +1,72 @@
package net.sourceforge.kolmafia.textui.command;

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.sourceforge.kolmafia.AdventureResult;
import net.sourceforge.kolmafia.RequestLogger;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.preferences.Preferences;

public class ColdMedicineCabinetCommand extends AbstractCommand {
public ColdMedicineCabinetCommand() {
this.usage = " - show information about the cold medicine cabinet";
}

private static final Map<Character, AdventureResult> PILLS =
Map.ofEntries(
Map.entry('i', ItemPool.get(ItemPool.EXTROVERMECTIN)),
Map.entry('o', ItemPool.get(ItemPool.HOMEBODYL)),
Map.entry('u', ItemPool.get(ItemPool.BREATHITIN)),
Map.entry('x', ItemPool.get(ItemPool.FLESHAZOLE)));

/**
* Count all the last combat environments
*
* @return The lastCombatEnvironments pref transformed into a map of environment characters to
* counts
*/
private static Map<Character, Integer> getCounts() {
return Preferences.getString("lastCombatEnvironments")
.chars()
.mapToObj(i -> (char) i)
.collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(i -> 1)));
}

public static AdventureResult nextPill() {
return nextPill(getCounts());
}

public static AdventureResult nextPill(Map<Character, Integer> counts) {
int unknown = counts.getOrDefault('?', 0);

if (unknown > 10) return null;

for (var e : counts.entrySet()) {
var environment = e.getKey();
if (environment == '?') continue;
var count = e.getValue();
// If we have an overall majority return it.
if (count > 10) return PILLS.get(environment);
// If there is a potential majority when considering unknowns, return none.
if ((count + unknown) > 10) return null;
}

// Otherwise return the pill you get from having a majority not inside, outside or underground.
return PILLS.get('x');
}

@Override
public void run(final String cmd, String parameter) {
var output = new StringBuilder();

var counts = getCounts();
var pill = nextPill(counts);

var pillName = (pill != null) ? pill.toString() : "unknown";

output.append("Your next pill is ").append(pillName);

RequestLogger.printLine(output.toString());
}
}
45 changes: 45 additions & 0 deletions test/net/sourceforge/kolmafia/request/FightRequestTest.java
Expand Up @@ -931,4 +931,49 @@ public void canTrackLoveBugDrops(
}
}
}

@Nested
class CombatEnvironment {
@ParameterizedTest
@CsvSource({
"Oil Peak, o",
"The Haunted Pantry, i",
"The Middle Chamber, u",
"The Briny Deeps, x",
// If they add Gausie's Grotto I promise to come and make up a new location
"Gausie's Grotto, ?"
})
public void canDetectEnvironment(String adventureName, String environmentSymbol) {
var cleanups = setProperty("lastCombatEnvironments", "xxxxxxxxxxxxxxxxxxxx");
try (cleanups) {
KoLAdventure.lastVisitedLocation = AdventureDatabase.getAdventure(adventureName);
// Any old non-free fight from our fixtures
parseCombatData("request/test_fight_oil_slick.html");
assertThat("lastCombatEnvironments", isSetTo("xxxxxxxxxxxxxxxxxxx" + environmentSymbol));
}
}

@ParameterizedTest
@ValueSource(strings = {"", "xxxxx", "xxxxxxxxxxxxxxxxxxx"})
public void canRecoverUndersizedProp(String pref) {
var cleanups = setProperty("lastCombatEnvironments", pref);
try (cleanups) {
KoLAdventure.lastVisitedLocation = AdventureDatabase.getAdventure("The Oasis");
// Any old non-free fight from our fixtures
parseCombatData("request/test_fight_oil_slick.html");
assertThat("lastCombatEnvironments", isSetTo("xxxxxxxxxxxxxxxxxxxo"));
}
}

@Test
public void doesNotCountFreeFights() {
var cleanups = setProperty("lastCombatEnvironments", "ioioioioioioioioioio");
try (cleanups) {
KoLAdventure.lastVisitedLocation = AdventureDatabase.getAdventure("Hobopolis Town Square");
// Any old free fight from our fixtures
parseCombatData("request/test_fight_potted_plant.html");
assertThat("lastCombatEnvironments", isSetTo("ioioioioioioioioioio"));
}
}
}
}
26 changes: 26 additions & 0 deletions test/net/sourceforge/kolmafia/textui/RuntimeLibraryTest.java
Expand Up @@ -2,16 +2,19 @@

import static internal.helpers.Networking.html;
import static internal.helpers.Player.addItem;
import static internal.helpers.Player.setProperty;
import static internal.helpers.Player.setupFakeResponse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;

import internal.helpers.Cleanups;
import net.sourceforge.kolmafia.KoLCharacter;
import net.sourceforge.kolmafia.preferences.Preferences;
import net.sourceforge.kolmafia.request.CharSheetRequest;
import net.sourceforge.kolmafia.textui.command.AbstractCommandTestBase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class RuntimeLibraryTest extends AbstractCommandTestBase {
Expand Down Expand Up @@ -106,4 +109,27 @@ void floundryLocations() {
assertThat(output, containsString("tuna => The Oasis"));
}
}

@Nested
class NextCmcPill {
@Test
void canGetNextPill() {
var cleanups = new Cleanups(setProperty("lastCombatEnvironments", "iiiiiiiiiiioooouuuuu"));

try (cleanups) {
String output = execute("expected_cold_medicine_cabinet_pill()");
assertThat(output, startsWith("Returned: Extrovermectin&trade;"));
}
}

@Test
void returnsNoneIfPillUnknown() {
var cleanups = new Cleanups(setProperty("lastCombatEnvironments", "????????????????????"));

try (cleanups) {
String output = execute("expected_cold_medicine_cabinet_pill()");
assertThat(output, startsWith("Returned: none"));
}
}
}
}
@@ -0,0 +1,55 @@
package net.sourceforge.kolmafia.textui.command;

import static internal.helpers.Player.setProperty;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.startsWith;

import internal.helpers.Cleanups;
import net.sourceforge.kolmafia.KoLCharacter;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

public class ColdMedicineCabinetCommandTest extends AbstractCommandTestBase {
@BeforeAll
public static void beforeAll() {
KoLCharacter.reset("ColdMedicineCabinetCommandTest");
}

public ColdMedicineCabinetCommandTest() {
this.command = "cmc";
}

@ParameterizedTest
@CsvSource({
"i, Extrovermectin&trade;",
"o, Homebodyl&trade;",
"u, Breathitin&trade;",
"x, Fleshazole&trade;",
"?, unknown"
})
void showsRightPillForRightMajority(String environment, String pill) {
var cleanups =
new Cleanups(setProperty("lastCombatEnvironments", environment.repeat(11) + "x".repeat(9)));

try (cleanups) {
String output = execute("");

assertThat(output, startsWith("Your next pill is " + pill));
assertContinueState();
}
}

@Test
void showsFleshazoleForNoOverallMajority() {
var cleanups = new Cleanups(setProperty("lastCombatEnvironments", "iiiiiioooooouuuuuuio"));

try (cleanups) {
String output = execute("");

assertThat(output, startsWith("Your next pill is Fleshazole&trade;"));
assertContinueState();
}
}
}