예제 #1
0
 def perform(self):
     parser = MazeParser(self.maze_file)
     #Parser returns Maze named-tuple
     parsed_maze = parser.perform()
     solver = MazeSolver(parsed_maze)
     solved_maze_coords = solver.perform()
     if not solved_maze_coords:
         #Solver has failed to solve the maze
         return
     plotter = MazePlotter(parsed_maze.maze_map, solved_maze_coords)
     return plotter.perform()
예제 #2
0
def solve_maze(maze_url):
    # Collect a maze and solve it using the MazeSolver object:
    maze = get_maze(maze_url)
    solution = MazeSolver(maze).execute()
    # Check solution and return the status:
    check_URL = "https://api.noopschallenge.com" + maze["mazePath"]
    reply = {"directions": solution}
    confirm = requests.post(check_URL, data=json.dumps(reply))

    return confirm.json()
예제 #3
0
    def __init__(self):
        """ Main controller """
        rospy.init_node('maze_navigator')

        self.odom = None
        self.prevOdom = None
        self.scan = []
        self.projected = []

        self.solver = MazeSolver()
        self.listener = TransformListener()
        self.broadcaster = TransformBroadcaster()

        self.counter = 0

        self.currentI = 0 #index to keep track of our instruction
        self.twist = Twist()
        self.laserScan = LaserScan()
        self.turn = True #turning or moving straight

        self.dist_centroid = 0
        self.angle_centroid = 0
        self.point = None
        self.foundHuman = False
        self.foundRealHuman = False
        self.humanThreshhold = .7  # distance that it will detect a human 

        self.maxDistance = .6
        self.wallDistance = .3
        self.nodeDistance = .8 # distance between nodes
        
        #publish robot commands and fake lidar data
        self.pubScan = rospy.Publisher('/maze_scan', LaserScan, queue_size=10)
        self.pubVel = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
        self.pubToViz = rospy.Publisher('/centroid', PointStamped, queue_size=10)

        #subscribe to robot position and real lidar data
        rospy.Subscriber('/odom', Odometry, self.callbackOdom)
        rospy.Subscriber('/scan', LaserScan, self.callbackScan)

        self.solver.visualize((0, 0)) 
