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
 | 
					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])
 | 
					NON_SOLID_IDS = set([SINGLE_SNOW])
 | 
				
			||||||
for block_name in NON_SOLID:
 | 
					for block_name in NON_SOLID:
 | 
				
			||||||
    for state in BLOCKS[block_name]['states']:
 | 
					    for state in BLOCKS[block_name]['states']:
 | 
				
			||||||
@@ -192,3 +212,13 @@ AVOID_IDS = set()
 | 
				
			|||||||
for block_name in AVOID:
 | 
					for block_name in AVOID:
 | 
				
			||||||
    for state in BLOCKS[block_name]['states']:
 | 
					    for state in BLOCKS[block_name]['states']:
 | 
				
			||||||
        AVOID_IDS.add(state['id'])
 | 
					        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 os
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import functools
 | 
					import functools
 | 
				
			||||||
from math import ceil, floor, hypot
 | 
					from math import ceil, floor, hypot, sqrt
 | 
				
			||||||
 | 
					from itertools import count
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import blocks
 | 
					import blocks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -318,6 +320,116 @@ class MazeSolver(AStar):
 | 
				
			|||||||
        (x2, y2, z2) = n2
 | 
					        (x2, y2, z2) = n2
 | 
				
			||||||
        return hypot(x2 - x1, z2 - z1)
 | 
					        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
 | 
					TICK = 0.05
 | 
				
			||||||
ANGLE_DIR = LVector3f(x=0, y=0, z=-1)
 | 
					ANGLE_DIR = LVector3f(x=0, y=0, z=-1)
 | 
				
			||||||
ANGLE_REF = LVector3f(x=0, y=1, z=0)
 | 
					ANGLE_REF = LVector3f(x=0, y=1, z=0)
 | 
				
			||||||
@@ -497,6 +609,20 @@ def main(connection, player_info):
 | 
				
			|||||||
            except BaseException as e:
 | 
					            except BaseException as e:
 | 
				
			||||||
                import traceback
 | 
					                import traceback
 | 
				
			||||||
                print(traceback.format_exc())
 | 
					                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(
 | 
					    connection.register_packet_listener(
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user