CS21 Lab 10: Triad

Due Saturday Night, December 5

Run update21, if you haven't already, to create the cs21/labs/10 directory. Then cd into your cs21/labs/10 directory and create the python programs for lab 10 in this directory.


This week you will implement a card game called Triad. The game Triad is quite similar to a card game called Set. You will get practice writing your own classes and becoming more familiar with object-oriented programming.

We will provide you with two supporting classes that will create the graphical display, draw the cards on the screen, highlight the cards when you select them with the mouse, remove cards from the screen when they form an appropriate triad, deal more cards onto the screen when necessary, and end the game when the game is over. Your assignment is to write three other classes that will work with the classes we provide you. When your three classes are implemented, you will have completed the lab and you will have a playable version of the game Triad.

First, we will explain the rules of the game. We will then describe the Card class and the Deck class that you will need to write. Finally, we will describe the TriadGame class that you will write which implements the logic of the game. The primary responsibility for testing the code will be yours.

We have written a very small tester for you which will work once you have written the Card and Deck classes. When you run update21, if you don't get Tester.py in your labs/10 directory, then type:

cp /home/knerr/Tester.py ~/cs21/labs/10/

Triad Game Play

Triad is a solitaire card game played with a deck of 81 cards. The face of each card contains a simple symbol which may be repeated. Each card can be described in four ways: by the number of symbols which appear (1, 2, or 3), by the shape of those symbols ("triangle", "oval", or "diamond"), by the color of those symbols ("orange", "purple", or "green"), and by the shading of those symbols ("none", "light", or "dark"). For example, consider the three cards presented below:

Three green triangles with no shading One purple oval with dark shading Two orange diamonds with light shading
The Triad deck contains all 3^4 = 81 unique combinations of these properties, one card for each. At the beginning of the game, the deck is shuffled and twelve cards are dealt onto the table (also called the "tableau"). The player's objective is to find triads of cards. A triad is formed of three cards such that, for each property, all cards are different or all cards are the same. The above three cards, for instance, form a triad because they all have a different number of symbols, a different shape, a different color, and a different shading. The following three cards are also a triad:

Two green diamonds with light shading Two orange diamonds with no shading Two purple diamonds with dark shading
The above cards form a triad because they all have different colors and all have different shadings, but they all have the same shape (diamonds) and they all have two symbols each. These cards do not form a triad:
Three green diamonds with dark shading Two green ovals with dark shading One green diamond with dark shading
These three cards are all different in their number of symbols. They are also all the same in their color (green) and shading (full). But two cards show diamonds and one card shows ovals; thus, they are neither all different nor all the same in their shape.

After the initial twelve cards are dealt, play proceeds with the player picking triads of three cards. Each time the player finds a triad of three cards, those three cards are removed from the playing field. Then, three new cards are dealt from the deck onto the tableau to replace them. Play continues until the deck is empty; at this point, the game is over when there are no more triads to find.

It may occasionally happen that, although there are cards left in the deck, none of the cards on the tableau form a triad. In this case (and only in this case!), all of the cards on the tableau are discarded -- you do not put them back in the deck. Then, twelve new cards are dealt from deck and those new cards are placed on the tableau.

The player wins and the game ends when there are no cards left in the deck. There is no loss condition; the only way to lose this game is by quitting.

Card class
The first class you are going to write will be the Card class. A Card object will represent a single Card in the game of Triad. You must define your class in a file called Card.py. Below are the data and methods your Card object will store.

Your Card object will store the shape, color, count and fill of the Card. Each of the data values store the following:

Your Card object will have six methods:

You should also write a main function that tests your Card class. Put the call to main at the end of your program in a block of code that looks like this:
if __name__ == "__main__":
Here is a sample run of the Card class to demonstrate how it should work:
$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from Card import Card
>>> c1 = Card("diamond", "green", 3, "dark")
>>> print(c1)
>>> c1.get_shape()
>>> c1.get_color()
>>> c1.get_count()
>>> c1.get_fill()
When you have finished with the Card class and after you have thoroughly tested it, you can move on to the Deck class. For this lab, you can assume that the Card constructor is only ever called with legal values for the shape, color, fill and count.
Deck class
The second class you are going to write will be the Deck class, which you will save in a file called Deck.py. A Deck object will represent deck of Triad cards. When you create a Deck of cards, you should create all 81 cards. Your Deck class will only have one piece of data: a list of Cards. You should use a nested for loop to create the cards, appending each Card to the list.

You Deck object will have three methods:

Like the Card class, you should write a main function that tests your Deck class. Put the call to main at the end of your program in a block of code that looks like this:
if __name__ == "__main__":
Here is a sample run of the Deck class to demonstrate how it should work:
$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from Deck import *
>>> deck = Deck()
>>> print(deck.cards_left())
>>> card = deck.deal()
>>> print(card)
>>> card = deck.deal()
>>> print(card)
>>> print(deck.cards_left())
>>> for i in range(5):
...     print(deck.deal())
>>> print(deck.cards_left())
Note that because the Deck is randomly shuffled, you will almost certainly get different Cards printed out than in this example.

It is absolutely critical that you do not proceed to the next section unless you are confident that your Deck and Card classes work without any errors. If anything is wrong with your Deck and/or Card classes, debugging the next class will be very difficult.


The final class you will write is the TriadGame class, saved in the file TriadGame.py. The TriadGame class will manage the current state of the game and implement methods that allow the game to be played.

The TriadGame class will have two pieces of data. First, the class will maintain a Deck of cards (constructed using your Deck class). At the start of the game this Deck will contain 81 cards, but as the game progresses the Deck will get smaller. The TriadGame class will also store a list of the cards that are currently on the tableau. The graphical portion of the game will lay out the cards by their indices like so:

  0    3    6    9
  1    4    7   10
  2    5    8   11

The organization of the cards on the tableau shouldn't affect how you write your TriadGame class, but it's helpful to know the indices of the displayed cards when you are debugging.

The TriadGame class will implement four methods:

In the process of writing the above methods, you will find it very helpful to write some helper methods for your class. We suggest:

You are not required to write the above methods, but you should.

Like the Card and Deck classes, you should write a main function that tests your TriadGame class. Be sure to put the call to main at the end of your program using the special __name__ == "__main__" syntax shown above.

Playing Triad
Once you are convinced that your Card, Deck, and TriadGame classes are working properly, you can run the Triad.py program. If everything was implemented correctly, you will have a working version of Triad that you can play -- and impress your friends with!

Once you are satisfied with your program, hand it in by typing handin21 in a terminal window.