Programming Assignment 11: War

Estimated reading time: 15 minutes
Estimated time to complete: 90–120 minutes (plus debugging time)
Prerequisites: Lab 05
Starter code: war-student.zip
Collaboration: not permitted

Overview

War, huh, yeah! What is it good for? Absolutely nothin’! Uh-huh!
— Whitfield and Strong

…except to serve as the basis of a 190D programming assignment, say it again, uh huh!
— Liberatore

War is a two player children’s card game, usually played with a standard 52-card deck of playing cards. It’s also a great toy problem for various applications. In this programming assignment, you’ll implement a simulation of a version of War based upon a textual description.

This version of War is deterministic, that is, there is no randomness involved. The winner can be determined entirely from the starting deck. Simulations (also known as computer models) are a way to find the outcomes of both deterministic and non-deterministic situations. Non-deterministic simulations are usually run many times, and the results are aggregated in some way (for example, to find the most likely outcome, or the median outcome, or whatnot).

Unlike previous assignments, we’ve not provided a large set of unit tests to help with automated testing or your design process. You will need to translate the relatively simple rules of the game from English text into Java code, to decide if and how to break the program up into methods, and to write your own tests. Or, you know, caveman debug and trial-and-error code until it works or you give up.

The Gradescope autograder includes only a few tests of your program’s overall correctness. We have left the names of these tests visible as a hint to what they’re testing, but you will likely need to write some tests of your own to complete this assignment.

Note that if you run into trouble with the Eclipse debugger mysteriously quitting during unit tests, it’s due to the timeout rule that we use to catch infinite loops:

@Rule
public Timeout globalTimeout = Timeout.seconds(10); // 10 seconds

Comment out the above two lines in all test files, and the debugger will no longer exit (and test cases can now get stuck in infinite loops).

Goals

  • Build a program to simulate the War card game, as described in English text.
  • Practice choosing and using relevant data structures for this program.
  • Practice breaking up portions of the program into separate methods, as needed.
  • Practice writing unit tests.
  • Test code using unit tests.

Downloading and importing the starter code

As in previous assignments, download and save (but do not decompress) the provided archive file containing the starter code. Then import it into Eclipse in the same way; you should end up with a war-student project in the “Project Explorer”.

The Game of War

If you haven’t played the game of War before, you might want to read the Wikipedia article describing it and find a classmate to play it with a few times, just to get a sense of how it works. But as noted in the article, there are many variants, so you’ll also want to read on to make sure you implement it exactly as described here.

An informal overview

The game is played by dealing the deck to two players. Each player plays a card; these two cards are compared in a “battle.” The higher card (by value, or rank: suits are irrelevant) wins the battle, and the player of the winning card takes both cards (the “spoils” of war), adding them to the bottom of their deck.

In case of tie, declare “war.” Each player deals out three more cards as additional spoils of war. Then each player plays another card. The winner of this battle takes all 10 cards (or more, in case of repeated ties).

Repeat this process until one player runs out of cards and is declared the loser.

A more formal description, with examples

Cards are represented as Integer values. In a classical game of War, the values range from 2 to 14, but your implementation should work with any value Integer and any non-negative number of cards.

Dealing out the cards

The initial “deck” of cards is provided as a List<Integer>. This deck must be dealt to the two players, A and B. Dealing the deck out entails giving alternating cards to each player, that is, the first card to player A, the second to player B, the third to player A, and so on. Each player accumulates cards into their own deck in this order.

For example, if the initial deck contains the values [2, 3, 4, 5] and we deal out all the cards, player A’s deck would then contain [2, 4] and player B’s deck would contain [3, 5]. The “top” of each of these decks is the left-hand side, that is, player A’s next card they would play would be 2.

Battling

Players then engage in a series of battle. To start a battle, they each take the top card off of their deck and place them into a temporary “spoils” pile, in the order A’s card, then B’s card. If either player has no cards in their deck, they lose (and the game is over). If both players have no cards, the game is a draw (and the game is over).

If one card’s value is numerically greater than than the other, then the player who played that card wins. The two cards in the spoils pile are added to the bottom of the winner’s deck (and the spoils pile is emptied as a result). When there are only two cards, Player A’s card always comes before player B’s card, regardless of which player won the battle.

