Skip to content

Instantly share code, notes, and snippets.

@reeddunkle
Last active June 3, 2016 20:28
Show Gist options
  • Select an option

  • Save reeddunkle/618b2f67c39c71b8f52500ad5345b6f5 to your computer and use it in GitHub Desktop.

Select an option

Save reeddunkle/618b2f67c39c71b8f52500ad5345b6f5 to your computer and use it in GitHub Desktop.
Obstacles not being drawn in the correct squares
"""
backtrack2.py
-------------
Converting backtrack to OOP:
"""
"""
Notes: June 3
Obstacles seem to be being drawn in the wrong squares
(but being registered correctly for finding a path)
"""
from random import randint
DOWN = "Down"
RIGHT = "Right"
DESTINATION = "Destination!"
EMPTY_CHAR = "[ ]"
OBSTACLE_CHAR = "[X]"
HOME_CHAR = "[+]"
DOWN_CHAR = "[|]" # From up going down
RIGHT_CHAR = "[-]" # From left going right
DESTINATION_CHAR = "[*]"
DOWN_RIGHT_CHAR = "[{}]".format(chr(8735))
RIGHT_DOWN_CHAR = "[{}]".format(chr(119317))
# class UserInputValidator(object):
def possible_directions(row, col, obstacles):
"""Returns the moves that are legal from the given coordinates."""
dirs = []
obs_coordinates = [(square.row, square.col) for square in obstacles]
# In case the user sets the robot's destination as (0,0),
# this adds their destination to the path.
if row == 0 and col == 0:
dirs.append((0, 0))
if col > 0 and (row, col-1) not in obs_coordinates:
dirs.append((row, col-1))
if row > 0 and (row-1, col) not in obs_coordinates:
dirs.append((row-1, col))
return dirs
class Board(object):
def __init__(self):
"""Calls _get_user_input()."""
self._get_user_input()
# self.rows and self.cols are set by _get_user_input()
self.squares = [[Square(r, c) for c in range(self.cols)] for r in range(self.rows)]
self._place_obstacles()
def _get_user_input(self):
"""Prompts the user for stuff!"""
self.dest_r, self.dest_c = get_destination()
self.rows = self.dest_r + 1 # add 1 to get size from coordinates
self.cols = self.dest_c + 1
# We subtract 2 because src and trg are not allowed to have obstacles.
self.max_obstacles = self.rows * self.cols - 2
self.num_obstacles = get_num_obstacles(self.max_obstacles)
def _place_obstacles(self):
"""Places obstacles randomly."""
self.obstacles = []
current_obstacles = 0
while (current_obstacles < self.num_obstacles):
r = randint(0, self.dest_r)
c = randint(0, self.dest_c)
# Avoid placing obstacles in src and trg
if (r == 0 and c == 0) or (r == self.dest_r and c == self.dest_c):
continue
elif self.squares[r][c].obstacle:
continue
else:
# Print for debugging
print((r,c))
self.squares[r][c].obstacle = True
self.obstacles.append(self.squares[r][c])
current_obstacles += 1
def find_path(self):
"""Finds path from (0,0) to (self.dest_r, self.dest_c)"""
return self._find_path(self.dest_r, self.dest_c)
def _find_path(self, cur_row, cur_col):
"""The backtrack algorithm -- returns the robot's path."""
# This ensures that the function returns the complete path even in this base case
if (self.dest_r == 0 and self.dest_c == 1) or (self.dest_r == 1 and self.dest_c == 0):
return [(0, 0), (self.dest_r, self.dest_c)]
else:
for coordinance in possible_directions(cur_row, cur_col, self.obstacles):
r, c = coordinance
if r == 0 and c == 0:
return [(0, 0)]
solution = self._find_path(r, c)
if solution:
final = solution + [(r, c)]
if cur_row == self.dest_r and cur_col == self.dest_c:
final += [(cur_row, cur_col)]
return final
def _coords_to_english(self, path):
"""Writes the English directions of the robot's path."""
path_english = []
for i in range(0, -1 + len(path)):
r, c = path[i]
next_r, next_c = path[i+1]
if c < next_c:
path_english.append(RIGHT)
elif r < next_r:
path_english.append(DOWN)
path_english.append(DESTINATION)
return path_english
def assign_path(self, path):
"""
Tells the Squares with coordinates in <path> that there's a path going
through them.
"""
# Edge case where destination is the same as starting pos
if self.dest_r == 0 and self.dest_c == 0:
self.squares[0][0].face = HOME_CHAR
# Print for debugging
print(path)
if path:
if path[1] == (0, 1):
self.squares[0][0].face = RIGHT_CHAR
else:
self.squares[0][0].face = DOWN_CHAR
path_english = self._coords_to_english(path)
for i in range(1, len(path) - 1):
prev_move = path_english[i-1]
next_move = path_english[i+1]
cur_move = path_english[i]
r, c = path[i]
if prev_move == RIGHT and cur_move == DOWN:
move = RIGHT_DOWN_CHAR
elif prev_move == DOWN and cur_move == RIGHT:
move = DOWN_RIGHT_CHAR
elif cur_move == RIGHT:
move = RIGHT_CHAR
elif cur_move == DOWN:
move = DOWN_CHAR
self.squares[r][c].face = move
self.squares[self.dest_r][self.dest_c] = DESTINATION_CHAR
def display(self):
"""Prints self.__str__()."""
print(self.__str__())
def __str__(self):
"""Returns a string representation of the board."""
board_string = ""
for square_row in self.squares:
row_string = "".join([s.__str__() for s in square_row])
board_string += row_string + "\n"
return board_string
class Square(object):
def __init__(self, row, col):
self.obstacle = False
self.row = row
self.col = col
self._face = ""
def __str__(self):
"""String representation of a Square."""
return self.face
@property
def face(self):
"""A property to compute the face value based on self.obstacle."""
if self._face == "":
if self.obstacle:
self._face = OBSTACLE_CHAR
else:
self._face = EMPTY_CHAR
return self._face
@face.setter
def face(self, new_face):
if not self.obstacle:
self._face = new_face
else:
raise ValueError("A path may not go through an obstacle.")
def print_introduction():
"""Prints the game's introduction messages."""
print("-" * 15)
print("Welcome to the Robot Game!")
print("Your robot starts in the upper left corner")
print("of the game board, at coordinates (0,0)\n")
print("She can only move to the RIGHT and DOWN.\n")
def validate_user_input(prompt, error_msg, test):
"""
Keeps prompting the user for a valid input. <test> is a function that returns
a Boolean value, depending on whether or not the input is valid.
"""
user_input = input(prompt)
while not test(user_input):
if not test(user_input):
print(error_msg)
user_input = input(prompt)
return user_input
def get_destination():
"""Gets coordinates from the user for the robot's destination."""
prompt = "What are the coordinates of your robot's destination?\nExample: 2,2\n> "
error_msg = "Sorry. You must enter the destination in the form Row,Column."
test = lambda s: len([char for char in s if char.isdigit()]) > 1
user_input = validate_user_input(prompt, error_msg, test)
r, c = [int(char) for char in user_input if char.isdigit()][:2]
return (r, c)
def get_num_obstacles(max_obstacles):
"""Get number of obstacles from the user."""
prompt = "Please enter a number of random obstacles:\n> "
error_msg = "Sorry, for a board of this size\nyou can only have {} obstacles.".format(max_obstacles)
num_obstacles = validate_user_input(prompt, error_msg, lambda s: s.isdigit() and int(s) < max_obstacles)
num_obstacles = int(num_obstacles)
return num_obstacles
if __name__ == '__main__':
### setup
print_introduction()
board = Board() # interacts with the user
### compute
board.display()
path = board.find_path()
# Print for debugging
print(path)
board.assign_path(path)
### output
board.display()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment