From a16b297db7f09c4d00f5ac945edfb86c28b1acd8 Mon Sep 17 00:00:00 2001 From: Tanner Date: Sat, 7 Nov 2026 22:14:46 +0000 Subject: [PATCH] Avoid redundant cache chest visits --- mosfet/jobs/cache_items.py | 43 ++++++++++++++++++++++++------ tests/test_cache_items.py | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/mosfet/jobs/cache_items.py b/mosfet/jobs/cache_items.py index ad0e777..33a8589 100644 --- a/mosfet/jobs/cache_items.py +++ b/mosfet/jobs/cache_items.py @@ -20,7 +20,7 @@ class CacheItemsStates: return None def init(self): - self.skip_slots = [] + self.skip_inventory_slots = set() self.skip_items = [] self.failed_transfer_slots = set() self.pending_transfer = None @@ -30,8 +30,8 @@ class CacheItemsStates: self.container_item_ids = None num_stacks = self.g.game.count_inventory_slots() - minimum = getattr( - self, 'minimum', self.g.get('minimum_cache_slots', 27)) + minimum = self.__dict__.pop( + 'minimum', self.g.get('minimum_cache_slots', 27)) print('Inventory amount:', num_stacks) if num_stacks == 0: self.state = self.cleanup @@ -79,8 +79,7 @@ class CacheItemsStates: if key in self.checked_labeled_chests: continue self.checked_labeled_chests.add(key) - if not any(item.present and item.item_id in container['item_ids'] - for item in self.g.inv.values()): + if not self._has_transferable_item(container['item_ids']): continue self.area = container['position'] @@ -97,6 +96,7 @@ class CacheItemsStates: self.opening = opening_position self.open_interaction = opening self.g.path = navpath + self.g.look_at = None self.state = self.going_to_trapped_chest return self.bad_openings.add(opening_position) @@ -128,6 +128,7 @@ class CacheItemsStates: self.opening = opening_position self.open_interaction = opening self.g.path = navpath + self.g.look_at = None self.state = self.going_to_trapped_chest return self.bad_openings.add(opening_position) @@ -140,6 +141,8 @@ class CacheItemsStates: self.g.look_at = self.open_interaction['look_at'] self.wait_time = 0.5 self.state = self.turn_to_trapped_chest + else: + self.g.look_at = None def turn_to_trapped_chest(self): self.g.look_at = self.open_interaction['look_at'] @@ -191,6 +194,7 @@ class CacheItemsStates: return self.g.path = navpath + self.g.look_at = None self.state = self.going_to_area def going_to_area(self): @@ -198,6 +202,8 @@ class CacheItemsStates: self.g.look_at = self.open_interaction['look_at'] self.wait_time = 0.5 self.state = self.turn_to_cache_spot + else: + self.g.look_at = None def turn_to_cache_spot(self): self.g.look_at = self.open_interaction['look_at'] @@ -255,6 +261,24 @@ class CacheItemsStates: keep.add(max(stacks, key=lambda pair: pair[1].item_count)[0]) return keep + def _has_transferable_item(self, item_ids): + item_ids = set(item_ids) + needed_slots = self._needed_inventory_slots_to_keep() + planned_skip_items = set(self.skip_items) + for inventory_slot, item in self.g.inv.items(): + if (not item.present or item.item_id not in item_ids or + inventory_slot in self.skip_inventory_slots): + continue + if (item.item_id in items.NEEDED_ITEMS and + inventory_slot in needed_slots): + continue + if (item.item_id in items.WANTED_ITEMS and + item.item_id not in planned_skip_items): + planned_skip_items.add(item.item_id) + continue + return True + return False + def _window_player_slot(self, window_slot): offset = window_slot - self.g.window.container_slots if 0 <= offset < 27: @@ -333,7 +357,8 @@ class CacheItemsStates: elif slot.item_id in items.NEEDED_ITEMS: continue - if (slot_num in self.skip_slots or + inventory_slot = self._window_player_slot(slot_num) + if (inventory_slot in self.skip_inventory_slots or slot_num in self.failed_transfer_slots): continue @@ -344,7 +369,9 @@ class CacheItemsStates: for slot_num, slot in slot_list: if slot.item_id in items.WANTED_ITEMS and slot.item_id not in self.skip_items: print('skipping wanted item', slot) - self.skip_slots.append(slot_num) + inventory_slot = self._window_player_slot(slot_num) + if inventory_slot is not None: + self.skip_inventory_slots.add(inventory_slot) self.skip_items.append(slot.item_id) continue @@ -393,7 +420,7 @@ class CacheItemsStates: self.silent = False - self.skip_slots = [] + self.skip_inventory_slots = set() self.failed_transfer_slots = set() self.pending_transfer = None self.transferred_to_labeled_chest = False diff --git a/tests/test_cache_items.py b/tests/test_cache_items.py index 2a347c6..17a7b73 100644 --- a/tests/test_cache_items.py +++ b/tests/test_cache_items.py @@ -30,6 +30,39 @@ class CacheItemsTests(unittest.TestCase): 'Found 11 labeled chests; found 0 trapped chests: []', output.getvalue()) + def test_wanted_single_stack_is_not_considered_transferable(self): + wanted_id = items.WHEAT_SEEDS_ID + old_wanted = items.WANTED_ITEMS + items.WANTED_ITEMS = set(old_wanted) | {wanted_id} + try: + state = CacheItemsStates(Munch(inv={ + 9: Munch(present=True, item_id=wanted_id, item_count=56), + })) + self.assertFalse(state._has_transferable_item({wanted_id})) + + state.g.inv[10] = Munch( + present=True, item_id=wanted_id, item_count=12) + self.assertTrue(state._has_transferable_item({wanted_id})) + finally: + items.WANTED_ITEMS = old_wanted + + def test_manual_cache_threshold_override_is_consumed_once(self): + class FakeGame: + @staticmethod + def count_inventory_slots(): + return 5 + + state = CacheItemsStates(Munch( + game=FakeGame(), minimum_cache_slots=27)) + state.minimum = 0 + + state.init() + self.assertIs(state.state.__func__, state.find_trapped_chests.__func__) + self.assertNotIn('minimum', state.__dict__) + + state.init() + self.assertIs(state.state.__func__, state.cleanup.__func__) + def test_successful_labeled_transfer_skips_placing_another_cache_chest(self): item_id = items.get_id('stone') stack = Munch(present=True, item_id=item_id, item_count=16) @@ -133,6 +166,27 @@ class CacheItemsTests(unittest.TestCase): self.assertFalse(state._opening_in_bad_region(FakeWorld(), candidate)) self.assertNotIn(candidate, state.bad_openings) + def test_cache_travel_looks_along_path_then_turns_to_chest(self): + opening = (5, 64, 8) + chest_look = (4.9, 64.1, 7.8) + state = CacheItemsStates(Munch( + pos=(2.5, 64, 2.5), look_at=(1, 64, 1))) + state.opening = opening + state.open_interaction = {'look_at': chest_look} + + state.going_to_trapped_chest() + self.assertIsNone(state.g.look_at) + + state.g.pos = opening + state.going_to_trapped_chest() + self.assertEqual(state.g.look_at, chest_look) + self.assertIs(state.state.__func__, state.turn_to_trapped_chest.__func__) + + state.g.pos = (2.5, 64, 2.5) + state.g.look_at = (1, 64, 1) + state.going_to_area() + self.assertIsNone(state.g.look_at) + def test_cache_chest_placement_sneaks_to_avoid_opening_support_container(self): calls = []