Locate nearest tree and pathfind to it
This commit is contained in:
		
							
								
								
									
										30
									
								
								blocks.py
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								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'])
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										128
									
								
								bot.py
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								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(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user