In this exercise, you will use the concept of functions to organize much of the functionality we developed in EX02. Moreover, rather than only getting one-attempt at guessing the secret boat location, you will get five! To get a sense of where you are going, here are two examples of the final game:
$ python -m exercises.ex03_functional_battleship === Turn 1/5 === Guess a row: 2 Guess a column: 1 🟦🟦🟦🟦🟦 ⬜🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 Miss! === Turn 2/5 === Guess a row: 2 Guess a column: 4 🟦🟦🟦🟦🟦 🟦🟦🟦⬜🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 Miss! === Turn 3/5 === Guess a row: 2 Guess a column: 1 🟦🟦🟦🟦🟦 ⬜🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 Miss! === Turn 4/5 === Guess a row: 3 Guess a column: 9 The grid is only 5 by 5. Try again: 6 The grid is only 5 by 5. Try again: 4 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟥🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 Hit! You won in 4/5 turns! $ python -m exercises.ex03_functional_battleship === Turn 1/5 === Guess a row: 1 Guess a column: 1 ⬜🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 Miss! === Turn 2/5 === Guess a row: 2 Guess a column: 2 🟦🟦🟦🟦🟦 🟦⬜🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 Miss! === Turn 3/5 === Guess a row: 3 Guess a column: 3 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦⬜🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 Miss! === Turn 4/5 === Guess a row: 4 Guess a column: 4 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦⬜🟦 🟦🟦🟦🟦🟦 Miss! === Turn 5/5 === Guess a column: 6 The grid is only 5 by 5. Try again: 5 Guess a column: 0 The grid is only 5 by 5. Try again: 5 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦⬜ 🟦🟦🟦🟦🟦 Miss! X/5 - Better luck next time!
Permitted Constructs
We expect you to implement this exercise using only the concepts covered in COMP110. If you have prior programming experience, restrict your implementation to only the concepts covered. While there are many ways to implement this program with additional concepts beyond those we have covered, you should not attempt to do so until after submitting this exercise for full credit once the autograder is posted. Gaining additional practice with the fundamentals may feel clunky, but will help ensure you have full command over the concepts we expect you to know. Additionally, it is good practice for working in other programming environments which are more constrained and require creativity to overcome restrictions. For this exercise, you will be penalized for using any kind of loop construct other than a while
loop.
Overview
In this program, you will implement a main
function that contains Battleship’s “game loop”. The game loop is what controls the overall game logic:
The main
flow of this game works as follows:
- The player has up to five turns.
- The player gets to
input_guess
a row and column within the bounds of the size of the grid. - The guess is compared to the secret row and column and we
print_grid
/ “codified” boxes are output- Recall, when a
correct_guess
is given, the guess location is a red box, otherwise, it is depicted as a white box.
- Recall, when a
- If the guess was correct, the game is over and the player wins.
- If the guess was incorrect, the game loop goes back to step 2 to continue on with the next turn.
Each of the four monospace font-face
words above (input_guess
,print_grid
, correct_guess
, main
) will be implemented as functions to better structure your program into simple, reusable abstractions. Once you have created these building-blocks, you will put them together by calling them within the main
function.
Part 0. Setting up the Python Program
In Visual Studio Code, be sure your workspace is open. (Reminder: File > Open Recent > comp110-YYS-workspace is a quick way to reopen it! Where YY is the current year and S is the semeseter: S for Spring, F for Fall.)
Open the Explorer pane (click the icon with two sheets of paper or to to View > Explorer) and expand the Workspace directory.
Right click on the exercises
directory and select “New File”. Enter the following filename, being careful to match capitalization and punctuation:
ex03_functional_battleship.py
Before beginning work on the program, you should add a docstring to the top of your Python module just as you have previously. Then, you should add a line with the special variable named __author__
assigned to be a string with your 9-digit student PID.
Part 1. input_guess
- 20 points
Declare a function named input_guess
. The function will prompt for and eventually return the user’s row or column guess. More specifically, the function takes in an int
parameter representing the size of the grid, a str
parameter representing a specification of row or column, and an int
return type. The algorithm of this function is very similar to the user input logic you have already used in EX02——note how we are condensing repetitive logic for inputting a row and column into one reusable function!
Since the caller of this function can be expected to provide a correct argument for the str
parameter, specifically one that is “row” or “column, we will”assert" this assumption in our code such that an error is raised if it is not found to be true. As the first line of code in your function’s body, add the following assert statement and fill in the blank (_____
) with your second parameter’s name:
assert ______ == "row" or ______ == "column"
Once you have tried implementing the function, import it for use in the REPL to test it out. As featured in the example below, run the python
command to start the REPL. Subsequently, the command from exercises.ex03_functional_battleship import input_guess
will import
your function definition from
your exercises.ex03_functional_battleship
. Type out the example function calls to check that your function returns the expected values.
$ python Python 3.10.2 (v3.10.2:a58ebcc701, Jan 13 2022, 14:50:16) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> from exercises.ex03_functional_battleship import input_guess >>> input_guess(4, "row") Guess a row: 4 4 >>> input_guess(3, "row") Guess a row: 4 The grid is only 3 by 3. Try again: 2 2 >>> input_guess(4, "column") Guess a column: 0 The grid is only 4 by 4. Try again: 1 1 >>> input_guess(3, "column") Guess a column: 3 3 >>> input_guess(4, "rolumn") Traceback (most recent call last): File "", line 1, in File "/workspace/exercises/ex03_functional_battleship.py", line 3, in input_guess assert type == "row" or type == "column" AssertionError >>>quit()
Part 2. print_grid
- 20 points
Declare a function named print_grid
. Given the size of the grid, the user’s row and column guesses, and whether the guesses were correct, the function will print a grid of boxes to represent the game board. The function has:
- An
int
parameter representing the size of the grid. - An
int
parameter representing the row guess. - An
int
parameter representing the column guess. - A
bool
parameter representing if the user’s guess was correct. - A
None
return type.
The body of this function should utilize the named constants you setup in EX02 for the colored emoji boxes. Just as before, you may test out this function in the REPL.
$ python Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> from exercises.ex03_functional_battleship import print_grid >>> print_grid(4,3,2,True) 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦🟥🟦🟦 🟦🟦🟦🟦 >>> print_grid(5,1,1,True) 🟥🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 🟦🟦🟦🟦🟦 >>> print_grid(4,3,2,False) 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦⬜🟦🟦 🟦🟦🟦🟦
Notice how your print_grid
function contains all of the logic needed to create any sort of grid representation, given just a few pieces of information. This is the beauty of abstraction!
Part 3. correct_guess
– 10 Points
Declare a function named correct_guess
. Given the secret boat location and the user’s guess, the function will return if the user was correct or not. The function should take in four int
parameters representing the secret row, the secret column, the row guess, and the column guess, and it will return a bool
.
$ python Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> from exercises.ex03_functional_battleship import correct_guess >>> correct_guess(4,4,3,1) False >>> correct_guess(3,2,1,2) False >>> correct_guess(1,1,1,1) True >>> correct_guess(2,3,2,3) True >>> quit()
Part 4. main
– 20 Points
Now, it’s finally time to implement the main
function that will pull together each of the smaller pieces that you created in your program. Similar to your other functions, the main
functions has certain specifications including:
- An
int
parameter for grid size. - An
int
paraemeter for secret row. - An
int
parameter for secret column. - A return type of
None
.
The “state” of a game refers to the variables you need to keep track of in memory in order to run the game. What variables do you need to keep track of? Define those inside of main
’s body first.
Then, begin the game loop while
the user still has turns left to play and the user hasn’t won yet, you will want to do the following:
- Print the current turn number in a format such as
=== Turn 1/5 ===
- Prompt the user for a row and column guess, relying on your function
input_guess
to obtain a guess within the proper bounds. - Verify the user’s guess using
correct_guess
. - Codify the emoji results of the user’s guess by making use of your
print_grid
function. - If the user’s guess is correct, the user has won! Print
Hit!
andYou won in N/5 turns!
where N is replaced with the number of guesses it took. End the loop by updating the appropriate variables. - Otherwise, print
Miss!
and move on to the next turn. - If the user has exhuasted all turns, print
X/5 - Better luck next time!
whereX
is literally the characterX
and end the game.
As you are working on main
, you can save your work and import the main
function just like the others and try calling it:
$ python Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> from exercises.ex03_functional_battleship import main >>> main(4, 2, 1) === Turn 1/5 === Guess a row: 3 Guess a column: 3 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦🟦⬜🟦 🟦🟦🟦🟦 Miss! === Turn 2/5 === Guess a row: 4 Guess a column: 4 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦🟦🟦⬜ Miss! === Turn 3/5 === Guess a row: 2 Guess a column: 2 🟦🟦🟦🟦 🟦⬜🟦🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 Miss! === Turn 4/5 === Guess a row: 2 Guess a column: 1 🟦🟦🟦🟦 🟥🟦🟦🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 Hit! You won in 4/5 turns! >>> quit() $ python Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> from exercises.ex03_functional_battleship import main >>> main(4, 4, 4) === Turn 1/5 === Guess a row: 1 Guess a column: 2 🟦⬜🟦🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 Miss! === Turn 2/5 === Guess a row: 3 Guess a column: 4 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦🟦🟦⬜ 🟦🟦🟦🟦 Miss! === Turn 3/5 === Guess a row: 5 The grid is only 4 by 4. Try again: 2 Guess a column: 4 🟦🟦🟦🟦 🟦🟦🟦⬜ 🟦🟦🟦🟦 🟦🟦🟦🟦 Miss! === Turn 4/5 === Guess a row: 2 Guess a column: 3 Miss! 🟦🟦🟦🟦 🟦🟦⬜🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 === Turn 5/5 === Guess a row: 1 Guess a column: 2 🟦⬜🟦🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 🟦🟦🟦🟦 Miss! X/5 - Better luck next time! >>> quit()
Once you have your main
function and game loop working, there’s only one bit of icing left to add to your delicious code cake. We will fully explain what is going on in the following code snippet soon, but for now note that this is an idiom common to Python programs like the one you have written. We will learn it does three things: 1. it makes it possible to run your Python program as a module (if you tried python -m exercises.ex03_functional_battleship
right now you would see nothing happens), 2. you can actually play your game with a randomized grid size and secret boat locations unknown to you, and 3. it makes it possible for other modules to import your functions and reuse them. Add the following snippet of code as the last 2 lines of your program (notice, there are two underscores on both sides of the words name
and main
):
if __name__ == "__main__": grid_size: int = random.randint(3, 5) main(grid_size, random.randint(1, grid_size), random.randint(1, grid_size))
NOTE: You will also need to add import random
somewhere at the top of your program, preferably underneath your author variable.
Now you can try running your game as a module and it should work: python -m exercises.ex03_functional_battleship
Congratulations on writing your first functional program in COMP110!
Part 5. Style and Documentation Requirements – 20 Points (Manually Graded)
We will manually grade your code and are looking for good choices of meaningful variable names. Your variable names should be descriptive of their purposes. (Loop indexing variables can be short, such as i
, or idx
.) You should also use the Python snake_case
convention where variable names are all lowercase and new words are separated by underscores.
You should add code comments in your own English words to describe what is happening at important stages of your program.
Your program should work regardless of the size of the grid. Thus, you should not have any hard-coded numbers (such as 3
for guess_row
). All numbers that appear in output and the boundaries of loops should be based on the size
of your grid.
Part 6. Type Safety and Linting - 9 Points
The autograder uses industry standard tools for checking the type safety of your programs (e.g. being sure you’re using variables of the correct data types in valid ways) and linting style rules. If you have point deductions on Type Safety or Linting, read through the feedback the autograder gives and it should direct you to the line number in your program with the issue.
Make a Backup Checkpoint “Commit”
As you make progress on this exercise, making backups is encouraged.
- Open the Source Control panel (Command Palette: “Show SCM” or click the icon with three circles and lines on the activity panel).
- Notice the files listed under Changes. These are files you’ve made modifications to since your last backup.
- Move your mouse’s cursor over the word Changes and notice the + symbol that appears. Click that plus symbol to add all changes to the next backup. You will now see the files listed under “Staged Changes”.
- If you do not want to backup all changed files, you can select them individually. For this course you’re encouraged to back everything up.
- In the Message box, give a brief description of what you’ve changed and are backing up. This will help you find a specific backup (called a “commit”) if needed. In this case a message such as, “Progress on Exercise N” will suffice, where N is the current exercise.
- Press the Check icon to make a Commit (a version) of your work.
- Finally, press the Ellipses icon (…), look for “Pull/Push” submenu, and select “Push to…”, and in the dropdown select your backup repository.
Submit to Gradescope for Grading
All that’s left now is to hand-in your work on Gradescope for grading!
Login to Gradescope and select the assignment named “EX03 - Functional Battleship”. You’ll see an area to upload a zip file. To produce a zip file for autograding, return back to Visual Studio Code.
If you do not see a Terminal at the bottom of your screen, open the Command Palette and search for “View: Toggle Integrated Terminal”.
Type the following command (all on a single line):
python -m tools.submission exercises/ex03_functional_battleship.py
In the file explorer pane, look to find the zip file named “yy.mm.dd-hh.mm-exercises-ex01_chardle.py.zip”. The “yy”, “mm”, “dd”, and so on, are timestamps with the current year, month, day, hour, minute. If you right click on this file and select “Reveal in File Explorer” on Windows or “Reveal in Finder” on Mac, the zip file’s location on your computer will open. Upload this file to Gradescope to submit your work for this exercise.
Autograding will take a few moments to complete. For this exercise there will be 20 “human graded” points. Thus, you should expect a maximum autograding score of 80 possible points on this assignment. If there are issues reported, you are encouraged to try and resolve them and resubmit. If for any reason you aren’t receiving full credit and aren’t sure what to try next, come give us a visit in office hours!