예제 #4
0
class MazeNavigator(object):
    """ Main controller for the robot maze solver """
    def __init__(self):
        """ Main controller """
        rospy.init_node('maze_navigator')

        self.odom = None
        self.prevOdom = None
        self.scan = []
        self.projected = []

        self.solver = MazeSolver()
        self.listener = TransformListener()
        self.broadcaster = TransformBroadcaster()

        self.counter = 0

        self.currentI = 0 #index to keep track of our instruction
        self.twist = Twist()
        self.laserScan = LaserScan()
        self.turn = True #turning or moving straight

        self.dist_centroid = 0
        self.angle_centroid = 0
        self.point = None
        self.foundHuman = False
        self.foundRealHuman = False
        self.humanThreshhold = .7  # distance that it will detect a human 

        self.maxDistance = .6
        self.wallDistance = .3
        self.nodeDistance = .8 # distance between nodes
        
        #publish robot commands and fake lidar data
        self.pubScan = rospy.Publisher('/maze_scan', LaserScan, queue_size=10)
        self.pubVel = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
        self.pubToViz = rospy.Publisher('/centroid', PointStamped, queue_size=10)

        #subscribe to robot position and real lidar data
        rospy.Subscriber('/odom', Odometry, self.callbackOdom)
        rospy.Subscriber('/scan', LaserScan, self.callbackScan)

        self.solver.visualize((0, 0)) 

        
    def callbackScan(self, data):
        """ updates on new scan data
            data: LaserScan data
        """
        if not self.scan:
            self.laserScan = data
        self.scan = data.ranges

    def callbackOdom(self, data):
        """ updates on new odom data
            data: Odometry data
        """
        self.odom = convert_pose_to_xy_and_theta(data.pose)
        if not self.prevOdom: #first reading
            self.prevOdom = self.odom #no change

    def detectHuman(self):
        """ look at the robot's scan and detect where the centroid of the human is 
        """

        # find a cluster of points within certain range
        objectdict = {}
        sumx = 0
        sumy = 0
        for i in range(0, 360):
            if self.scan[i] !=0 and self.scan[i] < self.humanThreshhold:
                locX = self.scan[i]*math.cos(i*math.pi/180.0)
                locY = self.scan[i]*math.sin(i*math.pi/180.0)
                objectdict[i] = [locX, locY]
                sumx += locX
                sumy += locY
        if len(objectdict) !=0: #found a human
            #publish the centroid for rViz
            centroid = [sumx/len(objectdict), sumy/len(objectdict)]
            self.dist_centroid = math.sqrt(centroid[0]**2 + centroid[1]**2)
            self.angle_centroid = math.atan2(centroid[1],centroid[0])
            self.point = PointStamped(point=Point(x=-centroid[0], y=-centroid[1]), header=Header(stamp=rospy.Time.now(), frame_id='base_laser_link'))
            self.foundHuman = True
        else: #no human found
            self.foundHuman = False
        
        if self.foundHuman and self.projected: #compare human location to wall locations
            self.projectedDistance = self.projected[int(self.angle_centroid*180/math.pi)]

            if self.projectedDistance == 0 or self.projectedDistance > self.dist_centroid: #human closer than
                self.foundRealHuman = True
            else: #if human is behind a 'wall'
                self.foundRealHuman = False #no human

        self.foundRealHuman = self.foundRealHuman and self.foundHuman


    def updateNode(self, instruction):
        """ updates visualization and publishes new scan data when a new node is reached
            instruction: new instruction
        """
        self.detectHuman()

        if self.foundRealHuman:
            self.currentI += 1 #increment instruction
            newNode = self.solver.path[self.currentI]
            
            self.turn = True 
            self.prevOdom = self.odom #update odometry
            
            wall = self.getWalls(instruction[1], newNode)
            self.projected = self.projectMaze(wall) #get new laser scan 
            
            stamp = rospy.Time.now()
            self.laserScan.ranges = tuple(self.projectMaze(wall)) #update laser scan
            self.laserScan.header=Header(stamp=rospy.Time.now(),frame_id="base_laser_link")
            fix_map_to_odom_transform(self, stamp, newNode, instruction[1], self.listener, self.broadcaster) #transform coordinate frames
            self.solver.visualize(newNode) #update visualization
        
        else:
            self.twist.linear.x = 0 #stop the robot
            self.twist.angular.z = 0 

    def performInstruction(self):
        """ sets twist and updates maze scan
            based on current instruction and odometry reading
        """
        if not self.odom or not self.prevOdom:
            return

        c = .5 #proportional control constant
        instruction = self.solver.instructions[self.currentI]

        if not self.projected:
            newNode = self.solver.path[self.currentI]
            wall = self.getWalls(instruction[1], newNode)
            self.projected = self.projectMaze(wall) #get new laser scan 

        diffPos, diffAng = self.calcDifference(instruction) #difference in position, difference in angle

        if abs(diffAng)%(2*math.pi) < .05: #turned to correct orientation
            self.turn = False
        
        if abs(diffPos) < .05: #moved forward successfully to next node
            self.updateNode(instruction)

        if self.turn: #set angular velocity
            self.twist.angular.z = c * diffAng
            self.twist.linear.x = 0

        else: #set linear velocity
            self.twist.linear.x = c *diffPos
            self.twist.angular.z = 0

    def calcDifference(self, instruction):
        """ calculate the difference in position and orientation between current and previous odometry
            returns tuple of form (difference in position, difference in orientation)
        """
        
        diffPos = self.nodeDistance - math.sqrt((self.odom[0] - self.prevOdom[0])**2 + (self.odom[1] - self.prevOdom[1])**2)
        diffAng = instruction[0] - angle_diff(self.odom[2],self.prevOdom[2])
        return diffPos, diffAng

    def getWalls(self, orientation, currentNode):
        """ get a representation of maze walls the robot can understand
            currentNode: coordinates of current node
            orientation: current orientation of the robot
            returns: list of length 4 with binary entries
                True is a path
                False is a wall
        """
        neighbors = self.solver.getNeighbors(currentNode)
        walls = [None]*4 #default is all walls
        for nextNode in neighbors:
            nextOrient = self.solver.getNextOrientation(currentNode, nextNode)
            walls[nextOrient] = 1 #update list with True where paths exist
        return walls[orientation:] + walls[:orientation] #rotated depending on robot's position


    def projectMaze(self, wall):
        """ get 'laser scan' ranges for the virtual maze based on surrounding walls
            wall: list with binary entries, output from getWalls
            returns a list of length 359 with maze scan data to be published
        """
        projected = [0]*361

        for i in range(1, 360):
            if i <= 45 or i > 315:
                if not wall[0]:
                   projected[i] = self.wallDistance / math.cos(i*math.pi / 180)
                else:
                    c = 1.0 if i <= 45 else -1.0
                    distance = self.wallDistance / math.sin(i*math.pi / 180) *c
                    projected[i] = 0 if distance > self.maxDistance else distance

            elif i <= 135:
                if not wall[3]:
                    projected[i] = self.wallDistance / math.sin(i*math.pi / 180)
                else:
                    c = 1.0 if i <= 90 else -1.0
                    distance = self.wallDistance /  math.cos(i*math.pi / 180) * c
                    projected[i] = 0 if distance > self.maxDistance else distance

            elif i <= 225:
                if not wall[2]:
                    projected[i] = self.wallDistance / -math.cos(i*math.pi / 180)
                else:
                    c = 1.0 if i <= 180 else -1.0
                    distance = self.wallDistance / math.sin(i*math.pi / 180) *c
                    projected[i] = 0 if distance > self.maxDistance else distance

            elif i <= 315:
                if not wall[1]:
                    projected[i] = self.wallDistance / -math.sin( i*math.pi / 180)
                else:
                    c = -1.0 if i <= 270 else 1.0
                    distance = self.wallDistance / math.cos(i*math.pi / 180)*c
                    projected[i] = 0 if distance > self.maxDistance else distance
        return projected

            
    def run(self):
        """ Our main 5Hz run loop
        """
        r = rospy.Rate(5)
        while not rospy.is_shutdown():
            if self.currentI < len(self.solver.instructions): #still have instructions to perform
                self.performInstruction()
                self.pubScan.publish(self.laserScan) #publish scans
                self.pubVel.publish(self.twist)
                if self.point:
                    self.pubToViz.publish(self.point)
                r.sleep()

            else: 
                self.twist.linear.x = 0 #stop the robot
                self.twist.angular.z = 0
                self.pubVel.publish(self.twist)
                print "done traversing the maze"
                break #exit
