Add 3D BFS iterator
This commit is contained in:
parent
6c4aaf8d7d
commit
0bbe516818
12
blocks.py
12
blocks.py
|
@ -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 = [
|
||||||
|
|
47
game.py
47
game.py
|
@ -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,8 +414,15 @@ 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)
|
||||||
|
if private and not reply.startswith('/'):
|
||||||
|
self.g.chat.send('/m ' + sender + ' ' + reply)
|
||||||
|
else:
|
||||||
self.g.chat.send(reply)
|
self.g.chat.send(reply)
|
||||||
|
|
||||||
def handle_time_update(self, packet):
|
def handle_time_update(self, packet):
|
||||||
|
@ -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,
|
||||||
|
|
3
items.py
3
items.py
|
@ -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
101
jobs.py
|
@ -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
|
||||||
|
|
||||||
|
|
48
utils.py
48
utils.py
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user