From b7295b4beb269781b3c7735dcd8a1219baba54ce Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 2 Dec 2020 05:16:46 +0000 Subject: [PATCH] Improve finding sand by using slices --- game.py | 75 +++++++++++++++++++++++++-- items.py | 2 + jobs.py | 155 +++++++++++++++++++++++++++++++++++++++++++------------ path.py | 2 +- 4 files changed, 195 insertions(+), 39 deletions(-) diff --git a/game.py b/game.py index 420fddd..3efc779 100644 --- a/game.py +++ b/game.py @@ -163,11 +163,9 @@ class MCWorld: return False return True - def find_sand(self, center, distance, origin): + def find_sand(self, center, distance, player): sand = [] - for i in range(10): - check = utils.padd(center, utils.alternate(i, 1)) - sand.extend(self.find_blocks(check, distance, [blocks.SAND], 20)) + sand.extend(self.find_blocks(center, distance, [blocks.SAND], 25)) safe_sand = [] for s in sand: @@ -187,9 +185,61 @@ class MCWorld: safe_sand.append(s) - safe_sand.sort(key=lambda x: utils.phyp_bias(center, x, origin)) + safe_sand.sort(key=lambda x: utils.phyp(player, x)) return safe_sand + def check_sand_slice(self, center): + # checks if a 5x5x1 slice has diggable sand in it + for i in range(9): + s = utils.padd(center, utils.spiral(i)) + + if self.block_at(*s) != blocks.SAND: + continue + # make sure it has solid below + if self.block_at(*utils.padd(s, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS: + continue + # make sure it has solid two below - prevent hanging sand + if self.block_at(*utils.padd(s, path.BLOCK_BELOW2)) in blocks.NON_SOLID_IDS: + continue + # and walkable air above + if self.block_at(*utils.padd(s, path.BLOCK_ABOVE)) not in blocks.NON_SOLID_IDS: + continue + if not self.sand_adjacent_safe(s): + continue + + return True + + return False + + def find_sand_slice(self, center, distance, skip_to=(0, 0)): + # returns the centre coord of the next 5x5x1 slice that still has + # diggable sand in it. lower slices are only valid if there's an + # adjacent slice farther at the same level. this should ensure an + # upside down pyramid gets excavated so the edges are still climbable + skip_vertical, skip_spiral = skip_to + + for v in count(skip_vertical): + peak = utils.padd(center, (0, 20-v, 0)) + + layer = 0 + start_step = skip_spiral if v == skip_vertical else 0 + for step in count(start_step): + offset = utils.spiral(step) + layer = max(layer, *offset) + offset = utils.pmul(offset, 3) + check = utils.padd(peak, offset) + check = utils.padd(check, (0, layer, 0)) + + if utils.phyp(center, check) >= distance: + break + + if self.check_sand_slice(check): + return (v-1, step+1), check + + if v > 40: + return None, None + + def find_bed_openings(self, area): # returns coords in a cardinal direction where we can stand by bed result = [] @@ -454,8 +504,23 @@ class Game: if command == 'cache': self.g.job.state = self.g.job.cache_items + self.g.job.cache_items_states.minimum = 0 + self.g.job.cache_items_states.silent = True reply = 'ok' + if command == 'spiral' and data: + for i in range(int(data)): + print(utils.spiral(i)) + + if command == 'sand_slice': + try: + _, result = self.g.world.find_sand_slice(utils.pint(self.g.pos), 50) + reply = str(result) + except: + import traceback + print(traceback.format_exc()) + reply = 'error' + if reply: print(reply) if private and not reply.startswith('/'): diff --git a/items.py b/items.py index 4e0e935..195486e 100644 --- a/items.py +++ b/items.py @@ -47,5 +47,7 @@ CHEST_ID = set([ITEMS['minecraft:chest']['protocol_id']]) GAPPLE_ID = set([ITEMS['minecraft:enchanted_golden_apple']['protocol_id']]) +SAND_ID = set([ITEMS['minecraft:sand']['protocol_id']]) + NEEDED_ITEMS = BED_IDS | CHEST_ID WANTED_ITEMS = SAPLING_IDS diff --git a/jobs.py b/jobs.py index 410889a..ee8e123 100644 --- a/jobs.py +++ b/jobs.py @@ -21,6 +21,8 @@ importlib.reload(items) import data importlib.reload(data) +BREAK_DISTANCE = 5 + class FindGappleStates: def idle(self): @@ -291,37 +293,62 @@ class GatherSandStates: return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS def bsand(self, p): - return self.g.chunks.get_block_at(*p) == 66 + return self.g.chunks.get_block_at(*p) == blocks.SAND def idle(self): return None def init(self): - self.state = self.find_new_sand + self.state = self.find_new_slice + + def find_new_slice(self): + print('Finding new slice...') + w = self.g.world + + #o = utils.padd(self.origin, (0, 10, 0)) + print('using origin', self.origin) + self.skip, s = w.find_sand_slice(self.origin, 50) + print('Found slice:', s, 'skip:', self.skip) + + if s: + self.slice = s + self.state = self.find_new_sand + else: + print('No slices remaining.') + self.state = self.cleanup def find_new_sand(self): print('Finding new sand...') w = self.g.world p = utils.pint(self.g.pos) - sand = w.find_sand(p, 50, self.origin) + sand = w.find_sand(self.slice, 2, p) print('Found sand:', sand) + if not len(sand): + self.state = self.cleanup + return + for check in sand: if check in self.bad_sand: continue self.sand = check break - self.state = self.nav_to_sand + if utils.phyp(p, self.sand) > BREAK_DISTANCE: + self.state = self.nav_to_sand + else: + self.state = self.dig_sand def nav_to_sand(self): w = self.g.world p = utils.pint(self.g.pos) + c = self.g.chunks - self.g.chunks.set_block_at(*self.sand, blocks.AIR) + tmp = c.get_block_at(*self.sand) + c.set_block_at(*self.sand, blocks.AIR) navpath = w.path_to_place(p, self.sand) - self.g.chunks.set_block_at(*self.sand, blocks.SAND) + c.set_block_at(*self.sand, tmp) if navpath: self.g.path = navpath[:-1] @@ -338,27 +365,11 @@ class GatherSandStates: def dig_sand(self): if not self.g.breaking: if self.bsand(self.sand): + self.g.look_at = self.sand self.g.game.break_block(self.sand) print('digging sand') else: - self.state = self.get_sand - - def get_sand(self): - w = self.g.world - p = utils.pint(self.g.pos) - navpath = w.path_to_place(p, self.sand) - - if navpath: - self.g.path = navpath - self.state = self.going_to_item - else: - self.bad_sand.append(self.sand) - self.state = self.find_new_sand - - def going_to_item(self): - if utils.pint(self.g.pos) == self.sand: - self.g.look_at = self.sand - self.state = self.cleanup + self.state = self.find_new_sand def cleanup(self): self.g.look_at = None @@ -374,6 +385,8 @@ class GatherSandStates: self.state = self.idle self.origin = utils.pint(self.g.pos) + self.skip = (0, 0) + self.slice = None self.sand = None self.bad_sand = [] self.wait_time = 0 @@ -382,6 +395,75 @@ class GatherSandStates: self.state() +class GrabSandStates: + def idle(self): + return None + + def init(self): + self.state = self.find_sand + print('Trying to grab sand') + + def find_sand(self): + w = self.g.world + p = utils.pint(self.g.pos) + + sand = w.find_objects(items.SAND_ID) + + if not sand: + print('No sand objects found, aborting') + self.state = self.cleanup + return + + sand.sort(key=lambda s: utils.phyp(p, (s.x, s.y, s.z))) + + for s in sand: + s_pos = utils.pint((s.x, s.y, s.z)) + check = utils.padd(s_pos, path.BLOCK_BELOW) + + if utils.phyp(p, s_pos) > 6: + continue + if s.entity_id in self.eid_blacklist: + continue + # skip if the sand is floating + if self.g.chunks.get_block_at(*check) in {0}: + continue + + navpath = w.path_to_place(p, s_pos) + + if navpath: + self.g.path = navpath + self.state = self.going_to_sand + self.sand = s_pos + self.eid_blacklist.append(s.entity_id) + print('Going to sand', self.sand) + return + + print('Cant get to any more sand, aborting') + self.state = self.cleanup + + def going_to_sand(self): + if utils.pint(self.g.pos) == self.sand: + self.state = self.find_sand + + def cleanup(self): + self.g.look_at = None + self.state = self.done + + def done(self): + # never gets ran, placeholder + return None + + def __init__(self, global_state): + self.g = global_state + self.state = self.idle + + self.sand = None + self.eid_blacklist = [] + + def run(self): + self.state() + + class SleepWithBedStates: def idle(self): return None @@ -395,7 +477,6 @@ class SleepWithBedStates: def select_bed(self): if self.g.game.select_item(items.BED_IDS): - self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) self.state = self.find_bed_spot else: print('No bed, aborting.') @@ -482,7 +563,7 @@ class SleepWithBedStates: self.g = global_state self.state = self.idle - self.silent = False + self.silent = True self.area = None self.opening = None @@ -503,7 +584,7 @@ class CacheItemsStates: num_stacks = len([x for x in self.g.inv.values() if x.present]) print('Inventory amount:', num_stacks) - if num_stacks >= 27: + if num_stacks >= self.minimum: self.state = self.find_trapped_chests else: print('Aborting caching, not full') @@ -676,6 +757,7 @@ class CacheItemsStates: self.g = global_state self.state = self.idle + self.minimum = 27 self.silent = False # keep all needed items @@ -806,9 +888,7 @@ class ClearLeavesStates: w = self.g.world p = utils.pint(self.g.pos) - break_distance = 5 - - for l in w.find_leaves(p, break_distance): + for l in w.find_leaves(p, BREAK_DISTANCE): self.leaves.append(l) self.state = self.break_leaves @@ -879,7 +959,7 @@ class GrabSaplingStates: if s.entity_id in self.eid_blacklist: continue - # slip if the sapling is floating + # skip if the sapling is floating if self.g.chunks.get_block_at(*check) in blocks.LEAF_IDS | {0}: continue @@ -945,13 +1025,15 @@ class JobStates: def gather_sand(self): s1 = self.gather_sand_states - s2 = self.sleep_with_bed_states - s3 = self.cache_items_states + s2 = self.grab_sand_states + s3 = self.sleep_with_bed_states + s4 = self.cache_items_states if s1.state == s1.idle: s1.state = s1.init s2.state = s2.init s3.state = s3.init + s4.state = s4.init elif s1.state == s1.done: if s2.state != s2.done: s2.run() @@ -961,9 +1043,14 @@ class JobStates: s3.run() return + if s4.state != s4.done: + s4.run() + return + s1.state = s1.init s2.state = s2.init s3.state = s3.init + s4.state = s4.init return s1.run() @@ -1051,6 +1138,7 @@ class JobStates: self.plant_tree_states = PlantTreeStates(self.g) self.clear_leaves_states = ClearLeavesStates(self.g) self.grab_sapling_states = GrabSaplingStates(self.g) + self.grab_sand_states = GrabSandStates(self.g) self.state = self.idle def __init__(self, global_state): @@ -1066,6 +1154,7 @@ class JobStates: self.plant_tree_states = PlantTreeStates(self.g) self.clear_leaves_states = ClearLeavesStates(self.g) self.grab_sapling_states = GrabSaplingStates(self.g) + self.grab_sand_states = GrabSandStates(self.g) def tick(self): self.state() diff --git a/path.py b/path.py index 5fa23a5..33e8021 100644 --- a/path.py +++ b/path.py @@ -131,7 +131,7 @@ class Pathfinder(AStar): @functools.lru_cache(maxsize=BLOCK_CACHE_SIZE) def bavoid(self, p): - return self.chunks.get_block_at(*p) in blocks.AVOID_IDS + return self.chunks.get_block_at(*p) in blocks.AVOID_IDS or p[1] < 0 def check_traverse(self, node, offset): dest = utils.padd(node, offset)