diff --git a/blocks.py b/blocks.py index d6647c4..322b7fc 100644 --- a/blocks.py +++ b/blocks.py @@ -183,6 +183,26 @@ NON_SOLID = [ ] SINGLE_SNOW = 3919 + +LOGS = [ + 'minecraft:oak_log', + 'minecraft:spruce_log', + 'minecraft:birch_log', + 'minecraft:jungle_log', + 'minecraft:acacia_log', + 'minecraft:dark_oak_log', +] + +LEAVES = [ + 'minecraft:oak_leaves', + 'minecraft:spruce_leaves', + 'minecraft:birch_leaves', + 'minecraft:jungle_leaves', + 'minecraft:acacia_leaves', + 'minecraft:dark_oak_leaves', +] + + NON_SOLID_IDS = set([SINGLE_SNOW]) for block_name in NON_SOLID: for state in BLOCKS[block_name]['states']: @@ -192,3 +212,13 @@ AVOID_IDS = set() for block_name in AVOID: for state in BLOCKS[block_name]['states']: AVOID_IDS.add(state['id']) + +LOG_IDS = set() +for block_name in LOGS: + for state in BLOCKS[block_name]['states']: + LOG_IDS.add(state['id']) + +LEAF_IDS = set() +for block_name in LEAVES: + for state in BLOCKS[block_name]['states']: + LEAF_IDS.add(state['id']) diff --git a/bot.py b/bot.py index 492c2bc..2d62a94 100644 --- a/bot.py +++ b/bot.py @@ -1,7 +1,9 @@ import os import time import functools -from math import ceil, floor, hypot +from math import ceil, floor, hypot, sqrt +from itertools import count + import blocks @@ -318,6 +320,116 @@ class MazeSolver(AStar): (x2, y2, z2) = n2 return hypot(x2 - x1, z2 - z1) + +def spiral(n): + # return x, 0, z coords along a spiral at step n + n += 1 + k = ceil((sqrt(n)-1)/2) + t = 2 * k + 1 + m = t**2 + t = t - 1 + if n >= m-t: + return k-(m-n), 0, -k + else: + m = m-t + if n >= m-t: + return -k, 0, -k+(m-n) + else: + m = m-t + if n >= m-t: + return -k+(m-n), 0, k + else: + return k, 0, k-(m-n-t) + +def alternate(n, amount): + # return 0, y, 0 where y alternates +/- by amount + # example: 0, 2, -2, 4, -4, 6, -6 for amount = 2 + sign = 1 if n % 2 else -1 + return (0, ceil(n/2) * sign * amount, 0) + +BLOCK_ABOVE = (0, +1, 0) +BLOCK_BELOW = (0, -1, 0) + +CHECK_NORTH = (0, 0, -1) +CHECK_SOUTH = (0, 0, +1) +CHECK_EAST = (+1, 0, 0) +CHECK_WEST = (-1, 0, 0) + +CHECK_DIRECTIONS = [ + CHECK_NORTH, + CHECK_SOUTH, + CHECK_EAST, + CHECK_WEST, +] + +class MCWorld: + def __init__(self, chunks): + self.chunks = chunks + + def block_at(self, x, y, z): + return self.chunks.get_block_at(x, y, z) + + def find_blocks(self, center, distance, block_ids, limit=0): + # search in a spiral from center to all blocks with ID + result = [] + for n in count(): + offset = spiral(n) + check = padd(center, offset) + if self.block_at(*check) in block_ids: + if hypot(*offset) < distance: + result.append(check) + if limit and len(result) == limit: + return result + if offset[0] > distance: + return result + + def find_tree(self, center, distance): + logs = [] + for i in range(5): + check = padd(center, alternate(i, 4)) + logs.extend(self.find_blocks(center, distance, blocks.LOG_IDS, 5)) + + for log in logs: + # crawl to the top log + while self.block_at(*padd(log, BLOCK_ABOVE)) in blocks.LOG_IDS: + log = padd(log, BLOCK_ABOVE) + + # make sure it's a tree + if self.block_at(*padd(log, BLOCK_ABOVE)) in blocks.LEAF_IDS: + break + else: # for + return None + + # crawl to the bottom log + while self.block_at(*padd(log, BLOCK_BELOW)) in blocks.LOG_IDS: + log = padd(log, BLOCK_BELOW) + + return log + + def find_tree_openings(self, tree): + # returns coords in a cardinal direction where we can stand by tree + maze_solver = MazeSolver(self.chunks) + result = [] + + for distance in range(5): + for direction in CHECK_DIRECTIONS: + offset = (0, 0, 0) + for _ in range(distance): + offset = padd(offset, direction) + if maze_solver.check_traverse(tree, offset): + result.append(padd(tree, offset)) + return result + + def navigate_to_opening(self, start, opening): + maze_solver = MazeSolver(self.chunks) + + try: + return list(maze_solver.astar(start, opening)) + except AStarTimeout: + return None + + + TICK = 0.05 ANGLE_DIR = LVector3f(x=0, y=0, z=-1) ANGLE_REF = LVector3f(x=0, y=1, z=0) @@ -497,6 +609,20 @@ def main(connection, player_info): except BaseException as e: import traceback print(traceback.format_exc()) + elif '!tree' in chat_packet.json_data: + try: + mc_world = MCWorld(player_info.chunks) + start = time.time() + coords = mc_world.find_tree(pint(player_info.pos), 100) + print(coords) + openings = mc_world.find_tree_openings(coords) + print(openings) + path = mc_world.navigate_to_opening(pint(player_info.pos), openings[0]) + print(path) + print(round(time.time() - start, 3), 'seconds') + except BaseException as e: + import traceback + print(traceback.format_exc()) connection.register_packet_listener(