Last active
June 3, 2016 20:28
-
-
Save reeddunkle/618b2f67c39c71b8f52500ad5345b6f5 to your computer and use it in GitHub Desktop.
Obstacles not being drawn in the correct squares
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| 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