Lecture 11 - Exceptions


As usual, create a directory to hold today's activities:

$ mkdir ~/cs170/labs/lab11
$ cd ~/cs170/labs/lab11

Fixing Exceptions

So you saw how to create and catch exceptions. However, you don't really understand why you would use them. Let's talk briefly about that, before we get into the test review.


Lab Activity 1
Test Review

In addition to the written portion of the test, there will be a coding portion. This will be done on the computers. For practice, attempt the following exercises, alone:

  1. Write a simple tkinter program that draws a filled in rectangle to with a length of 400 and a height of 300 to the center of the window, and writes your name inside that rectangle.
  2. Create a class called Point_3D, which represents a point in 3 dimensional space. This class should have methods for adding points together (which is just the sum of each component). This method should return a new Point_3D, instead of modifying one of the points. You should also include a method that can be used when printing the point.
  3. Create a class called Bin, which stores a list of all of the elements currently inside of the bin. Create another class called RecycleBin, which inherits the Bin class. Your RecycleBin class should have at least a method called empty, which removes everything from the bin.

Lab Assignment 11
Battleship

We didn't quite get through this one last time, so let's finish it up today!

Battleship is a classic board game that consists of two 2-dimensional grids containing some number of battleships. Players take turns firing "missiles" at locations in their opponents 2-dimensional grid. This game has a reputation for being a very easy game to cheat at.

Let's see if we can use Exceptions to help make it a little harder to cheat at the game. You are going to make a very simple, single player version of battleship. You will read in a battleship specification from a file, and allow a player to take turns choosing grid locations to destroy. Your program should handle invalid files and invalid player input gracefully.

Details

Create a file called battleship.py to house your battleship program. Your program should have at least two functions: read_configuration_file(file_name) and process_user_input(game_board).

read_configuration_file(file_name) should read a battleship configuration file specified in the parameter. This file will be of the following format:

1000000000
1000000000
0011110000
0000000000
0111000010
0000000010
0000000010
0000000010
0011100010
0000000000
Which represents this battleship board.

Notice that a valid configuration is a 10 × 10 board, with 17 1's on the board. Any other board is invalid. You should define a InvalidBattleshipBoard exception, and your read function should raise this exception if the board is invalid.

Once you have read a valid board, you should call process_user_input(game_board). This should read input from the user via the command line. The user should be able to specify coordinates like a traditional battleship game: Rows specified with letters from A-J, and columns specified with an integer in the range [1-10]. If the user doesn't specify a valid coordinate, you should raise an InvalidCoordinate exception, which of course you should write.

If the users input is valid, you should process it as normal. Marks hits with x's and misses with o's.

Example

The following is a sample output without exception handling. You should handle the exceptions you see below gracefully.

$ cat default.in
1000000000
1000000000
0011110000
0000000000
0111000010
0000000010
0000000010
0000000010
0011100010
0000000000
$ cat error.in
1111111111
1111111110
1111111100
1111111000
1111110000
1111100000
1111000000
1110000000
1100000000
1000000000
$ python3
>>> import battleship
>>> board = battleship.read_configuration_file("default.in")
>>> battleship.process_user_input(board)
Current Board:
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
Your move: A1
HIT!
x000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
Your move: A2
MISS
xo00000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
Your move: Z11
...snip...
During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "battleship.py", line 77, in 
    process_user_input(board)
  File "battleship.py", line 70, in process_user_input
    " is not a valid coordinate!")
__main__.InvalidCoordinate: Z11 is not a valid coordinate!
>>> board = battleship.read_configuration_file("error.in")
Traceback (most recent call last):
  File "battleship.py", line 76, in 
    board = read_configuration_file(fname)
  File "battleship.py", line 41, in read_configuration_file
    raise InvalidBattleshipBoard("Invalid board size, or invalid number of pieces")
__main__.InvalidBattleshipBoard: Invalid board size, or invalid number of pieces
>>> quit()

Hint

  • You can open a file in python using the open function. This function takes two parameters: a string that is the name of the file you want to open, and a string representing your permissions. For this, you only need 'r' (read) permissions.

    Don't forget to close your files when you are done!

  • Your exception classes are going to be very similar. As a matter of fact, if you are sneaky you can define the classes with very little code.

  • You are going to want to catch various exceptions when reading files, so that you can raise your InvalidBattleshipBoard exception. You also need to keep a running sum of the number of 1's on the board. If it does not equal 17 after reading the entire board, you also have an InvalidBattleshipBoard.

  • Remember, you can use ord to convert from strings to ASCII values. You can actually decompose every ASCII character to an integer this way.

    Instead of having an if statement for handling invalid coordinates, you can simply try/except an IndexError when attempting to check the value of the game board. If an IndexError is raised, you just re-raise (by raising an exception in the exception handler) and InvalidCoordinate exception.

 

Challenge

An easy way to make the program more "idiot-proof" is to make it incredibly easy to interact with. Instead of relying on the user to type coordinates to input, simply have them click on locations they want to "fire" missiles at.

That's right, it's tkinter time! Create a grid that the user can click on, and allow them to click to reveal locations. Note that this does not mean you can completely get rid of exceptions. You should raise an InvalidMove exception if they click on a location that is already uncovered.

 

Challenge

It seems that raising an exception in the graphical program should have a different connotation than doing so via the command line. especially since it is a little difficult to notice that an exception was actually raised when using the graphical program.

Look at the documentation for tkMessageBox dialogs. You should create a showerror pop-up window upon raising the exception. You will need the following import statement in your code:

  from tkinter import messagebox


Submission

When you have finished, create a tar file of your lab11 directory. To create a tar file, execute the following commands:

cd ~/cs170/labs
tar czvf lab11.tgz lab11/

To submit your activity, go to cseval.roanoke.edu. You should see an available assignment called Lab Assignment 11. Make sure you include a header listing the authors of the file.


In-class Notes