From 3a711e10b16e83040cb957d8813831c269fad8b6 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 26 Aug 2013 20:05:37 +0000 Subject: Add input validation and error handling to the game Now it’s finished! --- diff --git a/maze.py b/maze.py index ed833ac..600d690 100644 --- a/maze.py +++ b/maze.py @@ -17,22 +17,23 @@ node and (size, size) is the finish node. This is a standard way to represent a graph in a program. -Finally, the MazeGame class needs to be implemented to tie together both Maze -and Player. This class needs to implement the user interface, repeatedly asking -the player which move they'll make, then updating the Player's location -accordingly. - -Implementing this requires a method to be added to Maze to retrieve a list of -the nodes which the player can legitimately move to from their current -location. To avoid breaking compartmentalisation and encoding the player's -behaviour in the Maze class, the method takes a node as a parameter rather than -accessing the player's location directly. - -One language feature to note is the use of "while...else" in MazeGame.run(). -The code in the 'else' block will be executed immediately after the final -iteration of the 'while' loop, but won't be executed if execution leaves the -loop by executing the 'break' statement. This means the "you've won" message -won't be printed if the user quits the game prematurely. +In order to be a stable and robust program, the game must cope with unexpected +or incorrect user input. What happens if the user enters a string instead of +an integer when asked for the maze size, or for their next location? What +happens if they press Ctrl+C (a termination signal on Linux) while the program +is waiting for input? Try doing these things with the previous version of the +program and see what exceptions are raised. In order to make the program more +robust, these exceptions need to be explicitly handled. + +The handling code for them has been added below. Note that the maze size is +validated twice: once when the user inputs the size, checking that it's a +valid integer and is between the limits set to make the game fun; and once when +the Maze object is instantiated, checking that the size doesn't violate any of +the algorithmic constraints in the Maze implementation. To do all the checking +on input would mean the algorithmic checks would need to be reimplemented if +the Maze class was re-used in another program. To do all the checking in the +Maze class would tie it too closely to this particular game by rejecting all +maze sizes which don't fit in with this particular game. """ import random @@ -76,6 +77,10 @@ class Maze(object): The grid must be of size at least 1. """ def __init__(self, size): + # Validate the size. + if size < 1: + raise AttributeError('size must be at least 1') + self._links = {} self._finish_node = (size, size) self.__generate_maze(size) @@ -194,8 +199,32 @@ class MazeGame(object): handles the player's movement choices. """ def __init__(self): - name = raw_input('Please enter your name: ') - size = int(raw_input('Enter the maze size (between 5 and 20): ')) + self._player = None + self._maze = None + + # Get the player's name. + while True: + try: + name = raw_input('Please enter your name: ') + if name != '': + break + except KeyboardInterrupt: + # User pressed Ctrl+C; continue until we hit run(). + print('') + return None + + # Get the board size. + while True: + try: + size = int(raw_input('Enter the maze size (1 to 20): ')) + if 1 <= size <= 20: + break + except ValueError: + continue # user entered an invalid integer + except KeyboardInterrupt: + # User pressed Ctrl+C; continue until we hit run(). + print('') + return None self._player = Player(name) self._maze = Maze(size) @@ -222,14 +251,26 @@ class MazeGame(object): print(' 0) Quit game') # Ask the user which option they want. - chosen_option = int(raw_input('Enter the number of your choice: ')) - if chosen_option == 0: - return None # quit the game - else: - return adjacent_nodes[chosen_option - 1] + while True: + try: + chosen_option = \ + int(raw_input('Enter the number of your choice: ')) + if chosen_option == 0: + return None # quit the game + elif 0 <= chosen_option - 1 < len(adjacent_nodes): + return adjacent_nodes[chosen_option - 1] + except ValueError: + continue # non-number entered + except KeyboardInterrupt: + print('') + return None # player pressed Ctrl+C, so quit def run(self): """Run the game until the player wins or quits.""" + # Check that initialisation was successful; if not, quit immediately. + if self._player is None or self._maze is None: + return # quit + while self._player.location != self._maze.finish_node: chosen_node = self.__choose_next_location() if chosen_node is None: -- cgit v0.9.1