Project 1: Uno, Dos, Tres

Due by: Friday, February 7, 2020 at 11:59 p.m.

Uno is a popular card game in which each player has a hand of cards. During play, these cards are placed on a discard pile. There are rules determining which cards can be played, what the order of play is, and whether additional cards must be picked up. These rules are, in part, determined by the cards that have been played. The goal of the game is to be the first player to run out of cards.

For this project, you will write a class that behaves like a player playing Uno. In other words, given a hand of cards, information about the current top card, and the currently selected color, your class will need to choose which card to play (or none, if there is no legal play). The only requirement is that your class plays legally. However, extra credit is available for the teams whose implementations outplay the others.

Specification

Game play

If you are unfamiliar with the game of Uno, a detailed explanation is below. However, you may wish to consult other sources or play a few games with friends to understand all the intricacies.

A standard Uno deck consists of 108 cards. There are four Wild cards, four Wild Draw Four cards, and 25 cards belonging in each color of blue, green, red, and yellow. These 25 cards consist of two copies of the numbers 1 through 9, one copy of the number 0, two Reverse cards, two Skip cards, and two Draw Two cards. The following table summarizes the actions for each card and when they can be played.

CardsActionWhen Can Be Played
0 - 9NoneTop card matches in number or color
(including color set by a Wild card)
SkipSkips the next playerTop card is a Skip or matches in color
(including color set by a Wild card)
ReverseReverses the direction of play
(or acts like a Skip in a two-player game)
Top card is a Reverse or matches in color
(including color set by a Wild card)
Draw TwoNext player draws two cards and misses a turnTop card is a Draw Two or matches in color
(including color set by a Wild card)
WildPlayer who discards the Wild sets the current colorAlways
Wild Draw FourPlayer who discards the Wild Draw Four sets the current color,
and the next player draws four cards and misses a turn
Whenever no other cards can be played

A game begins by shuffling the deck of cards, dealing 7 cards to each player, and then turning the first card of the remaining deck over to start the discard pile. If the first card is a Wild Draw Four card, it is replaced, and the deck is reshuffled. If the first card is a Wild card, the first player is permitted to select the color before playing his or her first card.

The player clockwise from the dealer plays first. Play continues in this direction until a Reverse card is played, reversing the order. The game finishes when a player runs out of cards. When humans are playing, there's a requirement to call out "Uno!" when a player has only a single card remaining. In our version of the game with fully computerized players, this rule is essentially nonsensical. For players intending to play more than a single game, score can be kept. The winner of each game scores point values based on the cards the other players are still holding, with the first player to 500 points winning overall. For the purposes of this project, only the outcome of a single game is important, eliminating the need for this kind of scoring.

Implementation of a Player

You do not have to implement the game of Uno, however. That work has been done for you and is described below. The following files are provided for you, all in the uno package. Create a project called Project1, add a package to it called uno, and add the following files to that package. Note that Javadoc information is available here.

Most of these classes should be clear to you from their names. Card is an interface that all cards implement. ActionCard is an interface that action cards (Draw Two, Reverse, Skip, and Wild Draw Four) implement. ColoredCard is an interface that numbered and non-Wild action cards implement. WildDrawFourCard is a class that inherits from Wild. IllegalCardException is an exception that might be thrown if illegal cards are played.

Almost all of these files deal directly with cards, but Player.java does not, and it has special importance to you. Player is an interface that you must implement with your own class. Create a package called players inside your uno package (which creates a package called uno.players). Then, create a class inside this package named your group name (starting with a capital letter) followed by the word Player, such as Group1Player, Group2Player, and so on. This single file is all you will turn in. It must implement the Player interface, which contains the following methods.

Methods you must fully implement:

  • int makeChoice(Card top, List<Card> hand, Color color)
    Method called when your Player class must make a choice of which card to play. The input is the current card on the top of the discard pile, a List of all the cards you have in your hand, and the currently selected color. Usually, the color will be clear from the top card, but it will not be if a Wild card has been played. This method must return a legal index within hand corresponding to the card you want to play. This index will be between 0 and hand.size() - 1, inclusive, if you want to play a card. If you are unable to play a card, you should return -1.

  • Color pickColor()
    Method called when your Player class must make a choice of the current color. This method is usually called when you have just played a Wild or Wild Draw Four Card. Legal values are ColoredCard.Color.RED, ColoredCard.Color.Yellow, ColoredCard.Color.Green, and ColoredCard.Color.BLUE.