Continuing our earlier example, to start the first battle, player A would play a 2, and player B would play a 3. Their decks would then consist of [4] and [5], respectively, until the battle is decided. Player B wins this battle, and so would add the two cards, in the specified order, to their deck. Player A’s deck is still [4], and player B’s deck is now [5, 2, 3]. They play the next battle, B wins again, and A’s deck is now empty. At the start of the next battle, A would be out of cards and declared the loser of the battle.

War

If a battle is between two cards of the same value, then a “war” is declared. Player A must add three more cards, in order, to the spoils pile, and then B must do the same. The players then battle again. Note that before this second battle commences, the spoils pile contains eight cards: the two from the first battle, and the six from the war. If a player wins this second battle, they collect all ten cards (the previous eight, plus the two from this battle), in order, into the bottom of their deck. If there is another war, then there will be 16 cards in the spoils pile before the third battle (the two from the first battle, the six from the first war, the two from the second battle, and the six from the second war) and a total of 18 cards awarded to the winner of the third battle. Wars continue until a player wins a battle, or the game ends due to one or both players running out of cards.

For example, suppose that player A’s deck contained [2, 8, 9, 10, 5, 4] and player B’s deck contained [2, 6, 7, 11, 6, 4].

First, each player would play their top card, both of which are 2. So the spoils pile would contain [2, 2] and a war would occur. Player A would add the top three cards from their deck to the spoils pile, and then player B would do so. The spoils pile now contains [2, 2, 8, 9, 10, 6, 7, 11]. Player A’s deck contains [5, 4] and player B’s deck contains [6, 4].

Time for the next battle. Both players remove the top card from their deck and add it to the spoils pile, which now contains [2, 2, 8, 9, 10, 6, 7, 11, 5, 6]. Player B wins this battle (5 vs 6), and so adds the entire spoils pile to their deck. Player A’s deck is now just [4], and player B’s deck is now [4, 2, 2, 8, 9, 10, 6, 7, 11, 5, 6].

The next battle causes another war, but player A will run out of cards and thus lose.

Remember, players add their cards in order, so even if both might run out of cards during the same war, A will run out first, and thus lose. Consider the example of A’s deck containing [5, 2, 3] and B’s containing [5, 4]. Even though A has more cards than B, A will run out of cards while adding three to the spoils pile and thus lose.

Technical Draws

If a game of war goes on for 1,000 battles and neither player wins, it is declared a draw. We don’t have all day here!

What to do

Implement the method in War.java. You’re welcome (and in fact, encouraged) to add other methods as needed to this class. You can also add other classes to the src/ directory, though likely you won’t need to do so.

Other notes

Organizing and debugging your code

Note that findWinner is a static method! You can complete this assignment using only static methods, or you might want to create an object. Very important: static variables are not re-initialized between tests. So, for example, if your class contains a line like:

static List<Integer> deck = new ArrayList<>();

…then the deck will only be empty the first time a test is run. After that, it will keep whatever value is stored in it. This problem will manifest as having your tests work in isolation, but fail when they are all run together. Prevent it by making sure you initialize static variables within static methods as needed. Or work with instance variables and methods by defining a War or other object. (If you also want to define a War (or other) object, you’ll need to define instance methods and/or instance variables, instantiate the object, and use it appropriately.)

You could, of course, stuff all of your simulation code into the one findWinner method. But we strongly suggest breaking it up into sections that you can test separately. This suggestion is, in actuality, a requirement if you expect us to help you debug – if you can’t figure out what your 150-line wall-of-code is doing, why would Garrett or I want to try to do so?

Testing your code

I strongly suggest you write additional unit tests to help you incrementally build up to the correct solution. Here are some simple cases (“smoke tests”) I’d consider; to get you started, the first four are already in the set of tests we’ve provided.

  • An empty starting deck.
  • A starting deck with one card.
  • A starting deck with two cards, first card greater value.
  • A starting deck with two cards, second card greater value.
  • A starting deck with two cards, equal values.
  • Starting decks that result in the two examples given in the assignment writeup.
  • A modified version of the above where the other player wins.
  • A starting deck that results in a technical draw.
  • A starting deck that results in a draw during a war.
  • A starting deck that results in a battle → war → battle sequence containing at least three wars.

Submitting the assignment

When you have completed the changes to your code, you should export an archive file containing the src/ directory from your Java project. To do this, follow the same steps as from Assignment 01 to produce a .zip file, and upload it to Gradescope.

Remember, you can resubmit the assignment as many times as you want, until the deadline. If it turns out you missed something and your code doesn’t pass 100% of the tests, you can keep working until it does.