예제 #5
0
from maze import Maze
from maze_walker import MazeWalker
from maze_solver import MazeSolver
import maze_walker
import keyboard
from enum import Enum
import time
import os

rr = time.time()
a = MazeSolver(20, 20)
a.generate_prim()

a.find_path()
a.print()
print(time.time() - rr)
예제 #6
0
 def test_solves_valid_maze(self):
     parser = MazeParser('mazes/maze_pass.txt')
     parsed_maze = parser.perform()
     solver = MazeSolver(parsed_maze)
     self.assertTrue(solver.perform())
예제 #7
0
 def test_returns_from_unsolvable_maze(self):
     parser = MazeParser('mazes/maze_fail_no_solution.txt')
     parsed_maze = parser.perform()
     solver = MazeSolver(parsed_maze)
     self.assertFalse(solver.perform())
예제 #8
0
from maze_solver import MazeSolver

maze_solver = MazeSolver("maze_definition.txt", "maze_solution.txt")
maze_solver.solve()


예제 #9
0
def main():
		
	'''
	Load mazes.
	'''
	
	pyramid = None
	
	input_output = MazeIO()
	load_maze = str(raw_input("Do you want to load an existing maze? (Y/N) "))
	
	if load_maze.lower() == "y":
		file_name = str(raw_input("Enter file name: "))
		pyramid = input_output.load_maze(file_name)
	
	else:
		generator = MazeGenerator()
		print "MazeGen initialized..."
		
		'''
		The purpose of calling the signal function is to break the execution of generate_maze,
		if no maze seems to be produced. This was to fix the error in the generate_maze algorithm,
        as it cannot always be guaranteed that its execution stops.
		'''
		
		maze_done = False
		while not maze_done:
			#signal.signal(signal.SIGALRM, handler)
			
			#signal.alarm(5)
			
			try:
				pyramid = generator.generate_maze()
				if pyramid:
					maze_done = True
			except NameError:
				maze_done = False
			#signal.alarm(0)
	
	if pyramid != None:
	
		print "Pyramid initialized..."
			
		name = str(raw_input("Give player's name: "))
		
		if len(name) > 0:
			player = PlayerObject(name, pyramid)
		else:
			player = PlayerObject("Mikael", pyramid)
		
		pygame.init()
	
		graffat = GraphicsEngine()
		print "Graphics initialized..."
		
		interfeissi = CommandEngine()
		
		if_done = False
		clock = pygame.time.Clock()
		
		while if_done == False:
			
			if_done = interfeissi.handle_events(player)		
			
			#update graphics here
			
			graffat.draw_everything(player)
			
			# Limit to 20 frames per second
			clock.tick(20)
			if player.is_at_goal():
				print "Goal reached! Hurrah!"
				if_done = True
		
		print player, 'used', player.get_steps(), 'steps for solving this labyrinth.'
		
		show_solution = str(raw_input("Do you want to see the computer's solution? (Y/N) "))
		
		if show_solution.lower() == 'y':
			solver = MazeSolver()
			solution = solver.solve_maze(pyramid)
			height = len(solution)
			
			show_which_level = 0
			while show_which_level >= 0:
				#update graphics here
				
				graffat.draw_solution(solution, show_which_level)
				
				show_which_level = interfeissi.solution_mode(show_which_level, height)
				
				# Limit to 20 frames per second
				clock.tick(20)
		
		pygame.quit()
		
		'''
		Save mazes.
		'''
		
		save_yn = str(raw_input("Do you want to save this maze? (Y/N) "))
		
		if save_yn.lower() == "y":
			file_name = str(raw_input("Enter file name: "))
			input_output.save_maze(player.get_pyramid(), file_name)