You are on page 1of 8

CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game

Lab Assignment 5
The War Card Game
Due Date
(a two-week LA)
Deadline:
Keep reading until the end!

Goal
 A well-designed program helps with software maintenance, meaning that when new
requirement is considered, a well-designed program should not be modified. To achieve
this goal, you need to use inheritance and associations in your design/program.

The War Card Game


Two players are playing a card game that uses a standard card deck where each card has one of
suits – spades (S), hearts (H), diamonds (D) or clubs (C) – and one of ranks – Ace (A), K, Q, J,
T, 9, 8, 7, 6, 5, 4, 3, 2. Each of them has at most 26 cards. Players do not look at their cards but
keep them in a packet face down. The objective of the game is to win all the cards initially
owned by two players. Both players play by turning their top cards face up and putting them on
the table. Whoever has the highest card takes both cards and adds them (face down) to the
bottom of their packet. Cards rank as usual from high to low: A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, 2.
Suits are ignored. (implemented by isLargerThan(…)). Both players then turn up their next card
and repeat. The game continues until one player wins by taking all the cards.

There are two card representation formats: One, called the user view/format, is given (suit, rank)
for each card, such as (clubs,5). The other format, called the internal format, is an integer
representation based on the following mapping: (Clubs,2),(Clubs,3),…,(Clubs,K)=(Clubs,13) ,
(Clubs,A)=(Clubs, 14) the number goes from 1,2,…, to 12, 13; (Diamonds,2),…,
(Diamonds,A)=(Diamonds, 14) go from 14 to 26; (Hearts,2),…,(Hearts,A) =(Hearts, 14) cards
go from 27 to 39; and (Spades,2),…,(Spades,A) =(Spades, 14) cards go from 40 to 52. This must
be noted when considering the following case for ties.

During a turn, if both players turn over cards of equal rank, the following process is used to
decide a winner. The face-up cards stay on the table. Both players play the next card of their pile
face down. Then, both players draw and turn over the next card of their deck face up. Whoever
has the highest of those two face-up cards wins the war and adds all six cards to the bottom of his
or her packet. The order of adding the six cards to the winner’s packet is based on the two top
cards on the table, the winner’s last card, and finally the loser’s last card. If the new face up cards
are equal as well, the play continues: each player puts another card face down, then puts one card
face up afterward. The order is the first player’s card followed by the second player’s card. The
CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game

process goes on like this if the opposing face up cards continue to be equal in rank. As soon as
they are different, the player with the highest card from the last draw wins all the cards on the
table. If someone runs out of cards in the middle of the process, the other player automatically
wins (implemented by solve(…) method).

Let’s take an example of one round. Suppose player 1 lays down (clubs, 4) on the table and
player 2 lays down (hearts, 4) on the table. When comparing cards, we ignore the suit and care
about rank, so player 2’s card is the same as player 1’s card and thus results in a tie. So, we have
in the current stage of the scenario:

Player 1: (clubs, 4) 
Player 2: (hearts, 4) 

Since we have a tie, both players must lay a card face down and then another face up. Suppose
after doing so, we have the following setup on the table:

Player 1: (clubs, 4)  (clubs, J)  (diamonds, 6)


Player 2: (hearts, 4)  (clubs, Q)  (hearts, 7)

Player 1’s top card is (diamonds, 6) and player 2’s card is (hearts, 7). Because player 2’s card has
a higher rank, player 2 breaks the tie and wins the round!

Now consider a different scenario when handling the first tie. Suppose instead we have the
following setup on the table.

Player 1: (clubs, 4)  (clubs, J)  (diamonds, 6)


Player 2: (hearts, 4)  (clubs, Q)  (hearts, 6)

Player 1’s top card is (diamonds, 6) again and this time player 2’s card is (hearts, 6)! We have
another tie! Here, we apply the same steps as we did with the first tie. Player 1 puts a card face
down on the table, then face up. Player 2 does the same. Now let’s consider this scenario. Now
we have on the table:

Player 1: (clubs, 4)  (clubs, J)  (diamonds, 6)  (spades, 2)  (diamonds, A)


Player 2: (hearts, 4)  (clubs, Q)  (hearts, 6)  (diamonds, K)  (spades, 10)

Player 1’s top card is (diamonds, A) and player 2’s card is (spades, 10). Because player 1’s card
now has the highest rank, player 1 breaks the tie and wins! If another tie occurs, the same process
happens again. This will continue until the tie is broken, or one/both players run(s) out of cards
to play on the table.

