CS21 Lab 5: Fruitful functions

Due Saturday, October 17, before midnight

Programming Tips

As you write your first programs, start using good programming practices now:

  • Use a comment at the top of the file to describe the purpose of the program (see example).

  • All programs should have a main() function (see example).

  • Use variable names that describe the contents of the variables.

  • Write your programs incrementally and test them as you go. This is really crucial to success: don’t write lots of code and then test it all at once! Write a little code, make sure it works, then add some more and test it again.

  • Don’t assume that if your program passes the sample tests we provide that it is completely correct. Come up with your own test cases and verify that the program is producing the right output on them.

  • Avoid writing any lines of code that exceed 80 columns. In emacs, at the bottom, center of the window, there is an indication of both the line and the column of the cursor.

Function Comments

All functions should have a top-level comment! Please see our function example page if you are confused about writing function comments.

Are your files in the correct place?

Make sure all programs are saved to your cs21/labs/05 directory! Files outside that directory will not be graded.

$ update21
$ cd ~/cs21/labs/05
$ pwd
/home/username/cs21/labs/05
$ ls
Questions-05.txt
(should see your program files here)

Goals

  • Work with functions and lists

  • Work with mutable and immutable function parameters

  • Work with functions that return values

  • Work with functions done only for their side effect

1. Checkpoint

At the end of your lab session, be sure to run handin21.

Your lab instructor will check your files, which should reflect that you have made non-trivial progress (in python or pseudocode) towards the solution. Note that if you have not made much progress towards your solution, we expect that you would have been actively seeking help during your lab over Slack.

A portion of your final lab grade is dependent on this checkpoint.

If there are circumstances which prevent you from making substantial progress on this lab, please contact your lab instructor as soon as possible.

2. Tic-Tac-Toe

In tictactoe.py you will implement a version of the tic-tac-toe game. In this game, players alternate between placing their symbol (either "X" or "O") inside a 3x3 grid. The first player to get three in a line (either on a row, a column, or a diagonal) wins.

In our version, the user will play against the computer. The computer will always play first and use the symbol "O", and the user will play second and use the symbol "X". The game can end with the user winning, the computer winning, or a tie.

Here are examples of playing the game showing the three possible outcomes:

2.1. Incrementally develop main

Start by creating a main program that prints the welcome message. Test and modify your main until you are happy with the look of the welcome message.

As you add each function to your program, you will be using main to test them, so your main program will go through many iterations before you get to the final version. This is normal.

2.2. Display the board

In order to implement tic-tac-toe we will be using a list of characters to represent the board. The board will begin as a list of 9 empty spaces:

board = [" "," "," "," "," "," "," "," "," "]

Add the above assignment statement to your main program.

The board will be modified after each player takes a turn. For example, if the computer chooses to play in position 3, then the board will change to:

[" "," "," ","O"," "," "," "," "," "]

If the user chooses to play in position 7, then the board will change to:

[" "," "," ","O"," "," "," ","X"," "]

Write a function called display_board(board), that takes in a board represented in this way, and prints the board in the familiar grid format of tic-tac-toe. This function does not return any value, it is done for its side effect.

For example, the board above would print as:

   |   |
-----------
 O |   |
-----------
   | X |

2.2.1. Test display_board

Add the following assignment statement to your main program:

sample_board = ["0","1","2","3","4","5","6","7","8"]

Update your main program to print the instructions about how to play the game, and use your display_board function to draw out the sample board:

 0 | 1 | 2
-----------
 3 | 4 | 5
-----------
 6 | 7 | 8

Once you are convinced that your function works you may move on.

2.3. User turn

Write a function called user_turn(board) that takes in the current board as input and modifies the board to contain the user’s chosen play.

This function should prompt the user for a choice between 0 and 8. Your function will continue to prompt the user until they provide a valid choice. A valid choice must be between 0 and 8 (inclusive) and it must be an empty spot on the board. You should modify the board with a "X" at their valid choice. Note that this function does not return any value, but as a side effect modifies the board.

You should review Example 1 above to see how the function continues to prompt the user until a valid input is provided.

2.3.1. Test user_turn

In your main program, create a for loop to call the user_turn function and the display_board function 3 times. Ensure that your function only allows valid plays. Clearly, in the real game, the user will not be allowed to take multiple turns in a row. However, this is just a convenient way to test the function. Once you are convinced the function is correct you may move on.

2.4. Computer turn

Write a function called computer_turn(board) that takes in the current board as input and modifies the board to contain the computer’s chosen play. There is no return value for this function

To implement the computer’s turn you should create a list of all of the empty locations on the current board. Then you should you the random library’s choice function to select one of these empty locations. Be sure to update the board with an "O" at the chosen location and to also print out the computer’s selection. Like the user_turn function, the computer_turn function does not return any value, but as a side effect it modifies the board.

2.4.1. Test computer_turn

In your main program, add to the for loop that you previously created. Before you get the user’s selection, call your computer_turn function and your display_board function again. So your loop should now have four statements. We are not worried about recognizing a win at this point. We just want to see if the computer_turn function correctly selects valid empty locations to play and that the board is properly updated. Once you are convinced that your function is correct you may move on.

2.5. Determining a win

Write a function called check_player_win(board, symbol) that takes as input a board and the symbol representing one of the players (either "X" or "O"), and returns True if that player has won. Otherwise it returns False.

For example, if the current board look like this:

 O | X | O
-----------
 O | X |
-----------
   | X |

then check_player_win(board, "O") should return False and check_player_win(board, "X") should return True

There are a number of cases you need to check. We suggest that you:

  • use a for loop to check the three possible horizontal wins

  • use another for loop to check the three possible vertical wins

  • test separately for the two possible diagonal wins

2.5.1. Test check_player_win

In the main program, remove the for loop that you used to test the turn functions. Now create some test boards, like those shown below, that represent a win for one of the players and a loss for the other. Try cut and pasting this code from the web page right into your program. The outputs of the print statements should be True for the winner and False for the other player.

board1 = ["O", " ", "O", "X", "X", "X", "O", " ", " "]
display_board(board1)
print("Is O winner?", check_player_win(board1, "O")) # False
print("Is X winner?", check_player_win(board1, "X")) # True
board2 = ["O", " ", " ", "O", "X", "X", "O", " ", " "]
display_board(board2)
print("Is O winner?",check_player_win(board2, "O")) # True
print("Is X winner?",check_player_win(board2, "X")) # False
board3 = ["O", "X", "X", " ", "O", " ", " ", " ", "O"]
display_board(board3)
print("Is O winner?",check_player_win(board3, "O")) # True
print("Is X winner?",check_player_win(board3, "X")) # False

Once you are convinced that your function is correct, you may move on. Go ahead and delete all of the sample boards and extra code that you added to test your check_player_win function.

2.6. Putting it all together

Once all of your helper functions are complete and tested, it is time to build the final version of your main program.

Your main program should initially contain the statements that print the welcome message, initialize the board and the sample board, print the instructions, and display the sample board.

Now you are ready to create the game loop. Should you use a for loop or a while loop? Do you know exactly how many times you’ll run the loop or does it depend on the circumstances?

Each time through the game loop either the user will play or the computer will play. We recommend keeping a turn counter that you initialize to 1 and increment each time through the loop. When the turn counter is odd, it is the computer’s turn, because the computer gets to go first, otherwise it is the player’s turn. After each player’s turn you should check if that player has won. You should also display the updated game board.

The game loop ends when one of the player’s has won, or if the turn counter reaches 10 without a winner, indicating a tie. Be sure to print the outcome of the game at the end.

2.6.1. Test main and the whole game

To catch possible mistakes you might have made you should play lots of games using your program. Be sure that it works properly for all of the possible outcomes: a user win, a computer win, or a tie. Once you are convinced it’s correct you’re done programming! You just need to complete the questionnaire.

2.7. Extra Challenge

This extra challenge is not required and should only be attempted once the rest of your program is complete.

Update your program so that the computer player makes intelligent choices, rather than just choosing randomly from the open locations. For example, the computer could recognize when the user has the chance to win in the next turn, and block that location.

3. Answer the Questionnaire

Each lab will have a short questionnaire at the end. Please edit the Questions-05.txt file in your cs21/labs/05 directory and answer the questions in that file.

Once you’re done with that, run handin21 again.

Turning in your labs…​.

Remember to run handin21 to turn in your lab files! You may run handin21 as many times as you want. Each time it will turn in any new work. We recommend running handin21 after you complete each program or after you complete significant work on any one program.