Skip to content

Instantly share code, notes, and snippets.

@sthware
Last active June 3, 2017 01:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sthware/8700b47c9d66473aade4fbd70a76d4f0 to your computer and use it in GitHub Desktop.
Save sthware/8700b47c9d66473aade4fbd70a76d4f0 to your computer and use it in GitHub Desktop.
Food Delivery Bot - simple delivery routing system demo
#!/usr/bin/env python3
"""Foodbot test program
Simple food delivery bot. Accepts a grid size and list of coordinates
representing dropoff locations (represented by the string 'D')
Plots a route to each of those locations, displaying the steps it used to
get there in NEWS format.
"""
import argparse, re, copy, sys, pprint
__author__ = "Spencer Hoffman"
__version__ = "1.0.0"
__email__ = "spencer.hoffman@gmail.com"
__status__ = "One-off"
class DeliverySet:
def __init__(self, width: int, height: int, coordinates: list, DEBUG: bool):
""" Sets up delivery grid and coordinate list.
width -- grid width, positive integer
height -- grid height, positive integer
coordinates -- LoL of delivery coordinates in [x, y] format
DEBUG -- boolean flag for additional delivery information
"""
self.width = width
self.height = height
self.grid = [x[:] for x in [[0] * width] * height]
self.DEBUG = DEBUG
self.coordinates = coordinates
# Set up location grid with drop locations for printing.
for coordinate in self.coordinates:
x = coordinate[0]
y = coordinate[1]
# Number of deliveries at a particular location.
self.grid[x][y] += 1
# Always have the origin in our coordinate list at least once.
if (0, 0) not in self.coordinates:
self.coordinates.insert(0, (0,0))
def print_matrix(self):
""" Pretty prints the raw matrix """
pprint.pprint(self.grid)
def show_route(self):
""" Display route to each delivery location in NEWS format. Multiple
deliveries at the same location will show multiple 'D' strings.
"""
prev_x = None
prev_y = None
drop_complete = False
end_char=''
for coordinate in self.coordinates:
x = coordinate[0]
y = coordinate[1]
x_range = []
y_range = []
x_steps = None
y_steps = None
if prev_x is None and prev_y is None:
prev_x = 0
prev_y = 0
continue
# Some locations have multiple deliveries, since we do them
# all at once, we can stop processing if we see the same coordinate
# again.
if self.grid[x][y] == 0:
continue
x_steps = x - prev_x
y_steps = y - prev_y
if self.DEBUG:
print(' ', 'Coordinates, steps: ', x, ',', y, end=' ', sep='')
if x_steps > 0:
direction = 'E'
x_range = range(x_steps)
elif x_steps < 0:
direction = 'W'
x_range = range(x, x + x_steps, -1)
for x_step in x_range:
print(direction, end=end_char)
if y_steps > 0:
direction = 'N'
y_range = range(y_steps)
elif y_steps < 0:
direction = 'S'
y_range = range(y, y + y_steps, -1)
for y_step in y_range:
print(direction, end=end_char)
# Routes are pre-ordered, so if a delivery location(s) is at
# the origin, it will print first. Otherwise it will
# print after taking the appropriate steps to that location.
# Also note that multiple deliveries at the same location
# will display the delivery flag repeatedly: once for each
# delivery.
while self.grid[x][y] > 0:
print('D', end=end_char)
self.grid[x][y] -= 1
if self.DEBUG:
print()
# Now record the current coordinates as the previous ones so
# we can properly route to the next delivery location.
prev_x = x
prev_y = y
print()
def show_grid(self):
""" Prints the grid to match Cartesian coordinate plane orientation """
output_grid = copy.deepcopy(self.grid)
# Typically we'd use a library for matix operations, but
# we're sticking to the standard library since this is a one-off.
# Left rotate the matrix.
for layer in range(self.width // 2):
first = layer
last = self.width - 1 - layer
for index in range(first, last):
upper_left_val = output_grid[first][index]
last_diff = self.width - 1 - index
output_grid[first][index] = output_grid[index][last]
output_grid[index][last] = output_grid[last][last_diff]
output_grid[last][last_diff] = output_grid[last_diff][first]
output_grid[last_diff][first] = upper_left_val
print(re.sub("[\[\]',]", " ", pprint.pformat(output_grid)))
def get_input():
""" Parses command line arguments, and performs validation of matrix size
and coordinates
"""
arg_parser = argparse.ArgumentParser(description="Foodbot")
width = 0
height = 0
raw_coordinates = []
raw_coordinates_str = ''
coordinates = []
grid_size_str = ''
arg_parser.add_argument(
dest='params',
metavar='parameters',
action='store',
help='Grid size and moves to make. String. Ex: "4x4 (0,0) (1,1)"'
)
arg_parser.add_argument(
'--debug',
dest='DEBUG',
action='store_true',
help='print some helpful debug messages'
)
args = arg_parser.parse_args()
(grid_size_str, *raw_coordinates) = args.params.split(' ', 1)
if not raw_coordinates:
print("error: coordinate list must be supplied\n")
raise ValueError
# Remove spaces and starting and ending parens, as they aren't
# needed anymore.
raw_coordinates_str = raw_coordinates[0].replace(' ', '')
if not re.match('^ (?: \(\d+,\d+\) )+ $', raw_coordinates_str, re.X):
print("error: coordinates in wrong format. Use '(0, 0), (1, 1)'\n")
raise ValueError
raw_coordinates_str = raw_coordinates_str[1:]
raw_coordinates_str = raw_coordinates_str[:-1]
try:
(width, height) = map(int, grid_size_str.split('x'))
except ValueError:
print("error: width and height must be integers\n")
raise
if width != height:
print("error: only square matrices are supported\n")
raise ValueError
try:
for raw_coordinate in raw_coordinates_str.split(')('):
(x, y) = map(int, raw_coordinate.split(','))
coordinates.append([x, y])
except ValueError:
print("error: coordinates must all be integers\n")
raise
# Points are ordered for a straightforward, though not necessarily the
# fastest, route.
coordinates.sort()
if coordinates[-1][0] > (width - 1) or coordinates[-1][1] > (height - 1):
print("error: coordinate ({}, {}) out of range for grid size\n".format(
coordinates[-1][0],
coordinates[-1][1]
))
raise ValueError
return (width, height, coordinates, args.DEBUG)
if __name__ == '__main__':
(width, height, coordinates, DEBUG) = get_input()
deliveries = DeliverySet(width, height, coordinates, DEBUG)
if DEBUG:
print("Matrix: ")
deliveries.print_matrix()
print()
print("Delivery grid: ")
deliveries.show_grid()
print()
deliveries.show_route()
sys.exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment