Test Project 2 Due: 04:15:00PM on 2021-12-13

A test project is an assignment that you complete on your own, without the help of others. It is like a take-home exam. You may use the textbook, your notes, your previous assignments, the notes and examples on the course web page and the Python library documentation (linked from the course web page), but use of any other source, such as Google, is not permitted. You may not discuss these assignment with anyone except the course instructor, and ASIs and tutors. However, all of those parties may only give you limited answers (as you would expect during an exam).

To be clear, you may not work with, discuss, or in any way collaborate with anyone else.

You are encouraged to reuse code from your assignments or our class examples. Partial credit will be awarded, so if you can’t solve the whole problem, get as far as you can. If you are stuck, contact me. While I cannot help you solve the test project itself, I may be able to suggest general problem-solving strategies, help with conceptual difficulties about Python, and or direct you to relevant examples from class.

Memory Game

For this test project you will be implementing a text-based version of the game “Memory” (aka concentration), where the user has to find matching pairs of cards. See the Wikipedia page on the game for more information.

When the game starts, all of the cards are face down. In our version there are 16 cards, each indicated by a numbered square. The cards are laid out in four rows of four cards each:

1  2  3  4  
5  6  7  8  
9  10 11 12 
13 14 15 16 

“Underneath” each of these numbers is a letter. The user does not see these, but the program will keep track of them. For example, the board above may have the following letters underneath:

G  D  B  F  
H  B  C  D  
A  A  E  E  
F  H  G  C 

The game proceeds by having the user pick two squares to look under by entering the numbers of the squares separated by a space.

1  2  3  4  
5  6  7  8  
9  10 11 12 
13 14 15 16 
Guess two squares:

For example, to view squares 1 and 2 the user would type 1 2; to view 7 and 15 the user would type 7 15. The two cards that the user specified are then shown. For example, if the user entered 7 15 they would see:

1  2  3  4  
5  6  C  8  
9  10 11 12 
13 14 G  16 

If the user’s selected cards do NOT match, like 7 (C) and 15 (G), then the cards are displayed for 2 seconds and then turned back over leaving just the numbers. The user is then prompted for another choice:

1  2  3  4  
5  6  7  8  
9  10 11 12 
13 14 15 16 
Guess two squares:

If the user selects two that do match, for example 2 and 8 (both D) then those two are flipped over and they stay flipped over for the rest of the game. The user is then immediately prompted to enter an additional pair.

1  D  3  4  
5  6  7  D
9  10 11 12 
13 14 15 16 
Guess two squares:

If the user enters invalid numbers (outside the range 1-16, or cards that have already been turned over), the program notifies the user of the invalid input and prompts them again:

Guess two squares: 1 33
Invalid number(s).
Guess two squares:

The game ends once the user finds all matching pairs. When the game ends, the text “You win!” is displayed along with the finished board, the number of moves, and the amount of time it took the player to solve the game. For example:

You win!
G  D  B  F 
H  B  C  D  
A  A  E  E  
F  H  G  C
It took you 15 guesses and 99 seconds.

A Demo

To run a demo of the memory game:

  1. On OSX, start Terminal from within Thonny via the “Tools -> Open System Shell” menu option. Windows users can download and use the Putty SSH program. Note that you want the “putty.exe” binary (further down on the page), not the package installer. Here are some additional instructions for using Putty.

  2. Connect to basin via using the following command, replacing <username> with your Middlebury username. Answer yes to any question and then enter your Middlebury password (as a security measure nothing will show on the screen while you type your password).

    ssh <middlebury username>@basin.cs.middlebury.edu
    

    e.g. for me it is ssh mlinderman@basin.cs.middlebury.edu.

  3. Once connected, in the Terminal window type:

     ~mlinderman/courses/cs150/f21/tp2
    

    This should execute the example program in the terminal window. If you ever want to exit or quit early, you can type Ctrl + c.

Specifications

  1. Your program must exactly implement the game play shown above:
    • The displayed board must look exactly like the board shown above with the columns lined up correctly and left justified (each column has a width of 3 characters).
    • It must display the flipped over entries for 2 seconds if the guess is incorrect. If the guess is correct it immediately shows the updated board and prompts for another guess.
    • The game should finish when all of the entries have been correctly guessed and are flipped over. You should then display the amount of time it took and the total number of valid guesses that the user made. A valid guess is any guess where the cards are flipped over (whether they match or don’t match).
    • The “underside” of the entries will be pairs of the letters ‘A’ through ‘H’ (for a 4x4 game). Each time the game is played you should get a different, random, configuration of the letters.
  2. Your game must be launched by invoking a function named play_game that does not take any parameters.
  3. When run, your program should start playing the game automatically. If your module is imported, however, the game should not start playing.
  4. You can assume that the user enters two numbers separated by a space. However, you must check that the user input is valid, specifically:
    • That the numbers are valid entries on the board (e.g. 1-16 for the default board size)
    • That the entries corresponding to the numbers entered have not already been correctly guessed (i.e., flipped over)
    • That the two numbers are different (e.g., “3 3” is NOT valid)
  5. You should define constants for the number of rows and columns at the top of your program, and use these constants throughout. Your submitted version should use 4 rows and 4 columns.

