Add 3D BFS iterator

This commit is contained in:
Tanner Collin 2020-10-16 02:56:11 -06:00
parent 6c4aaf8d7d
commit 0bbe516818
5 changed files with 143 additions and 70 deletions

View File

@ -59,6 +59,12 @@ AVOID = [
'sandstone_wall', 'sandstone_wall',
'end_stone_brick_wall', 'end_stone_brick_wall',
'diorite_wall', 'diorite_wall',
'oak_sapling', # saplings can grow up and hurt
'spruce_sapling',
'birch_sapling',
'jungle_sapling',
'acacia_sapling',
'dark_oak_sapling',
] ]
NON_SOLID = [ NON_SOLID = [
@ -194,12 +200,6 @@ NON_SOLID = [
'cave_air', 'cave_air',
'lantern', 'lantern',
'soul_torch', 'soul_torch',
#'oak_sapling', # saplings can grow up and hurt
#'spruce_sapling',
#'birch_sapling',
#'jungle_sapling',
#'acacia_sapling',
#'dark_oak_sapling',
] ]
LOGS = [ LOGS = [

49
game.py
View File

@ -41,6 +41,12 @@ class MCWorld:
def block_at(self, x, y, z): def block_at(self, x, y, z):
return self.g.chunks.get_block_at(x, y, z) return self.g.chunks.get_block_at(x, y, z)
def find_blocks_3d(self, center, block_ids, distance=0, y_limit=0):
for offset in utils.search_3d(distance, y_limit):
check = utils.padd(center, offset)
if self.block_at(*check) in block_ids:
yield check
def find_blocks(self, center, distance, block_ids, limit=0): def find_blocks(self, center, distance, block_ids, limit=0):
# search in a spiral from center to all blocks with ID # search in a spiral from center to all blocks with ID
result = [] result = []
@ -112,16 +118,10 @@ class MCWorld:
return None return None
def find_bed_areas(self, center, distance): def find_bed_areas(self, center, distance):
air = []
for i in range(5):
check = utils.padd(center, utils.alternate(i, 1))
air.extend(self.find_blocks(check, distance, [0], 0))
bed_clearance = 25 # 5x5 area bed_clearance = 25 # 5x5 area
clear_distance = 3 clear_distance = 3
areas = [] for a in self.find_blocks_3d(center, [0], distance, 10):
for a in air:
# check for air around the area # check for air around the area
if len(self.find_blocks(a, clear_distance, [0], bed_clearance)) < bed_clearance: if len(self.find_blocks(a, clear_distance, [0], bed_clearance)) < bed_clearance:
continue continue
@ -134,10 +134,7 @@ class MCWorld:
if len(self.find_blocks(utils.padd(a, path.BLOCK_ABOVE), clear_distance, [0], bed_clearance)) < bed_clearance: if len(self.find_blocks(utils.padd(a, path.BLOCK_ABOVE), clear_distance, [0], bed_clearance)) < bed_clearance:
continue continue
areas.append(a) yield a
areas.sort(key=lambda x: utils.phyp(center, x))
return areas
def find_cache_areas(self, center, distance): def find_cache_areas(self, center, distance):
return self.find_bed_areas(center, distance) return self.find_bed_areas(center, distance)
@ -264,15 +261,19 @@ class Game:
def handle_chat(self, message): def handle_chat(self, message):
source, text = message source, text = message
reply = None reply = None
private = False
if source == 'SYSTEM': if source == 'SYSTEM':
print('unlocking command...')
self.g.command_lock = False self.g.command_lock = False
return return
match = re.match(r'<(\w+)> (.*)', text) match1 = re.match(r'<(\w+)> (.*)', text)
if match: match2 = re.match(r'\[(\w+) -> me] (.*)', text)
sender, text = match.groups() if match1:
sender, text = match1.groups()
elif match2:
sender, text = match2.groups()
private = True
else: else:
return return
@ -350,15 +351,12 @@ class Game:
reply = 'ok' reply = 'ok'
if command == 'inv': if command == 'inv':
print(self.g.inv)
inv_list = [] inv_list = []
for i in self.g.inv.values(): for i in self.g.inv.values():
if i.present: if i.present:
inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count)) inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
reply = ', '.join(inv_list) result = '\n'.join(inv_list)
print(result or 'Empty')
if not reply:
reply = 'empty'
if command == 'drop': if command == 'drop':
self.drop_stack() self.drop_stack()
@ -416,9 +414,16 @@ class Game:
if data and v.item_id != int(data): continue if data and v.item_id != int(data): continue
print(str(k) + ':', v) print(str(k) + ':', v)
if command == 'cache':
self.g.job.state = self.g.job.cache_items
reply = 'ok'
if reply: if reply:
print(reply) print(reply)
self.g.chat.send(reply) if private and not reply.startswith('/'):
self.g.chat.send('/m ' + sender + ' ' + reply)
else:
self.g.chat.send(reply)
def handle_time_update(self, packet): def handle_time_update(self, packet):
self.g.time = packet.time_of_day % 24000 self.g.time = packet.time_of_day % 24000
@ -578,7 +583,7 @@ class Game:
def handle_spawn_object(self, packet): def handle_spawn_object(self, packet):
#return #return
if packet.type_id != 37: return if packet.type_id != 37: return
print(packet) #print(packet)
self.g.objects[packet.entity_id] = Munch( self.g.objects[packet.entity_id] = Munch(
entity_id=packet.entity_id, entity_id=packet.entity_id,
x=packet.x, x=packet.x,

View File

@ -47,4 +47,5 @@ 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']])
USEFUL_ITEMS = BED_IDS | CHEST_ID NEEDED_ITEMS = BED_IDS | CHEST_ID
WANTED_ITEMS = SAPLING_IDS

101
jobs.py
View File

@ -385,20 +385,16 @@ class SleepWithBedStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
areas = w.find_bed_areas(p, 100) for area in w.find_bed_areas(p, 100):
print('Found areas:', areas) print('Found area:', area)
if area not in self.bad_areas:
if len(areas): break
while areas[0] in self.bad_areas: else: # for
areas.pop(0) print('Unable to find area')
self.area = areas[0]
elif self.last_area:
self.area = self.last_area
else:
print('Unable to find area, and no last area')
self.state = self.cleanup self.state = self.cleanup
return return
self.area = area
openings = w.find_bed_openings(self.area) openings = w.find_bed_openings(self.area)
for o in openings: for o in openings:
@ -413,7 +409,6 @@ class SleepWithBedStates:
self.g.path = navpath self.g.path = navpath
self.state = self.going_to_area self.state = self.going_to_area
self.last_area = self.area
def going_to_area(self): def going_to_area(self):
if utils.pint(self.g.pos) == self.opening: if utils.pint(self.g.pos) == self.opening:
@ -479,7 +474,6 @@ class SleepWithBedStates:
self.area = None self.area = None
self.opening = None self.opening = None
self.bad_areas = [] self.bad_areas = []
self.last_area = None
self.wait_time = 0 self.wait_time = 0
def run(self): def run(self):
@ -491,8 +485,11 @@ class CacheItemsStates:
return None return None
def init(self): def init(self):
self.skip_slots = []
self.skip_items = []
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 >= 27:
self.state = self.find_cache_spot self.state = self.find_cache_spot
else: else:
@ -504,20 +501,16 @@ class CacheItemsStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
areas = w.find_cache_areas(p, 100) for area in w.find_cache_areas(p, 100):
print('Found areas:', areas) print('Found area:', area)
if area not in self.bad_areas:
if len(areas): break
while areas[0] in self.bad_areas: else: # for
areas.pop(0) print('Unable to find area')
self.area = areas[0]
elif self.last_area:
self.area = self.last_area
else:
print('Unable to find area, and no last area')
self.state = self.cleanup self.state = self.cleanup
return return
self.area = area
openings = w.find_cache_openings(self.area) openings = w.find_cache_openings(self.area)
for o in openings: for o in openings:
@ -532,7 +525,6 @@ class CacheItemsStates:
self.g.path = navpath self.g.path = navpath
self.state = self.going_to_area self.state = self.going_to_area
self.last_area = self.area
def going_to_area(self): def going_to_area(self):
if utils.pint(self.g.pos) == self.opening: if utils.pint(self.g.pos) == self.opening:
@ -571,6 +563,8 @@ class CacheItemsStates:
w_info = data.WINDOWS[w.data.window_type] w_info = data.WINDOWS[w.data.window_type]
w_inventory_slots = w_info.inventory w_inventory_slots = w_info.inventory
slot_list = []
for slot_num in w_inventory_slots: for slot_num in w_inventory_slots:
if slot_num not in w.contents: if slot_num not in w.contents:
continue continue
@ -580,14 +574,25 @@ class CacheItemsStates:
if not slot.present: if not slot.present:
continue continue
if slot.item_id in items.USEFUL_ITEMS: if slot.item_id in self.needed_items:
continue
if slot_num in self.skip_slots:
continue
slot_list.append((slot_num, slot))
slot_list.sort(key=lambda x: x[1].item_count, reverse=True)
for slot_num, slot in slot_list:
if slot.item_id in self.wanted_items and slot.item_id not in self.skip_items:
print('skipping wanted item', slot)
self.skip_slots.append(slot_num)
self.skip_items.append(slot.item_id)
continue continue
print('moving', slot) print('moving', slot)
#inv_slot_num = slot_num - w_info.slot_diff
#self.g.inv.pop(inv_slot_num, None)
self.g.item_lock = True self.g.item_lock = True
self.g.game.click_window(slot_num, 0, 1, slot) self.g.game.click_window(slot_num, 0, 1, slot)
return return
@ -613,10 +618,17 @@ class CacheItemsStates:
self.g = global_state self.g = global_state
self.state = self.idle self.state = self.idle
# keep all needed items
self.needed_items = items.NEEDED_ITEMS
# keep one stack of wanted items
self.wanted_items = items.WANTED_ITEMS
self.skip_slots = []
self.skip_items = []
self.area = None self.area = None
self.opening = None self.opening = None
self.bad_areas = [] self.bad_areas = []
self.last_area = None
self.wait_time = 0 self.wait_time = 0
def run(self): def run(self):
@ -681,19 +693,16 @@ class PlantTreeStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
areas = w.find_cache_areas(p, 20) for area in w.find_cache_areas(p, 20):
print('Found areas:', areas) print('Found area:', area)
if area not in self.bad_areas:
try: break
while areas[0] in self.bad_areas: else: # for
areas.pop(0) print('Unable to find area')
self.area = areas[0]
except IndexError:
print('No good areas left, aborting.')
self.bad_areas = []
self.state = self.cleanup self.state = self.cleanup
return return
self.area = area
navpath = w.path_to_place(p, self.area) navpath = w.path_to_place(p, self.area)
if not navpath: if not navpath:
@ -803,6 +812,16 @@ class JobStates:
def idle(self): def idle(self):
return None return None
def cache_items(self):
s1 = self.cache_items_states
if s1.state == s1.idle:
s1.state = s1.init
elif s1.state == s1.done:
self.state = self.idle
s1.run()
def find_gapple(self): def find_gapple(self):
s1 = self.find_gapple_states s1 = self.find_gapple_states

View File

@ -1,4 +1,5 @@
import importlib import importlib
import collections
from math import floor, ceil, sqrt, hypot from math import floor, ceil, sqrt, hypot
import blocks import blocks
@ -94,3 +95,50 @@ def break_time(block_id, held_item=0, in_water=False, on_ground=True, enchantmen
if not on_ground: time *= 5.0 if not on_ground: time *= 5.0
return time return time
def search_3d(distance=0, y_limit=0):
def get_neighbors(x,y,z):
return [
(x+1, y+1, z+0),
(x+1, y-1, z+0),
(x+1, y+1, z+1),
(x+1, y+0, z+1),
(x+1, y-1, z+1),
(x+1, y+1, z-1),
(x+1, y+0, z-1),
(x+1, y-1, z-1),
(x+1, y+0, z+0),
(x+0, y+1, z+0),
(x+0, y-1, z+0),
(x+0, y+1, z+1),
(x+0, y+0, z+1),
(x+0, y-1, z+1),
(x+0, y+1, z-1),
(x+0, y+0, z-1),
(x+0, y-1, z-1),
(x-1, y+1, z+0),
(x-1, y-1, z+0),
(x-1, y+1, z+1),
(x-1, y+0, z+1),
(x-1, y-1, z+1),
(x-1, y+1, z-1),
(x-1, y+0, z-1),
(x-1, y-1, z-1),
(x-1, y+0, z+0),
]
to_visit = collections.deque([(0, 0, 0)])
visited = set()
while to_visit:
cur = to_visit.pop()
if cur in visited:
continue
if y_limit and abs(cur[1]) > y_limit:
continue
if distance and hypot(*cur) > distance:
continue
for neighbor in get_neighbors(*cur):
to_visit.appendleft(neighbor)
visited.add(cur)
yield cur