Methods you must have but may be empty:

  • void startGame(int players, int index, Card start)
    Method called when a game is starting. Its strategic value lies in telling your code that a new game has begun, how many players there are, the index of the first player, and the starting card.

  • void playerPlays(int index, Card card)
    Method called when a player (including you) plays a card. Its strategic value lies in telling your code that a player has played and which card was played.

  • void playerPicks(Color color)
    Method called when a player (including you) picks a color. Its strategic value lies in telling your code that a player has picked a color.

  • void playerDraws(int index)
    Method called when a player (including you) draws a card. Its strategic value lies in telling your code the index of a player who just drew a card.

  • void deckShuffles()
    Method called when the deck is shuffled. Its strategic value lies in telling your code that the deck has been shuffled, which might be important for card-counting strategies.

For full credit, all you must do is implement this interface and always play legally. Your Player class does not need to be an exceptionally good Uno player. However, you must play a card if there is a legal card to play. Choosing to draw a card when there was a card you could have played will result in a loss.

However, I will hold two tournaments. One tournament will run in two stages. The first stage will be played with games of approximately four players. Each stage will produce one winner overall. The second stage will be played with games of approximately eight players. The team whose Player class wins the second stage will earn 1% extra credit toward their final semester grade.

The second tournament will be all one-on-one games. The team whose Player class wins the most one-on-one games will earn 1% extra credit toward their final semester grade. It's possible that this team will be the same as the winner of the multiplayer tournament.

Note that a Player class instantly loses a game if it plays an illegal card (either one that doesn't match or a Wild Draw Four when it isn't allowed). There will also be grade penalties for playing illegally.

The Game class

All of the other classes to play the game are provided to you as source code. However, the Game class has some details that determine whether a card is legal that you need to write on your own. For that reason, Game and the PlayStatus enum inside Game are provided to you as .class files.

To use these files, first right-click on your Eclipse project and select Build Path > Configure Build Path... Click on the Libraries tab and click the Add Class Folder... button. Click the Create New Folder... button on the lower left and create a new folder with a meaningful name, like classes. Then, check the box next to that folder. Click the OK button and then click the Apply and Close button. Then, back in the main screen of Eclipse, right-click on the newly created folder and select New > Folder to create a new folder inside called uno. Then, right-click the links above and save them into the uno folder. All of this rigmarole is needed to use these .class files in your project without having their source code.

The Game class has a constructor with the signature shown below that takes an array of Player objects and a boolean indicating whether or not the game created should produce output.

public Game(Player[] players, boolean output)

You should create a main() method, either in your Player class or some other testing code. Your main() method will not be graded, but you can use it for testing purposes to create an array of Player objects and pass them this constructor with a boolean specifying whether or not you want output. (You probably do want output.) One of the issues is that you need an array with at least two Player objects in order to play a game. At first, you can create two of your own objects and have them play against each other. You may also want to create another class that implements the Player interface just to have another player to play against.

Once you've created a Game object, you can call its play() method whose signature is given below.

public int play() throws IllegalCardException

If you call this method on the Game object you've created, it will return the index within the array of the winning player (and produce output showing the game unfolding, if you passed in true for the output parameter in the constructor). If one of the players plays an illegal card, this method will throw an IllegalCardException. You may choose to catch this exception in your main() method, but a thrown exception means an illegal card and the loss of a game for the player that played the illegal card.

Testing

You must test your code to be sure that it does what it says that it does. First, write down your testing plan. Then, show the input (if applicable) and output for each test, copied and pasted from the console. Include this file as a file called testing.txt, testing.docx, or testing.pdf.

Turn In

Your Eclipse project should be called Project1, but the class you create that implements Player should be your group name (starting with a capital letter) followed by the word Player (such as Group1Player), and it must be in the uno.players package. Zip up this .java file from the Project1\src\uno\players folder inside your workspace folder with your testing file and upload the zip file to Blackboard. You should not need to make more than one class to solve this problem, but if you do, you can include those other .java files in your zip file. Only the team leader should turn in this file.

All work must be submitted before Friday, February 7, 2020 at 11:59 p.m. unless you are going to use a grace day.

All work must be done within assigned teams. You may discuss general concepts with your classmates, but it is never acceptable for you to look at another team's code. Please refer to the course policies if you have any questions about academic integrity. If you have trouble with the assignment, I am always available for assistance.

Grading

Your grade will be determined by the following categories, based largely on correctly computing four geometric values and four costs:

Category Weight
Method makeChoice() selects a legal card 50%
Method pickColor() selects a legal color 20%
Testing plan 10%
Coding style and comments, including Javadoc comments 20%

Code that does not compile will automatically score zero points.

Under no circumstances should any member of one group look at the code written by another group. Tools will be used to detect code similarity automatically.