import io import unittest from contextlib import redirect_stdout from munch import Munch from minecraft.networking.types import BlockFace from mosfet.info import items from mosfet.jobs.cache_items import CacheItemsStates class CacheItemsTests(unittest.TestCase): def test_chest_search_log_distinguishes_labeled_and_trapped_counts(self): class FakeWorld: @staticmethod def find_labeled_chests(*_args): return [{} for _ in range(11)] @staticmethod def find_blocks_indexed(*_args): return [] state = CacheItemsStates(Munch( world=FakeWorld(), pos=(0, 64, 0))) output = io.StringIO() with redirect_stdout(output): state.find_trapped_chests() self.assertIn( '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_post_sleep_halves_threshold_only_with_compatible_nearby_chest(self): bed = (0, 64, 0) chest_position = (5, 64, 0) item_id = items.get_id('stone') class FakeGame: @staticmethod def count_inventory_slots(): return 14 class NearbyWorld: @staticmethod def find_labeled_chests(_center, _distance): return [{ 'position': chest_position, 'positions': (chest_position,), 'item_ids': {item_id}, }] g = Munch( game=FakeGame(), world=NearbyWorld(), minimum_cache_slots=27, post_sleep_cache_bed=bed, inv={9: Munch(present=True, item_id=item_id, item_count=64)}) state = CacheItemsStates(g) state.init() self.assertIs(state.state.__func__, state.find_trapped_chests.__func__) self.assertNotIn('post_sleep_cache_bed', g) def test_post_sleep_threshold_rounds_27_up_to_14(self): bed = (0, 64, 0) item_id = items.get_id('stone') class FakeGame: @staticmethod def count_inventory_slots(): return 13 class NearbyWorld: @staticmethod def find_labeled_chests(_center, _distance): return [{ 'position': (5, 64, 0), 'positions': ((5, 64, 0),), 'item_ids': {item_id}, }] g = Munch( game=FakeGame(), world=NearbyWorld(), minimum_cache_slots=27, post_sleep_cache_bed=bed, inv={9: Munch(present=True, item_id=item_id, item_count=64)}) state = CacheItemsStates(g) state.init() self.assertIs(state.state.__func__, state.cleanup.__func__) def test_post_sleep_does_not_lower_threshold_without_compatible_nearby_chest(self): class FakeGame: @staticmethod def count_inventory_slots(): return 14 class EmptyWorld: @staticmethod def find_labeled_chests(_center, _distance): return [] g = Munch( game=FakeGame(), world=EmptyWorld(), minimum_cache_slots=27, post_sleep_cache_bed=(0, 64, 0), inv={}) state = CacheItemsStates(g) state.init() self.assertIs(state.state.__func__, state.cleanup.__func__) self.assertNotIn('post_sleep_cache_bed', g) def test_post_sleep_ignores_compatible_chest_beyond_16_blocks(self): item_id = items.get_id('stone') class FakeGame: @staticmethod def count_inventory_slots(): return 14 class FarWorld: @staticmethod def find_labeled_chests(_center, _distance): position = (17, 64, 0) return [{ 'position': position, 'positions': (position,), 'item_ids': {item_id}, }] state = CacheItemsStates(Munch( game=FakeGame(), world=FarWorld(), minimum_cache_slots=27, post_sleep_cache_bed=(0, 64, 0), inv={9: Munch(present=True, item_id=item_id, item_count=64)})) state.init() self.assertIs(state.state.__func__, state.cleanup.__func__) 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) window = Munch(container_slots=27, contents={ **dict.fromkeys(range(27)), 27: stack, }) clicks = [] class FakeGame: @staticmethod def count_window_slots(): return 0 @staticmethod def click_window(*args): clicks.append(args) class FakeWorld: @staticmethod def find_blocks_indexed(*_args): return [] @staticmethod def find_labeled_chests(*_args): return [] g = Munch( game=FakeGame(), world=FakeWorld(), pos=(0, 64, 0), inv={}, window=window, item_lock=False) state = CacheItemsStates(g) state.is_labeled_container = True state.container_item_ids = {item_id} state.move_items() self.assertEqual(len(clicks), 1) self.assertFalse(state.transferred_to_labeled_chest) window.contents[27] = Munch(present=False, item_id=0, item_count=0) g.item_lock = False state.pending_transfer['confirm_after'] = 0 state.move_items() self.assertTrue(state.transferred_to_labeled_chest) state.labeled_chests = [] state.trapped_chests = [] state.choose_trapped_chest() self.assertIs(state.state.__func__, state.cleanup.__func__) def test_mutually_reachable_opening_is_skipped_after_path_failure(self): start = (0, 64, 0) first = (4, 64, 0) nearby = (4, 64, 1) item_id = items.get_id('stone') calls = [] class FakeWorld: @staticmethod def find_cache_openings(area, _center): position = first if area == (10, 64, 0) else nearby return [{'position': position}] def path_to_place(self, origin, target): origin, target = tuple(origin), tuple(target) calls.append((origin, target)) if origin == start and target == first: return None if (origin, target) in ((nearby, first), (first, nearby)): return [origin, target] return None state = CacheItemsStates(Munch( world=FakeWorld(), pos=start, inv={9: Munch(present=True, item_id=item_id)})) state.labeled_chests = [ {'position': (10, 64, 0), 'positions': ((10, 64, 0),), 'item_ids': {item_id}, 'labels': {'Stone'}}, {'position': (11, 64, 0), 'positions': ((11, 64, 0),), 'item_ids': {item_id}, 'labels': {'Stone'}}, ] state.trapped_chests = [] state.choose_trapped_chest() self.assertEqual(calls, [ (start, first), (nearby, first), (first, nearby), ]) self.assertIn(first, state.bad_openings) self.assertIn(nearby, state.bad_openings) def test_one_way_reachability_does_not_blacklist_opening(self): bad = (1, 64, 0) candidate = (2, 64, 0) class FakeWorld: @staticmethod def path_to_place(start, target): return [start, target] if tuple(start) == candidate else None state = CacheItemsStates(Munch()) state.bad_openings = {bad} 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 = [] class FakeGame: @staticmethod def place_block(*args, **kwargs): calls.append((args, kwargs)) state = CacheItemsStates(Munch(game=FakeGame())) state.area = (3, 65, 7) state.place_chest() self.assertEqual(calls, [( ((3, 64, 7), BlockFace.TOP), {'sneaking': True}, )]) self.assertIs(state.state.__func__, state.open_chest.__func__) if __name__ == '__main__': unittest.main()