If you follow the above requirements (and also get full credit for style and documentation), you will earn 48 of 50 points. For the remaining two points, your program should easily generalize to other board sizes, solely by changing your two constants that specify the number of rows and columns. This means you will need to use loops for initializing and printing the board. Any even number of squares should be supported, up to 50 squares (25 pairs of letters), with no more than 10 rows or 10 columns. For instance, if 5 rows and 8 columns are specified, the game should use 20 pairs of letters (‘A’ through ‘T’).

Guide

As always, I strongly suggest an incremental approach to developing your program. Before you start programming, develop a design (like you did for lab 8) that describes what functions you will need, the parameters each function should take and how each function will work. You won’t turn this in, but it will save you a lot of time if you think through the design of the program first. As you start to code, work incrementally - pick one function and get it working before moving on. Read through this entire section before you start the program since I provide many implementation suggestions.

Representing state

As you’re thinking about how you will write your program, think about what information you need to store and update as the game progresses and how you are going to represent this information. For example, for the word game (Lab 6) we had a number of pieces of information:

What information do we need to keep track of for this game? And how will it be stored? As a string, a list, a set, a dictionary, etc.? You should choose data structures that are convenient and efficient. You will be graded based on your choices; for example, using a string to represent the entire board will be neither convenient nor efficient, and so would not earn full credit.

Game play

Your program will necessarily have a loop to repeat the steps of each “turn. In each “turn”:

  1. The current version of the board should be displayed
  2. A valid guess should be obtained from the user
  3. Check whether the guess is a match or not and act accordingly

This is a rough skeleton of each “turn”. I suggest making a more detailed listing of the game play in each turn during the planning phase.

Notes and suggestions

There are many ways of implementing this program. Here is one approach:

  1. Pick your board representation. You will need an approach for mapping “card numbers” to the underlying letter and keeping track of which numbers on the board have been correctly guessed. Enumerate the different actions in the game, e.g., “flipping card over” and map those actions to transformations to your chosen data structure(s). I encourage you to figure out your data structure(s) before writing any code.
  2. Write a function to generate a random board configuration. The random module has a function named shuffle that takes a list as an argument and randomly shuffles the elements in place. For example, try shuffling the list ['A', 'A', 'B', 'B'].
  3. Write a function to display the board. The board should be printed out as 4 lines. If a number has not been correctly guessed, then the number will be printed. If the number has been correctly guessed, then the letter will be printed. To check that you have this working correctly, manually add numbers that have been “correctly guessed” and make sure that the board is displayed correctly.
  4. Obtain the input from the user and then, regardless of whether they match or not, show the updated version of the board (i.e., with the letters flipped over for the selected numbers), then revert back to the original board after the 2 second pause. Some suggestions:
    • In the time module there is a function named sleep that takes the number of seconds as a parameter and the program stops executing for that number of seconds.
    • You cannot “delete” text that you have already printed on the screen. However, one way to make it look like the text has been deleted is to print out a number of blank lines (e.g., 100). This causes anything that was on the screen to disappear. If you do this every time before you display the board, it will appear to the user like the board is simply being updated.
    • There are many ways of updating the board to reflect the current guess and then “undoing” that update if the guess is not right. Think about the best approach for your approach to storing the board.
  5. Update your game loop to determine when the pairs match versus when the pairs don’t match. To help you when you’re debugging, you can print out what the “underneath” letters are, though make sure to remove this printing before you submit.
  6. Add checks to make sure that the user enters valid numbers. You may assume that the user enters two integers separated by a space. You must however check to see if they are valid board spaces and whether or not they have already been successfully guessed. If the user’s input is not valid, then you should prompt the user to enter another selection without printing the board again. If you write a separate function that implements all of these different checks then your code will be much simpler.
  7. Add functionality to check if the user has won and print “You win!” when the game is won. Also print the number of valid guesses and time it took the user to solve the problem. At this point you should have a working version of the memory game. During testing, it may be easier if you make your game smaller (e.g. use just a 2 x 2 board or 2 x 4 board).

A Python Tip

To print multiple strings on the same line, use the optional keyword parameter end to specify what should be printed after each item (by default end is the newline character). See help(print). For example:

>>> for i in range(3):
...     print(i)
...
0
1
2
>>> for i in range(3):
...     print(i, end=" ")
...
0 1 2 >>>
>>>
>>> for i in range(3):
...     print(i, end="")
...
012>>>

Note that in both of the latter cases, changing end parameter means no newlines are printed at all. An alternate approach is to build up a string, and then print that string:

>>> s = ''
>>> for i in range(3):
...     s += str(i)
...
>>> print(s)
012

Python strings have a ljust method that can be used to generate left-justified text padded out to a fixed width.

When you’re done

Before submitting your code, double check to make sure you have satisfied all of the requirements and your program runs properly in a newly started Python shell (running in the terminal is a good way ensure that is indeed the case).

Make sure that your program is properly documented:

and uses good coding style:

Submit your program via Gradescope. Your program program file must be named tp2_memory.py. You can submit multiple times, with only the most recent submission (before the due date) graded. Note that the tests performed by Gradescope are limited. Passing all of the visible tests does not guarantee that your submission correctly satisfies all of the requirements of the assignment.

Gradescope will import your file for testing so that make sure that, as specified, no code executes on import. That is when imported your program should not start the game.

Features Points
Board displays correctly 6
Board is randomly initialized 4
Loops until game completed 4
Properly handles match 7
Properly handles non-match 7
Handles invalid user input 6
Starts properly, counts guesses and records time elapsed 4
Generalizes to other sizes 2
Code design and style 10
Total 50