Improve finding sand by using slices

This commit is contained in:
Tanner Collin 2020-12-02 05:16:46 +00:00
parent eabb0a04d1
commit b7295b4beb
4 changed files with 195 additions and 39 deletions

75
game.py
View File

@ -163,11 +163,9 @@ class MCWorld:
return False return False
return True return True
def find_sand(self, center, distance, origin): def find_sand(self, center, distance, player):
sand = [] sand = []
for i in range(10): sand.extend(self.find_blocks(center, distance, [blocks.SAND], 25))
check = utils.padd(center, utils.alternate(i, 1))
sand.extend(self.find_blocks(check, distance, [blocks.SAND], 20))
safe_sand = [] safe_sand = []
for s in sand: for s in sand:
@ -187,9 +185,61 @@ class MCWorld:
safe_sand.append(s) 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 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): def find_bed_openings(self, area):
# returns coords in a cardinal direction where we can stand by bed # returns coords in a cardinal direction where we can stand by bed
result = [] result = []
@ -454,8 +504,23 @@ class Game:
if command == 'cache': if command == 'cache':
self.g.job.state = self.g.job.cache_items 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' 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: if reply:
print(reply) print(reply)
if private and not reply.startswith('/'): if private and not reply.startswith('/'):

View File

@ -47,5 +47,7 @@ CHEST_ID = set([ITEMS['minecraft:chest']['protocol_id']])
GAPPLE_ID = set([ITEMS['minecraft:enchanted_golden_apple']['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 NEEDED_ITEMS = BED_IDS | CHEST_ID
WANTED_ITEMS = SAPLING_IDS WANTED_ITEMS = SAPLING_IDS

155
jobs.py
View File

@ -21,6 +21,8 @@ importlib.reload(items)
import data import data
importlib.reload(data) importlib.reload(data)
BREAK_DISTANCE = 5
class FindGappleStates: class FindGappleStates:
def idle(self): def idle(self):
@ -291,37 +293,62 @@ class GatherSandStates:
return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS
def bsand(self, p): 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): def idle(self):
return None return None
def init(self): 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): def find_new_sand(self):
print('Finding new sand...') print('Finding new sand...')
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) 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) print('Found sand:', sand)
if not len(sand):
self.state = self.cleanup
return
for check in sand: for check in sand:
if check in self.bad_sand: if check in self.bad_sand:
continue continue
self.sand = check self.sand = check
break 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): def nav_to_sand(self):
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) 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) 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: if navpath:
self.g.path = navpath[:-1] self.g.path = navpath[:-1]
@ -338,27 +365,11 @@ class GatherSandStates:
def dig_sand(self): def dig_sand(self):
if not self.g.breaking: if not self.g.breaking:
if self.bsand(self.sand): if self.bsand(self.sand):
self.g.look_at = self.sand
self.g.game.break_block(self.sand) self.g.game.break_block(self.sand)
print('digging sand') print('digging sand')
else: else:
self.state = self.get_sand self.state = self.find_new_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
def cleanup(self): def cleanup(self):
self.g.look_at = None self.g.look_at = None
@ -374,6 +385,8 @@ class GatherSandStates:
self.state = self.idle self.state = self.idle
self.origin = utils.pint(self.g.pos) self.origin = utils.pint(self.g.pos)
self.skip = (0, 0)
self.slice = None
self.sand = None self.sand = None
self.bad_sand = [] self.bad_sand = []
self.wait_time = 0 self.wait_time = 0
@ -382,6 +395,75 @@ class GatherSandStates:
self.state() 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: class SleepWithBedStates:
def idle(self): def idle(self):
return None return None
@ -395,7 +477,6 @@ class SleepWithBedStates:
def select_bed(self): def select_bed(self):
if self.g.game.select_item(items.BED_IDS): 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 self.state = self.find_bed_spot
else: else:
print('No bed, aborting.') print('No bed, aborting.')
@ -482,7 +563,7 @@ class SleepWithBedStates:
self.g = global_state self.g = global_state
self.state = self.idle self.state = self.idle
self.silent = False self.silent = True
self.area = None self.area = None
self.opening = None self.opening = None
@ -503,7 +584,7 @@ class CacheItemsStates:
num_stacks = len([x for x in self.g.inv.values() if x.present]) num_stacks = len([x for x in self.g.inv.values() if x.present])
print('Inventory amount:', num_stacks) print('Inventory amount:', num_stacks)
if num_stacks >= 27: if num_stacks >= self.minimum:
self.state = self.find_trapped_chests self.state = self.find_trapped_chests
else: else:
print('Aborting caching, not full') print('Aborting caching, not full')
@ -676,6 +757,7 @@ class CacheItemsStates:
self.g = global_state self.g = global_state
self.state = self.idle self.state = self.idle
self.minimum = 27
self.silent = False self.silent = False
# keep all needed items # keep all needed items
@ -806,9 +888,7 @@ class ClearLeavesStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) 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.leaves.append(l)
self.state = self.break_leaves self.state = self.break_leaves
@ -879,7 +959,7 @@ class GrabSaplingStates:
if s.entity_id in self.eid_blacklist: if s.entity_id in self.eid_blacklist:
continue 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}: if self.g.chunks.get_block_at(*check) in blocks.LEAF_IDS | {0}:
continue continue
@ -945,13 +1025,15 @@ class JobStates:
def gather_sand(self): def gather_sand(self):
s1 = self.gather_sand_states s1 = self.gather_sand_states
s2 = self.sleep_with_bed_states s2 = self.grab_sand_states
s3 = self.cache_items_states s3 = self.sleep_with_bed_states
s4 = self.cache_items_states
if s1.state == s1.idle: if s1.state == s1.idle:
s1.state = s1.init s1.state = s1.init
s2.state = s2.init s2.state = s2.init
s3.state = s3.init s3.state = s3.init
s4.state = s4.init
elif s1.state == s1.done: elif s1.state == s1.done:
if s2.state != s2.done: if s2.state != s2.done:
s2.run() s2.run()
@ -961,9 +1043,14 @@ class JobStates:
s3.run() s3.run()
return return
if s4.state != s4.done:
s4.run()
return
s1.state = s1.init s1.state = s1.init
s2.state = s2.init s2.state = s2.init
s3.state = s3.init s3.state = s3.init
s4.state = s4.init
return return
s1.run() s1.run()
@ -1051,6 +1138,7 @@ class JobStates:
self.plant_tree_states = PlantTreeStates(self.g) self.plant_tree_states = PlantTreeStates(self.g)
self.clear_leaves_states = ClearLeavesStates(self.g) self.clear_leaves_states = ClearLeavesStates(self.g)
self.grab_sapling_states = GrabSaplingStates(self.g) self.grab_sapling_states = GrabSaplingStates(self.g)
self.grab_sand_states = GrabSandStates(self.g)
self.state = self.idle self.state = self.idle
def __init__(self, global_state): def __init__(self, global_state):
@ -1066,6 +1154,7 @@ class JobStates:
self.plant_tree_states = PlantTreeStates(self.g) self.plant_tree_states = PlantTreeStates(self.g)
self.clear_leaves_states = ClearLeavesStates(self.g) self.clear_leaves_states = ClearLeavesStates(self.g)
self.grab_sapling_states = GrabSaplingStates(self.g) self.grab_sapling_states = GrabSaplingStates(self.g)
self.grab_sand_states = GrabSandStates(self.g)
def tick(self): def tick(self):
self.state() self.state()

View File

@ -131,7 +131,7 @@ class Pathfinder(AStar):
@functools.lru_cache(maxsize=BLOCK_CACHE_SIZE) @functools.lru_cache(maxsize=BLOCK_CACHE_SIZE)
def bavoid(self, p): 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): def check_traverse(self, node, offset):
dest = utils.padd(node, offset) dest = utils.padd(node, offset)