2017-03-05 23:03:21 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
from enum import IntEnum, unique
|
|
|
|
from typing import List, Optional, Tuple, Dict
|
|
|
|
# IMPORTANT NOTE: DO NOT IMPORT THE ev3dev.ev3 MODULE IN THIS FILE
|
|
|
|
|
|
|
|
|
|
|
|
@unique
|
|
|
|
class Direction(IntEnum):
|
|
|
|
""" Directions in degrees """
|
|
|
|
NORTH = 0
|
|
|
|
EAST = 90
|
|
|
|
SOUTH = 180
|
|
|
|
WEST = 270
|
|
|
|
|
|
|
|
|
|
|
|
# simple alias, no magic here
|
|
|
|
Weight = int
|
2018-03-19 02:46:25 +01:00
|
|
|
"""
|
2017-03-05 23:03:21 +01:00
|
|
|
Weight of a given path (received from the server)
|
2018-03-19 02:46:25 +01:00
|
|
|
value: -1 if broken path
|
2017-03-05 23:03:21 +01:00
|
|
|
>0 for all other paths
|
|
|
|
never 0
|
|
|
|
"""
|
|
|
|
|
2018-03-19 05:14:58 +01:00
|
|
|
'''
|
|
|
|
https://www.python.org/dev/peps/pep-0289/
|
2018-03-20 22:31:15 +01:00
|
|
|
Node checking examples:
|
2018-03-19 05:14:58 +01:00
|
|
|
((0,0),Direction.NORTH) in planetmap[0]
|
|
|
|
Simplify this for node search:
|
|
|
|
next(x for (x, y), direction in planetmap[0])
|
|
|
|
next(y for x, y in planetmap[0] if x == (0, 0))
|
2018-03-19 10:14:42 +01:00
|
|
|
planetmap[0][next((x, y) for x, y in planetmap[0] if y == 'North')]
|
2018-03-20 22:31:15 +01:00
|
|
|
|
|
|
|
formatting example of bidirectional map:
|
|
|
|
{
|
|
|
|
(0, 0): {
|
|
|
|
<Direction.EAST: 90>: ((0, 0), <Direction.WEST: 270>, 1),
|
|
|
|
<Direction.WEST: 270>: ((0, 0), <Direction.EAST: 90>, 1),
|
|
|
|
<Direction.NORTH: 0>: ((0, 1), <Direction.SOUTH: 180>, 2)
|
|
|
|
},
|
|
|
|
(0, 1): {
|
|
|
|
<Direction.SOUTH: 180>: ((0, 0), <Direction.NORTH: 0>, 2)
|
|
|
|
}
|
|
|
|
}
|
2018-03-19 05:14:58 +01:00
|
|
|
'''
|
2017-03-05 23:03:21 +01:00
|
|
|
|
2018-03-22 02:14:44 +01:00
|
|
|
# Contains the representation of the map and provides certain functions to manipulate it according to the specifications
|
2018-03-19 22:42:27 +01:00
|
|
|
class Planet:
|
2017-03-05 23:03:21 +01:00
|
|
|
def __init__(self):
|
|
|
|
""" Initializes the data structure """
|
2018-03-19 22:42:27 +01:00
|
|
|
self._planetmap = {}
|
2018-03-22 14:34:55 +01:00
|
|
|
self._unexedge = []
|
2018-03-22 02:14:44 +01:00
|
|
|
self._curnode = None
|
2018-03-24 14:32:48 +01:00
|
|
|
self._curdir = Direction.NORTH
|
2017-03-05 23:03:21 +01:00
|
|
|
self.target = None
|
|
|
|
|
2018-03-20 22:31:15 +01:00
|
|
|
# Adds a bidirectional path defined between the start and end coordinates to the map and assigns the weight to it.
|
2017-03-05 23:03:21 +01:00
|
|
|
def add_path(self, start: Tuple[Tuple[int, int], Direction], target: Tuple[Tuple[int, int], Direction], weight: int):
|
2018-03-19 22:42:27 +01:00
|
|
|
if(start[0] not in self._planetmap):
|
|
|
|
self._planetmap[start[0]] = {}
|
|
|
|
if(start[1] not in self._planetmap[start[0]]):
|
|
|
|
self._planetmap[start[0]][start[1]] = (target[0], target[1], weight)
|
|
|
|
if(target[0] not in self._planetmap):
|
|
|
|
self._planetmap[target[0]] = {}
|
|
|
|
if(target[1] not in self._planetmap[target[0]]):
|
|
|
|
self._planetmap[target[0]][target[1]] = (start[0], start[1], weight)
|
2017-03-05 23:03:21 +01:00
|
|
|
|
|
|
|
def get_paths(self) -> Dict[Tuple[int, int], Dict[Direction, Tuple[Tuple[int, int], Direction, Weight]]]:
|
2018-03-19 22:42:27 +01:00
|
|
|
return self._planetmap
|
2017-03-05 23:03:21 +01:00
|
|
|
|
2018-03-22 02:14:44 +01:00
|
|
|
def setcurnode(self, node):
|
|
|
|
self._curnode = node
|
|
|
|
|
|
|
|
def getcurnode(self):
|
|
|
|
return self._curnode
|
|
|
|
|
2018-03-24 14:32:48 +01:00
|
|
|
def getcurdir(self):
|
|
|
|
return self._curdir
|
|
|
|
|
2018-03-22 14:34:55 +01:00
|
|
|
def addtounexedge(self, node_edge_list):
|
|
|
|
self._unexedge = self._unexedge + node_edge_list
|
|
|
|
|
|
|
|
def exploreedge(self, node_edge):
|
|
|
|
self._unexedge.remove(node_edge)
|
|
|
|
|
2018-03-20 22:31:15 +01:00
|
|
|
'''
|
|
|
|
Returns a shortest path between two nodes.
|
2018-03-21 14:44:55 +01:00
|
|
|
Used Algorithm: Dijkstra's Algorithm
|
|
|
|
Have a look at heapqueue: https://docs.python.org/2/library/heapq.html
|
2018-03-20 22:31:15 +01:00
|
|
|
Formatting:
|
|
|
|
List = [(total_len, (current_node, [((node), Direction)]))]
|
|
|
|
'''
|
2017-03-05 23:03:21 +01:00
|
|
|
def shortest_path(self, start: Tuple[int, int], target: Tuple[int, int]) -> Optional[List[Tuple[Tuple[int, int], Direction]]]:
|
2018-03-20 22:31:15 +01:00
|
|
|
if((start in self._planetmap) and (target in self._planetmap)):
|
|
|
|
self.path_found = []
|
|
|
|
self.path_search = [(0, (start, []))]
|
|
|
|
while True:
|
|
|
|
for srcdir, (destnode, destdir, weight) in self._planetmap[self.path_search[0][1][0]].items():
|
|
|
|
if(destnode not in self.path_found and weight > 0): # check if in shortest_found
|
|
|
|
for x, (s_node, s_pathlist) in self.path_search:
|
|
|
|
if (destnode == s_node and self.path_search[0][0] + weight < x):
|
|
|
|
self.path_search.remove((x, (s_node, s_pathlist)))
|
|
|
|
if(s_weight for s_weight, (s_node, s_pathlist) in self.path_search if s_node == destnode):
|
|
|
|
self.path_search.append((self.path_search[0][0]+weight, (destnode, self.path_search[0][1][1] + [(self.path_search[0][1][0], srcdir)])))
|
|
|
|
self.path_found.append(self.path_search[0][1][0])
|
|
|
|
self.path_search.pop(0)
|
|
|
|
if(self.path_search == []): # if map is empty
|
2018-03-21 14:44:55 +01:00
|
|
|
print("Destination is not reachable from start.")
|
2018-03-20 22:31:15 +01:00
|
|
|
break
|
|
|
|
self.path_search.sort()
|
|
|
|
if(self.path_search[0][1][0] == target):
|
|
|
|
return self.path_search[0][1][1]
|
|
|
|
else:
|
|
|
|
print("Cannot calculate shortest Path. Start or target is not in known.")
|