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 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:

• The shape is a string: "diamond", "oval", or "triangle"
• The color is a string: "orange", "purple", or "green"
• The count is an integer: 1, 2, or 3.
• The fill is a string: "none", "light", or "dark"

Your Card object will have six methods:

• A constructor that takes the shape, color, count and fill as parameters in that order.
• Four getters/accessors: get_shape, get_color, get_count and get_fill. Each method will return the appropriate value of the data stored in the instance.
• A __str__ method that returns a string representing the data in the Card.
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__":
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) diamond,green,3,dark >>> c1.get_shape() 'diamond' >>> c1.get_color() 'green' >>> c1.get_count() 3 >>> c1.get_fill() 'dark' >>>  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: • A constructor that takes no parameters. After calling the constructor, your deck should contain a shuffled list of 81 cards. • A deal method that takes no parameters. This method should remove one Card from the list of cards stored in the object and return the removed Card. • A cards_left method that returns the number of Cards left in the Deck. 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__": 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
>>> from Deck import *
>>> deck = Deck()
>>> print(deck.cards_left())
81
>>> card = deck.deal()
>>> print(card)
triangle,green,1,none
>>> card = deck.deal()
>>> print(card)
triangle,purple,1,dark
>>> print(deck.cards_left())
79
>>> for i in range(5):
...     print(deck.deal())
...
diamond,orange,1,none
diamond,purple,2,light
triangle,purple,1,light
oval,orange,1,light
diamond,green,3,dark
>>> print(deck.cards_left())
74
>>>

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:

• A constructor that takes no parameters. When the constructor is called, the object will first create a Deck of cards. Then, you will deal twelve cards from the Deck into your list of cards. You should deal the cards one at a time from the Deck using the deal method from your Deck class.
• A get_card method that takes a parameter i and returns the Card on the tableau at index i.
• A player_guesses_triad method which takes three integer parameters. These parameters are the indices of different cards which the user thinks form a triad. If the user is correct, you should replace all three cards with new ones from the deck (so that get_card returns those new cards now). Whether the user is correct or not, this method should return an appropriate message to show to the user (as a string).
• A player_guesses_no_triad method which takes no parameters. If there is no triad, then this method should replace all twelve cards on the tableau with new cards from the deck. (Note that if there are fewer than twelve cards left in the deck, replace as many cards as you can and then just stop.) Either way, this method should return an appropriate message (as a string) to show to the user.
• A game_over method that returns True if there are no cards left in the Deck; otherwise, this method returns False.
In the process of writing the above methods, you will find it very helpful to write some helper methods for your class. We suggest:
• A test_match method that takes three integer parameters, i, j, and k. This method will examine the tableau Cards at three indexes and determine if they form a triad. If they do, this method returns True; otherwise, it returns False.
• An exists_match method that takes no parameters. This method returns True if there exists a triad somewhere on the board. To check this, use nested for loops to test all combinations of three cards using your test_match methods. Hint/Note: Sometimes it is hard for you to find a Triad while you are testing. As a debugging tool only you can have this method cheat for you by having it print out the indices of the Triad it found. This will allow you to make more progress in the game while you are testing it. Be sure to take this out when you turn in your final version.
• A cards_left method that takes no parameters and returns the number of cards remaining in your Deck. You can determine the number of cards left in the Deck using the cards_left method from the Deck class.
• A replace method that takes a parameter i and replaces the card in your list of tableau cards with a new card dealt from the Deck.

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.