The cards are added to the back of the winner’s hand in the exact order they were dealt,
specifically player 1’s first card, player 2’s first card, player 1’s second card, etc. Your job is to
write a program to simulate this game, report the number of moves, and print out each move via
our provided methods/class.
CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game

Design Requirements
Here is the class diagram you need to use for this assignment. You are not allowed to add new
attributes in a class beyond the class shown in the following diagram. Note that you should hide
all attributes in your class with getter/setter methods. Each class should be saved in its own
individual .py file whose starting letter should be a lower case.

I. Solver class: An important feature is that this class SHOULD NOT BE MODIFIED
IN THE FUTURE NO MATTER HOW A CARD OR A DECK OF CARDS ARE
REPRESENTED.

a. solve(): A main method to play war between two players based on the problem
description. One additional requirement is you must print both player’s cards as
well as the cards on the table using the user view/format for each turn. The solve()
method returns (player_number, numberOfSteps). For instance, (1,30) denotes
player 1 wins the game in 30 steps. During each step, call the following method to
print out each step information:

self.print_result.print_step(c,self.user_1,self.user_2,self.table)

We provide the Print_Result class so the output will be uniform. Last, you should
use a local variable to set the number of plays like 100 to avoid an infinite loop.

b. player1 denotes the cards owned by player1

c. player2 denotes the cards owned by player2

d. table denotes all the cards on the table.


CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game

II. CardContainer is an abstract class. All methods should deal with Card objects.
a. addNewCards(lofc) creates an internal representation of a deck of cards, either
owned by player 1, player 2, or on the table, based on a list of cards given by the
user view/format. The internal representation is given by a subclass. Parameter
lofc is a list of cards in the user view/format.

III. CardCnterUsingList is a subclass of CardContainer in which you are asked to use a


linked list to represent a deck of cards. Namely, a linked list is used to denote a deck
of cards, where each node of the list represents a card and the data type of the node is
Card, as shown in the above class diagram. Further, your implementation should
follow the above class diagram. As a result, there is NO attribute called tail in the
CardCnterUsingList class, that allows to access the tail of the linked list, i.e., the last
card of a deck. In its implementation, you are asked to use a linked list to denote a
deck/list of cards. Each Node contains a Card as its data.
1. addNewCards(lofc): creates an internal representation of a deck of cards, either
owned by player 1, player 2, or on the table, based on a list of cards given by the
user format. Note that each card is internally represented by an integer, contained
in an object of IntCard. Parameter lofc is a list of cards in the user format.

IV. Class Card.


a. value is an attribute that denotes a card’s value in internal view representation. In
this version, an integer value is assigned to a card based on the aforementioned
mapping. Here is a copy of the text. “(Clubs,2),(Clubs,3),…,(Clubs,K),(Clubs,A)
the number goes from 1,2,…, to 12, 13; (Diamonds,2),…,(Diamonds,A) go from
14 to 26; (Hearts,2),…,(Hearts,A) cards go from 27 to 39; and (Spades,2),…,
(Spades,A) cards go from 40 to 52.”
b. isLargerThan(c ) compares the current card with the c parameter by their ranks
extracted from their __value attributes in internal format, when implemented
since ranks must be compared (Note: this method is abstract in Card and must be
implemented in a child class)..

V. Class IntCard
a. This class gives an internal representation of a card based on the previous
description. Note that an integer, just like one of those shown at the end of a line
below is what an object of IntCard stores as a value.
(Clubs,2) => 1
(Clubs,3) => 2
(Clubs,4) => 3

(Clubs,Q) => 11
(Clubs,K)=(Clubs,13)=>12
CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game


(Diamonds,2) => 14
(Diamonds,3) => 15

(Diamonds,10) => 22
(Diamonds,J) => 23
...
(Hearts,2) => 27
(Hearts,3) => 28

(Hearts,9) => 34
(Hearts,10) => 35
..
(Spades,3) => 41

(Spades,10) => 48
(Spades,J) => 49
(Spades,Q) => 50
(Spades,K) => 51
(Spades,A) =(Spades, 14) => 52

Testing Phase
Here is a sample program to test your program:

Example:

class Main:

def read_file_input(player_1, player_2, f_name):


try:
with open(f_name) as my_file:
all_cards = my_file.readlines()
p1_lofc = []
p2_lofc = []
for line in all_cards:
p1_lofc.append((line.split(" ")[0].split(",")[0], line.split(" ")[0].split(",")[1]))
p2_lofc.append((line.split(" ")[1].split(",")[0], line.split("
")[1].split(",")[1].strip("\n")))
player_1.add_new_cards(p1_lofc)
player_2.add_new_cards(p2_lofc)
except:
print("File not found")
sys.exit(1)
CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game

def main(self):

file_name = input("Please enter the input file for player1 an player2 cards to play War\n")

p1 = CardsCnterUsingList()
p2 = CardsCnterUsingList()

read_file_input(p1, p2, file_name)

table = CardsCnterUsingList()

solver = Solver()
solver.set_player1(p1)
solver.set_player2(p2)
solver.set_table(table)
result = PrintResult()
solver.set_print_result(result)
res, num_moves = solver.solve()
result.set_res(res)
result.set_num_moves(num_moves)
print("Game Over!\n")
result.print_output()

Input for your players cards will be given by a text file, which is formatted such that each line
contains two tuples: 1 for player 1 and the second for player 2. Each tuple consists of the name of
a suit and the name of a card, separated by a comma. The tuples for player 1 and player 2 are
separated by a space, so input parsing is made easy. An example input file input3.txt appears as
follows:

Clubs,K Hearts,5
Diamonds,3 Diamonds,6
Hearts,10 Clubs,8
Spades,7 Diamonds,2

These items will be in the list of user view cards that you need to convert when adding them to
your players’ cards lists.

With this input3.txt, output should look as follows when running the program:

Please enter the input file for player1 an player2 cards to play War
input3.txt

from Print_Result After move: 0


User 1: [('Clubs', 'K'), ('Diamonds', '3'), ('Hearts', '10'), ('Spades', '7')]
User 2: [('Hearts', '5'), ('Diamonds', '6'), ('Clubs', '8'), ('Diamonds', '2')]
Table : []
from Print_Result After move: 1
CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game

User 1: [('Diamonds', '3'), ('Hearts', '10'), ('Spades', '7'), ('Clubs', 'K'), ('Hearts', '5')]
User 2: [('Diamonds', '6'), ('Clubs', '8'), ('Diamonds', '2')]
Table : []

from Print_Result After move: 2


User 1: [('Hearts', '10'), ('Spades', '7'), ('Clubs', 'K'), ('Hearts', '5')]
User 2: [('Clubs', '8'), ('Diamonds', '2'), ('Diamonds', '3'), ('Diamonds', '6')]
Table : []

from Print_Result After move: 3


User 1: [('Spades', '7'), ('Clubs', 'K'), ('Hearts', '5'), ('Hearts', '10'), ('Clubs', '8')]
User 2: [('Diamonds', '2'), ('Diamonds', '3'), ('Diamonds', '6')]
Table : []

from Print_Result After move: 4


User 1: [('Clubs', 'K'), ('Hearts', '5'), ('Hearts', '10'), ('Clubs', '8'), ('Spades', '7'), ('Diamonds', '2')]
User 2: [('Diamonds', '3'), ('Diamonds', '6')]
Table : []

from Print_Result After move: 5


User 1: [('Hearts', '5'), ('Hearts', '10'), ('Clubs', '8'), ('Spades', '7'), ('Diamonds', '2'), ('Clubs', 'K'),
('Diamonds', '3')]
User 2: [('Diamonds', '6')]
Table : []

from Print_Result After move: 6


User 1: [('Hearts', '10'), ('Clubs', '8'), ('Spades', '7'), ('Diamonds', '2'), ('Clubs', 'K'), ('Diamonds',
'3')]
User 2: [('Hearts', '5'), ('Diamonds', '6')]
Table : []

from Print_Result After move: 7


User 1: [('Clubs', '8'), ('Spades', '7'), ('Diamonds', '2'), ('Clubs', 'K'), ('Diamonds', '3'), ('Hearts',
'10'), ('Hearts', '5')]
User 2: [('Diamonds', '6')]
Table : []

Game Over!

The Result is:


CS / CYCS 1120 (Python) - Spring 2023 PA5 The War Card Game

Player 1 wins!
Number of moves played is -> 8

Process finished with exit code 0

Additional Requirements
Coding Standards

You must adhere to all conventions in the CS 1120 Python coding standards (discussed in class
and in the Lab). This includes the use of white spaces and indentations for readability, and the
use of comments to explain the meaning of various methods and attributes. Be sure to follow the
conventions also for naming classes, variables, method parameters and methods.

Assignment Submission

 Generate a .zip file that contains all your files including:


o Program Files
o Any input or output files
o Flowchart.

 Submit the .zip file to the appropriate folder on ELearning.

You might also like