class ExampleTestPlanet(unittest.TestCase): def setUp(self): """ Instantiates the planet data structure and fills it with paths example planet: +--+ | | +-0,3------+ | | 0,2-----2,2 (target) | / +-0,1 / | | / +-0,0-1,0 | (start) """ # set your data structure self.planet = Planet() # add the paths self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 1), Direction.WEST), ((0, 0), Direction.WEST), 1) def test_target_not_reachable_with_loop(self): # does the shortest path algorithm loop infinitely? # there is no shortest path self.assertIsNone(self.planet.shortest_path((0, 0), (1, 2)))
class ExampleTestPlanet(unittest.TestCase): def setUp(self): """ Instantiates the planet data structure and fills it with paths example planet: +--+ | | +-0,3------+ | | 0,2-----2,2 (target) | / +-0,1 / | | / +-0,0-1,0 | (start) """ # set your data structure self.planet = Planet() # add the paths self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 1), Direction.WEST), ((0, 0), Direction.WEST), 1)
class ExampleTestPlanet(unittest.TestCase): def setUp(self): """ Instantiates the planet data structure and fills it with paths +--+ | | +-0,3------+ | | 0,2-----2,2 (target) | / +-0,1 / | | / +-0,0-1,0 | (start) """ # Initialize your data structure here self.planet = Planet() self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 1), Direction.WEST), ((0, 0), Direction.WEST), 1) @unittest.skip('Example test, should not count in final test results') def test_target_not_reachable_with_loop(self): """ This test should check that the shortest-path algorithm does not get stuck in a loop between two points while searching for a target not reachable nearby Result: Target is not reachable """ self.assertIsNone(self.planet.shortest_path((0, 0), (1, 2)))
class Communication: """ Class to hold the MQTT client Feel free to add functions, change the constructor and the example send_message() to satisfy your requirements and thereby solve the task according to the specifications """ def __init__(self, mqtt_client): """ Initializes communication module, connect to server, subscribe, etc. """ # THESE TWO VARIABLES MUST NOT BE CHANGED self.client = mqtt_client self.client.on_message = self.on_message # ADD YOUR VARIABLES HERE # Basic configuration of MQTT # Wichtig? self.client.on_message = self.on_message_excepthandler self.client.username_pw_set('118', password='******') # Your group credentials self.client.connect('mothership.inf.tu-dresden.de', port=8883) self.client.subscribe('explorer/118', qos=1) # Subscribe to topic explorer/xxx # self.send_ready() # Start listening to incoming messages self.client.loop_start() #self.timer() self.planet = Planet() #Parameter: self.data = None self.topic = "explorer/118" self.planet_Chan = None self.aktX = None self.aktY = None self.direc = None # this is a helper method that catches errors and prints them # it is necessary because on_message is called by paho-mqtt in a different thread and exceptions # are not handled in that thread # # you don't need to change this method at all def on_message_excepthandler(self, client, data, message): try: self.on_message(client, data, message) except: import traceback traceback.print_exc() raise # THIS FUNCTIONS SIGNATURE MUST NOT BE CHANGED def on_message(self, client, data, message): """ Handles the callback if any message arrived """ print('Got message with topic "{}":'.format(message.topic)) data = json.loads(message.payload.decode('utf-8')) print(json.dumps(data, indent=2)) print('\n') self.data = data self.typ_Entsch() #self.timer() #Timer: jede 2 Sekunden warten: def timer(self): t0 = time.time() while (time.time()-t0) < 2: pass print("neue Message kommt!") def typ_Entsch(self): #Test #print("schon in typ_Epntsch") von = self.data["from"] nach = self.data["type"] if von == "server" and nach == "planet": payload = self.data["payload"] planetName = payload["planetName"] self.planet_Chan = 'planet/'+planetName+'-118' self.client.subscribe(self.planet_Chan, qos=1) self.aktX = payload["startX"] self.aktY = payload["startY"] print(self.aktX, self.aktY) elif von == "server" and nach == "pathSelect": self.serverPath() elif von == "server" and nach == "path": self.set_korrePos() def send_ready(self): erk = '{"from": "client", "type": "ready"}' self.client.publish("explorer/118", erk, qos=1) def send_test(self): mess = '{"from": "client", "type": "testplanet", "payload": {"planetName":"Hawkeye"}}' self.client.publish("explorer/118", mess, qos=1) def set_korrePos(self): korre_pos = self.data["payload"] startX = int(korre_pos["startX"]) startY = int(korre_pos["startY"]) startDir = korre_pos["startDirection"] endX = int(korre_pos["endX"]) endY = int(korre_pos["endY"]) endDir = korre_pos["endDirection"] weight = int(korre_pos["pathWeight"]) self.aktX = endX self.aktY = endY self.direc = endDir self.planet.add_path(((startX, startY), startDir), ((endX, endY), endDir), weight) return [(endX, endY), endDir] def pruefDaten(self): self.pathStat = "free" print(self.planet_Chan) pruef = '{"from":"client", "type":"path", "payload": {"startX": '+str(13)+', "startY": '+str(37)+', "startDirection": "N", "endX": '+str(13)+', "endY": '+str(38)+', "endDirection": "S", "pathStatus": "'+str(self.pathStat)+'"} }' self.client.publish(self.planet_Chan, pruef, qos=1) def pruefDaten2(self): self.pathStat = "free" print(self.planet_Chan) pruef = '{"from":"client", "type":"path", "payload": {"startX": '+str(13)+', "startY": '+str(38)+', "startDirection": "N", "endX": '+str(14)+', "endY": '+str(39)+', "endDirection": "W", "pathStatus": "'+str(self.pathStat)+'"} }' self.client.publish(self.planet_Chan, pruef, qos=1) def pruefDaten3(self): self.pathStat = "free" print(self.planet_Chan) pruef = '{"from":"client", "type":"path", "payload": {"startX": '+str(14)+', "startY": '+str(39)+', "startDirection": "S", "endX": '+str(15)+', "endY": '+str(37)+', "endDirection": "W", "pathStatus": "'+str(self.pathStat)+'"} }' self.client.publish(self.planet_Chan, pruef, qos=1) def pruefDaten4(self): self.pathStat = "free" print(self.planet_Chan) pruef = '{"from":"client", "type":"path", "payload": {"startX": '+str(15)+', "startY": '+str(37)+', "startDirection": "E", "endX": '+str(17)+', "endY": '+str(37)+', "endDirection": "W", "pathStatus": "'+str(self.pathStat)+'"} }' self.client.publish(self.planet_Chan, pruef, qos=1) def pruefDaten5(self): self.pathStat = "free" print(self.planet_Chan) pruef = '{"from":"client", "type":"path", "payload": {"startX": '+str(17)+', "startY": '+str(37)+', "startDirection": "N", "endX": '+str(17)+', "endY": '+str(38)+', "endDirection": "W", "pathStatus": "'+str(self.pathStat)+'"} }' self.client.publish(self.planet_Chan, pruef, qos=1) def pathSelect(self, node): result = self.planet.unknown_paths(node) startX = result[0][0] startY = result[0][1] startDir = result[1].value self.aktX = startX self.aktY = startY self.direc = startDir select = '{"from":"client", "type":"pathSelect", "payload": {"startX": '+str(startX)+', "startY": '+str(startY)+', "startDirection": "'+str(startDir)+'"} }' self.client.publish(self.planet_Chan, select, qos=1) return self.direc def serverPath(self): path_server = self.data["payload"] startDir = path_server["startDirection"] self.direc = startDir return Direction(startDir)
class Robot: def __init__(self, mqtt_client, logger): # Create Planet and planet_name variable self.planet = Planet() self.planet_name = None # Setup Sensors, Motors and Odometry self.rm = ev3.LargeMotor("outB") self.lm = ev3.LargeMotor("outC") self.sound = ev3.Sound() self.odometry = Odometry(self.lm, self.rm) self.motors = Motors(self.odometry) self.cs = ColorSensor(self.motors) self.us = Ultrasonic() # Setup Communication self.communication = Communication(mqtt_client, logger, self) # Create variable to write to from communication self.start_location = None self.end_location = None self.path_choice = None self.running = True def run(self): counter = 0 bottle_detected = False previous_l, previous_r, previous_b = 0, 0, 350 start_message = """ ################################## # # # RoboLab Praktikum 2020 # # Exploration by Paula # # ❰❱ with ❤ by # # Eric, Marc, Nico # # # ################################## """ # Print start screen print(start_message) # Calibrate colors for better color accuracy self.cs.calibrate_colors() # Wait until we want to start print("» Press Button to start") sensors.button_pressed() start_time = time.time() print(time.strftime("Starttime: %H:%M:%S", time.localtime())) print("Lets start to explore...") # Start fancy features feature_threads = features.start_features(self) # Main robot loop while self.running: # Test if robot detect an obstacle if self.us.get_distance() < 15: # Rotate robot to drive back, and save that an obstacle was detected self.sound.play("/home/robot/src/assets/found.wav") print("Obstacle detected") self.cs.rotate_to_path(180) bottle_detected = True continue # Test if robot reached node if self.cs.get_node() in ["blue", "red"] and counter == 0: counter += 1 # Drive to center of node to better scanning self.motors.drive_in_center_of_node(100, 2) # Check if robot reached first node if self.planet_name is None: # Tell mothership to send planet information (planet_name and start coordinates) self.communication.send_ready() # Wait until message is received while self.planet_name is None: pass # Reset odometry of last path, not used for first path self.odometry.reset_list() # Current robot orientation forward_dir = self.start_location[1] # End-position and direction of incoming path self.end_location = (self.start_location[0], (forward_dir + 180) % 360) # Save path as blocked for better path calculation self.planet.add_path(self.end_location, self.end_location, -1) else: # Test if path is known if self.start_location[0] in self.planet.planet_dict \ and self.start_location[1] in self.planet.planet_dict[self.start_location[0]]: # Get end node from planet dictionary ((x, y), send_dir, _) = self.planet.planet_dict[self.start_location[0]][self.start_location[1]] # Reset odometry of last path, not used for first path self.odometry.reset_list() else: # Calculate postion and direction from odometry x, y, forward_dir = self.odometry.calculate_path( self.start_location[1], bottle_detected, self.start_location[0][0], self.start_location[0][1]) # Direction facing back, used for path-message send_dir = (forward_dir + 180) % 360 # Send path to mothership get real position and direction self.communication.send_path(self.start_location, ((x, y), send_dir), bottle_detected) # Wait until message is received while self.end_location is None: pass # Position of current node current_pos = self.end_location[0] # Direction of robot forward_dir = (self.end_location[1] + 180) % 360 # Test if robot already scanned node if current_pos in self.planet.scanned_nodes: # If scanned, wait if mothership sends information like target and pathUnveiled time.sleep(2) else: # Scan node for posible directions possible_dirs = self.cs.analyze(forward_dir) # Save direction into unexlored edges if they aren't in the planet dictionary for d in possible_dirs: if current_pos not in self.planet.planet_dict \ or d not in self.planet.planet_dict[current_pos].keys(): self.planet.add_unexplored_edge(current_pos, d) # Mark node as scanned self.planet.scanned_nodes.append(current_pos) # Test if robot reached the target if current_pos == self.planet.target: # Send targetReached to mothership self.communication.send_target_reached("Target reached!") # Wait until confirmation while self.running: pass self.sound.play("/home/robot/src/assets/smb_stage_clear.wav") continue # Calculate next direction based on planet information and posible directions next_dir = self.choose_dir() # If no direction is found: Exloration completed if next_dir == -1: # Send explorationCompleted to mothership self.communication.send_exploration_completed("Exloration completed!") # Wait until confirmation while self.running: pass self.sound.play("/home/robot/src/assets/metroid.wav") continue # Save new starting postion for next path self.start_location = (self.end_location[0], next_dir) # Rotate to new path self.cs.select_new_path(forward_dir, next_dir) # Reset variables self.end_location = None bottle_detected = False self.odometry.reset_position() else: # Follow line and save last data for next tick previous_l, previous_r, previous_b = self.motors.follow_line(self.cs, previous_l, previous_r, previous_b) # new (better solution?) -> multiple times calles -> ticks_previous needed counter = 0 print(time.strftime("Endtime: %H:%M:%S", time.localtime())) delta = (time.time() - start_time) / 60 print("Timedelta: %.2f min" % delta) for thread in feature_threads: thread.join() def choose_dir(self): # Next direction, -1 = no direction found choice = -1 # Test if target is set if self.planet.target is not None: # Calculate shortest path to target shortest = self.planet.shortest_path(self.end_location[0], self.planet.target) # Test if target is reachable if shortest is not None: # Select first direction to get to target choice = shortest[0][1] # Test if no direction is set (no target or target not reachable) if choice == -1: # Nodes which need be explored incomplete_nodes = [] # Shortest distance to unexplored node distance = math.inf # Nearest node nearest = None # Collect all node, which have open paths for node in self.planet.unexplored_edges.keys(): incomplete_nodes.append(node) # Collect all node, which aren't scanned for node in self.planet.get_nodes(): if node not in self.planet.scanned_nodes: incomplete_nodes.append(node) # Test if all nodes are explored if len(incomplete_nodes) == 0: return -1 # Calculate nearest open node for node in incomplete_nodes: cur = self.planet.shortest_distance(self.end_location[0], node) if cur < distance: distance = cur nearest = node # Test if no open node is reachable (explorationCompleted) if distance == math.inf: return -1 # Test if open node is reached, choose first open path elif distance == 0: choice = self.planet.unexplored_edges[nearest][0] # Otherwise drive to open node (select first direction to get to open node) else: choice = self.planet.shortest_next_dir(self.end_location[0], nearest) # Send pathSelect to mothership self.communication.send_path_select((self.end_location[0], choice)) # Wait until message receive or 3sec (answer optional) start_time = time.time() while self.path_choice is None and time.time() - start_time < 3.0: pass # Test if mothership overwrites direction, use forced direction if self.path_choice is not None: choice = self.path_choice print("Next direction: %s" % Direction(choice)) # Reset Variable self.path_choice = None # Play sound self.sound.play("/home/robot/src/assets/codecover.wav") return choice
class YourFirstTestPlanet(unittest.TestCase): def setUp(self): """ Planet: +--+ | | +-0,3------+ | | 0,2-----2,2 (target) | / +-0,1 / | | / +-0,0-1,0 | (start) Instantiates the planet data structure and fills it with paths MODEL YOUR TEST PLANET HERE (if you'd like): """ # set your data structure self.planet = Planet() # ADD YOUR PATHS HERE: # self.planet.add_path(...) ''' self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) ''' def test_add_paths(self): self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) pprint.pprint(self.planet.planetKarte) def test_get_paths(self): self.planet.get_paths() pprint.pprint(self.planet.planetPaths) """ print(sorted(self.planet.planetPaths)) print(list(range(0,10))) l = [(1, 5),(2,5),(3,5)] for k,v in l: print(k, v) for s,v, in self.planet.planetPaths.items(): print("!") print(s) #print(v) for k,v in self.planet.planetPaths.get(s).items(): print(s, v[0], v[2]) #print(v) """ def test_integrity(self): # were all paths added correctly to the planet # check if add_path() works by using get_paths() self.assertEqual( self.planet.get_paths(), { (0, 0): { Direction.NORTH: ((0, 1), Direction.SOUTH, 1), Direction.EAST: ((1, 0), Direction.WEST, 1), Direction.WEST: ((0, 1), Direction.WEST, 2) }, (0, 1): { Direction.NORTH: ((0, 2), Direction.SOUTH, 1), Direction.SOUTH: ((0, 0), Direction.NORTH, 1), Direction.WEST: ((0, 0), Direction.WEST, 2) }, (0, 2): { Direction.NORTH: ((0, 3), Direction.SOUTH, 1), Direction.EAST: ((2, 2), Direction.WEST, 2), Direction.SOUTH: ((0, 1), Direction.NORTH, 1) }, (0, 3): { Direction.NORTH: ((0, 3), Direction.WEST, 2), Direction.EAST: ((2, 2), Direction.NORTH, 3), Direction.SOUTH: ((0, 2), Direction.NORTH, 1), Direction.WEST: ((0, 3), Direction.NORTH, 2) }, (1, 0): { Direction.NORTH: ((2, 2), Direction.SOUTH, 3), Direction.WEST: ((0, 0), Direction.EAST, 1) }, (2, 2): { Direction.NORTH: ((0, 3), Direction.EAST, 3), Direction.SOUTH: ((1, 0), Direction.NORTH, 3), Direction.WEST: ((0, 2), Direction.EAST, 2) } }) #self.fail('implement me!') def test_empty_planet(self): self.assertEqual(self.planet.shortest_path((0, 0), (1, 2)), []) def test_target_not_reachable(self): self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) self.assertEqual(self.planet.shortest_path((0, 0), (1, 2)), []) #self.fail('implement me!') def test_shortest_path(self): # at least 2 possible paths self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 2) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) #self.planet.get_paths() self.assertEqual(self.planet.shortest_path((0, 0), (2, 2)), [((0, 0), Direction.EAST), ((1, 0), Direction.NORTH)]) #print(self.planet.planetPaths.items()) #self.fail('implement me!') def test_same_length(self): # at least 2 possible paths with the same weight self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 4) self.assertEqual(self.planet.shortest_path((0, 0), ( 2, 2, )), ([((0, 0), 0), ((0, 1), 0), ((0, 2), 90)] or [((0, 0), 90), ((1, 0), 0)])) #self.fail('implement me!') def test_shortest_path_with_loop(self): # does the shortest path algorithm loop infinitely? # there is a shortest path self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 5) self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) self.assertEqual(self.planet.shortest_path((0, 2), ( 2, 2, )), [((0, 2), 0), ((0, 3), 90)]) #self.fail('implement me!') def test_target_not_reachable_with_loop(self): #there is no shortest path self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 1), Direction.EAST), ((1, 1), Direction.WEST), 1) self.planet.add_path(((0, 0), Direction.WEST), ((1, 0), Direction.EAST), 1) self.planet.add_path(((1, 0), Direction.NORTH), ((1, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.NORTH), ((1, 1), Direction.SOUTH), 2) self.planet.add_path(((0, 1), Direction.EAST), ((1, 0), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.WEST), ((0, 0), Direction.NORTH), 1) self.assertEqual(self.planet.shortest_path((0, 0), (5, 5)), []) #self.fail('implement me!') def test_target_not_reacheable_blocked(self): self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), -1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) #self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) #self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) self.assertEqual(self.planet.shortest_path((0, 0), (0, 3)), []) def test_target_not_reacheable_blocked_with_loop(self): self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), 1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), -1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.NORTH), ((0, 0), Direction.WEST), 1) # self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) self.assertEqual(self.planet.shortest_path((0, 0), (0, 3)), []) pass def test_shortest_path_with_blocked(self): self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), 1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.EAST), ((1, 0), Direction.WEST), -1) self.planet.add_path(((0, 1), Direction.NORTH), ((0, 2), Direction.SOUTH), -1) self.planet.add_path(((0, 2), Direction.NORTH), ((0, 3), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.NORTH), ((0, 3), Direction.WEST), 2) self.planet.add_path(((0, 3), Direction.EAST), ((2, 2), Direction.NORTH), 3) self.planet.add_path(((1, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 3) pass def test_add_path_twice(self): # at least 2 possible paths self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH), -1) self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) self.assertEqual(self.planet.planetPaths, self.planet.get_paths()) # self.planet.get_paths() print(self.planet.planetPaths) def test_add_path_bidirectional(self): self.planet.add_path(((0, 0), Direction.WEST), ((0, 1), Direction.WEST), 2) map_test = { (0, 0): { Direction.WEST: ((0, 1), Direction.WEST, 2) }, (0, 1): { Direction.WEST: ((0, 0), Direction.WEST, 2) } } self.assertEqual(self.planet.get_paths(), map_test) pass
class TestRoboLabPlanet(unittest.TestCase): def setUp(self): """ Instantiates the planet data structure and fills it with paths MODEL YOUR TEST PLANET HERE (if you'd like): """ """ planet planet: (0, 2)----4----(2, 2) | | 1 2 | | (0, 0)----2----(2, 0) """ self.planet = Planet() self.planet.add_path(((0, 0), Direction.EAST), ((2, 0), Direction.WEST), 2) self.planet.add_path(((0, 0), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.planet.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 4) self.planet.add_path(((2, 2), Direction.SOUTH), ((2, 0), Direction.NORTH), 2) """ planet same: +--1--+ (0, 2)---4---(2, 2) | | | | | | 1 2 +---(3, 2) | | (0, 0)---1---(2, 0) | | | | +-----1------+ """ self.same = Planet() self.same.add_path(((0, 0), Direction.EAST), ((2, 0), Direction.WEST), 1) self.same.add_path(((0, 0), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.same.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 4) self.same.add_path(((2, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 2) self.same.add_path(((3, 2), Direction.WEST), ((3, 2), Direction.NORTH), 1) self.same.add_path(((0, 0), Direction.SOUTH), ((2, 0), Direction.SOUTH), 1) """ planet blocked: X = blocked edge +----(0, 2)------3-------(2, 2)----+ | | | | | | +----+ | | | | | | | +--X--+ | 1 | | 1 | X-----(1, 1)---X | | | | | +----(0, 0)-----X +---(2, 0)--+ | | +---------2----------+ """ self.blocked = Planet() self.blocked.add_path(((0, 0), Direction.EAST), ((1, 1), Direction.SOUTH), -1) self.blocked.add_path(((0, 0), Direction.SOUTH), ((2, 0), Direction.SOUTH), 2) self.blocked.add_path(((0, 0), Direction.WEST), ((0, 2), Direction.WEST), 1) self.blocked.add_path(((0, 2), Direction.SOUTH), ((1, 1), Direction.WEST), -1) self.blocked.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 3) self.blocked.add_path(((2, 0), Direction.WEST), ((1, 1), Direction.EAST), -1) self.blocked.add_path(((2, 0), Direction.EAST), ((2, 2), Direction.EAST), 1) self.blocked.add_path(((2, 2), Direction.SOUTH), ((1, 1), Direction.NORTH), -1) """ planet big: X = blocked edge +-1-+ +--1-+ | | | | | | | | (-1, 3)-+ +--(3, 3)-------1---------+ | | +-------1--------+ 2 | | | | | +--(0, 2)-----4-----(2, 2)---+ (3, 2)---2----+ | | | | | | | | | | | | 1 | | 1 1 2 +-2-(3, 1)---3--(4, 1)-----+ | | | | | | | | +--(0, 0)-----1-----(2, 0)---X---(3, 0) 1 | | | +-------1--------+ | (4, -1) """ self.big = Planet() self.big.add_path(((0, 0), Direction.EAST), ((2, 0), Direction.WEST), 1) self.big.add_path(((0, 0), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.big.add_path(((0, 0), Direction.WEST), ((0, 2), Direction.WEST), 1) self.big.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 4) self.big.add_path(((0, 2), Direction.NORTH), ((2, 2), Direction.NORTH), 1) self.big.add_path(((2, 2), Direction.SOUTH), ((2, 0), Direction.NORTH), 2) self.big.add_path(((2, 2), Direction.EAST), ((3, 1), Direction.WEST), 2) self.big.add_path(((-1, 3), Direction.EAST), ((-1, 3), Direction.NORTH), 1) self.big.add_path(((0, 0), Direction.SOUTH), ((2, 0), Direction.SOUTH), 1) self.big.add_path(((2, 0), Direction.EAST), ((3, 0), Direction.WEST), -1) self.big.add_path(((3, 1), Direction.NORTH), ((3, 2), Direction.SOUTH), 1) self.big.add_path(((3, 1), Direction.EAST), ((4, 1), Direction.WEST), 3) self.big.add_path(((3, 2), Direction.EAST), ((4, 1), Direction.NORTH), 2) self.big.add_path(((3, 2), Direction.NORTH), ((3, 3), Direction.SOUTH), 2) self.big.add_path(((3, 3), Direction.EAST), ((4, 1), Direction.EAST), 1) self.big.add_path(((4, 1), Direction.SOUTH), ((4, -1), Direction.NORTH), 1) self.big.add_path(((3, 3), Direction.WEST), ((3, 3), Direction.NORTH), 1) """ planet one_path: X = blocked edge +---X----(3, 3)-----+ | | | | | | (0, 2)-----X-----(2, 2)---1---+ | | | | | | | X X | | | | | | | (0, 0)-----X-----(2, 0)--------4-------+ | | +--------1-------+ """ self.one_path = Planet() self.one_path.add_path(((0, 0), Direction.NORTH), ((0, 2), Direction.SOUTH), -1) self.one_path.add_path(((0, 0), Direction.EAST), ((2, 0), Direction.WEST), -1) self.one_path.add_path(((0, 0), Direction.SOUTH), ((2, 0), Direction.SOUTH), 1) self.one_path.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), -1) self.one_path.add_path(((2, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), -1) self.one_path.add_path(((2, 0), Direction.EAST), ((3, 3), Direction.EAST), 4) self.one_path.add_path(((2, 2), Direction.NORTH), ((3, 3), Direction.WEST), -1) self.one_path.add_path(((2, 2), Direction.EAST), ((3, 3), Direction.SOUTH), 1) """ planet loops: +--1--+ +---1--+ | | | | | | | | +---(0, 2)----1-----(2, 2)---+ | | | | 1 1 | | | | +---(0, 0)----1-----(2, 0)--+ | | | | | | | | +--1--+ +--1--+ """ self.loops = Planet() self.loops.add_path(((0, 0), Direction.WEST), ((0, 0), Direction.SOUTH), 1) self.loops.add_path(((0, 0), Direction.NORTH), ((0, 2), Direction.SOUTH), 1) self.loops.add_path(((0, 0), Direction.EAST), ((2, 0), Direction.WEST), 1) self.loops.add_path(((0, 2), Direction.WEST), ((0, 2), Direction.NORTH), 1) self.loops.add_path(((0, 2), Direction.EAST), ((2, 2), Direction.WEST), 1) self.loops.add_path(((2, 0), Direction.SOUTH), ((2, 0), Direction.EAST), 1) self.loops.add_path(((2, 0), Direction.NORTH), ((2, 2), Direction.SOUTH), 1) self.loops.add_path(((2, 2), Direction.NORTH), ((2, 2), Direction.EAST), 1) self.empty = Planet() def test_integrity(self): """ This test should check that the dictionary returned by "planet.get_paths()" matches the expected structure """ b = self.planet.get_paths() self.assertEqual({(0, 0): {Direction.EAST: ((2, 0), Direction.WEST, 2), Direction.NORTH: ((0, 2), Direction.SOUTH, 1)}, (2, 0): {Direction.WEST: ((0, 0), Direction.EAST, 2), Direction.NORTH: ((2, 2), Direction.SOUTH, 2)}, (0, 2): {Direction.SOUTH: ((0, 0), Direction.NORTH, 1), Direction.EAST: ((2, 2), Direction.WEST, 4)}, (2, 2): {Direction.WEST: ((0, 2), Direction.EAST, 4), Direction.SOUTH: ((2, 0), Direction.NORTH, 2)}}, b) def test_empty_planet(self): """ This test should check that an empty planet really is empty """ empty_planet = self.empty.planet_dict self.assertFalse(empty_planet) def test_target(self): """ This test should check that the shortest-path algorithm implemented works. Requirement: Minimum distance is three nodes (two paths in list returned) """ path_1 = self.planet.shortest_path((0, 0), (2, 2)) path_2 = self.big.shortest_path((3, 2), (2, 0)) path_3 = self.blocked.shortest_path((2, 0), (0, 2)) self.assertEqual([((0, 0), Direction.EAST), ((2, 0), Direction.NORTH)], path_1) self.assertEqual([((3, 2), Direction.SOUTH), ((3, 1), Direction.WEST), ((2, 2), Direction.SOUTH)], path_2) self.assertEqual([((2, 0), Direction.SOUTH), ((0, 0), Direction.WEST)], path_3) def test_target_not_reachable(self): """ This test should check that a target outside the map or at an unexplored node is not reachable """ # (3, 3) is not in the map and is therefore currently unexplored/unreachable. path_1 = self.planet.shortest_path((0, 0), (3, 3)) # (-1, -1) is not in the map and is therefore currently unexplored/unreachable. path_2 = self.planet.shortest_path((0, 0), (-1, -1)) self.assertIsNone(path_1) self.assertIsNone(path_2) def test_same_length(self): """ This test should check that the shortest-path algorithm implemented also can return alternative routes with the same cost (weight) to a specific target Requirement: Minimum of two paths with same cost exists, only one is returned by the logic implemented """ shortest_same_1 = self.same.shortest_path((0, 0), (2, 2)) my_list_of_paths_1 = [] my_list_of_paths_1.append(shortest_same_1) shortest_same_2 = self.big.shortest_path((0, 0), (4, -1)) my_list_of_paths_2 = [] my_list_of_paths_2.append(shortest_same_2) shortest_same_3 = self.loops.shortest_path((0, 0), (2, 2)) my_list_of_paths_3 = [] my_list_of_paths_3.append(shortest_same_3) self.assertEqual(len(shortest_same_1), 2) self.assertEqual(len(my_list_of_paths_1), 1) self.assertEqual([((0, 0), Direction.EAST), ((2, 0), Direction.NORTH)], shortest_same_1) self.assertEqual(len(shortest_same_2), 5) self.assertEqual(len(my_list_of_paths_2), 1) self.assertEqual([((0, 0), Direction.NORTH), ((0, 2), Direction.NORTH), ((2, 2), Direction.EAST), ((3, 1), Direction.EAST), ((4, 1), Direction.SOUTH)], shortest_same_2) self.assertEqual(len(shortest_same_3), 2) self.assertEqual(len(my_list_of_paths_3), 1) self.assertEqual([((0, 0), Direction.EAST), ((2, 0), Direction.NORTH)], shortest_same_3) def test_target_with_loop(self): """ This test should check that the shortest-path algorithm does not get stuck in a loop between two points while searching for a target nearby Result: Target is reachable """ shortest_1 = self.same.shortest_path((0, 0), (2, 2)) shortest_2 = self.big.shortest_path((0, 0), (4, -1)) shortest_3 = self.loops.shortest_path((0, 0), (2, 2)) self.assertIsNotNone(shortest_1) self.assertIsNotNone(shortest_2) self.assertIsNotNone(shortest_3) def test_target_not_reachable_with_loop(self): """ This test should check that the shortest-path algorithm does not get stuck in a loop between two points while searching for a target not reachable nearby Result: Target is not reachable """ # (-1, 3) is located in the map but cannot be reached. path_1 = self.big.shortest_path((0, 0), (-1, 3)) # (3, 2) is located in the map but cannot be reached. path_2 = self.same.shortest_path((0, 0), (3, 2)) self.assertIsNone(path_1) self.assertIsNone(path_2) def test_target_reached(self): """ This test checks that the shortest-path algorithm recognizes when the robot has reached the target. Result: Target reached """ path = self.blocked.shortest_path((0, 0), (0, 0)) self.assertFalse(path) def test_target_blocked(self): """ This test checks that the shortest-path algorithm will not return a shortest path or get caught in a loop when the target is only connected to blocked edges. Result: Target is unreachable """ path_1 = self.big.shortest_path((0, 0), (3, 0)) path_2 = self.blocked.shortest_path((0, 0), (1, 1)) self.assertIsNone(path_1) self.assertIsNone(path_2) def test_start_blocked(self): """ This test checks that the shortest-path algorithm will not return a shortest path or get caught in a loop when the start node is only connected to blocked edges. Result: Target is unreachable """ path_1 = self.big.shortest_path((3, 0), (0, 0)) path_2 = self.blocked.shortest_path((1, 1), (0, 0)) self.assertIsNone(path_1) self.assertIsNone(path_2) def avoid_blocked(self): """ This test checks that the shortest-path algorithm avoids blocked edges when searching for a shortest path. Result: Only one path without blocked edges exists, and that is the path that is returned. """ path = self.one_path.shortest_path((0, 0), (2, 2)) self.assertEqual([((0, 0), Direction.SOUTH), ((2, 0), Direction.EAST), ((3, 3), Direction.SOUTH)], path)
class YourFirstTestPlanet(unittest.TestCase): def setUp(self): """ Instantiates the planet data structure and fills it with paths MODEL YOUR TEST PLANET HERE (if you'd like): """ # set your data structure self.planet = Planet() # ADD YOUR PATHS HERE: self.planet.add_path(((0, 0), Direction.NORTH), ((0, 1), Direction.SOUTH)) self.planet.add_path(((0, 0), Direction.EAST), ((0, 1), Direction.EAST)) self.planet.add_path(((0, 1), Direction.NORTH), ((1, 2), Direction.WEST)) self.planet.add_path(((1, 2), Direction.SOUTH), ((1, 1), Direction.NORTH)) self.planet.add_path(((1, 1), Direction.EAST), ((2, 1), Direction.WEST)) self.planet.add_path(((1, 1), Direction.SOUTH), ((2, 0), Direction.WEST)) self.planet.add_path(((2, 1), Direction.NORTH), ((2, 2), Direction.SOUTH)) self.planet.add_path(((2, 2), Direction.NORTH), ((2, 2), Direction.WEST)) self.planet.add_path(((2, 0), Direction.NORTH), ((2, 1), Direction.SOUTH)) self.planet.add_path(((2, 0), Direction.EAST), ((4, 0), Direction.WEST)) self.planet.add_path(((4, 0), Direction.NORTH), ((4, 1), Direction.SOUTH)) self.planet.add_path(((4, 0), Direction.EAST), ((4, 1), Direction.EAST)) # todo: implement hulk as tesplanet for testing stuff. def test_integrity(self): # were all paths added correctly to the planet # check if add_path() works by using get_paths() self.fail('implement me!') def test_empty_planet(self): self.fail('implement me!') def test_target_not_reachable(self): self.fail('implement me!') def test_shortest_path(self): # at least 2 possible paths # self.assertEqual() # self.failUnlessEqual self.fail('implement me!') def test_same_length(self): # at least 2 possible paths with the same weight self.fail('implement me!') def test_shortest_path_with_loop(self): # does the shortest path algorithm loop infinitely? # there is a shortest path self.fail('implement me!') def test_target_not_reachable_with_loop(self): # there is no shortest path self.fail('implement me!')
class TestHulk(unittest.TestCase): def setUp(self): """ Instantiates the planet data structure and fills it with paths MODEL YOUR TEST PLANET HERE (if you'd like): """ # set your data structure self.planet = Planet() self.planet.add_path(((13, 37), Direction.NORTH), ((13, 38), Direction.SOUTH), 1) self.planet.add_path(((13, 38), Direction.NORTH), ((14, 39), Direction.WEST), 1) self.planet.add_path(((14, 39), Direction.SOUTH), ((14, 38), Direction.NORTH), 1) self.planet.add_path(((14, 38), Direction.SOUTH), ((15, 37), Direction.WEST), 1) self.planet.add_path(((15, 37), Direction.NORTH), ((15, 38), Direction.SOUTH), 1) self.planet.add_path(((15, 38), Direction.WEST), ((14, 38), Direction.EAST), 1) self.planet.add_path(((17, 37), Direction.SOUTH), ((17, 37), Direction.EAST), -1) print(self.planet.shortest_path((14, 38), (17, 37))) def test_shortest_path(self): print()