12,949,858 members (95,968 online)
Add your own
alternative version

#### Stats

54.1K views
2.8K downloads
38 bookmarked
Posted 20 Jun 2009

# War Card Game Simulation in C#

, 22 Jun 2009 CPOL
 Rate this:
Please Sign up or sign in to vote.
Windows Forms Application using LINQ expressions and Dictionary objects to recreate a classic card game

## Introduction

(Note, in this article, the capitalized word ‘War’ refers to the game, while the lowercase word ‘war’ refers to a battle in which opponent’s card values are equal.)

Recently, I needed a method to generate data for a statistical analysis course I was taking. While playing the card game War with my young niece, it dawned on me; War was the perfect game simulation from which to generate all types of statistical data. What is the average number of battles it takes to complete the game? What are the odds a battle will result in war (a tie)? How about the odds of a double or triple war (a series of ties)? Are there ways to predict the outcome? How are the results influenced by varying the way rules of the game?

## Background

Doing some research on the web, it was clear I was not the first to undertake this endeavor. Due to the simplicity of War, there are an infinite number of computer game simulations available. In addition, there is a profusion of articles written about programming principals that can be applied to the construction of a game simulation such as War, and statistical analysis that can be done based on the game’s outcomes. Before creating the simulation, I limited my research to the rules of the card game, not the architecture of any existing simulations. I wanted the challenge of creating a working simulation.

War is what is known as a simple accumulating-type card game. The objective of the game is to acquire (win from the opponent) all the cards in a standard 52-card Anglo-American playing card deck. In War, each card generally has one of thirteen possible predetermined values – Two (deuce) through Ace. In war, the card’s suite — hearts, diamonds, spades, or clubs, have no impact on the cards value.

There are many variations of War. For this simulation, I choose the most basic 2-player variation of the game. Here are the rules:

1. Starting a standard 52-card deck, the deck is shuffled and the cards are dealt to each player. Both players receive 26 cards, face down.
2. The game consists of a series of ‘battles’ in which each player draws a single off the top of their deck and places it face up on the table. If both cards have different values, the player with the highest value card wins the battle. The winner of the battle takes all cards from the table and places them onto the bottom of their deck, in no particular order.
3. A battle in which both players lay down a card of equal value is referred to as ‘war’ (thus the name of the game). In this event, each player places two additional cards face-down on the table, followed by a third card face-up. The winner is then determined as before by higher-valued card. If perchance these cards are also identical (a ‘double war’), the process repeats itself until a winner is determined.
4. The player that eventually accumulates all 52 cards, leaving the opponent with no cards, is the winner. In the event of war, if a player doesn't have enough cards left for the war to commence (two cards down and one up), she/he looses the battle and therefore the game.

## Using the Code

This simulation is constructed as a simple Windows Forms Application, built in C# 3.0 using Visual Studio 2008. The UI consists of a single Windows Form, which displays the game’s state. In addition, there are a series of classes that contains the games logic. My primary focus was on the accuracy of the simulation, construction of the code, and extraction of data, not creating a visually rich GUI. Many enhancements to the user experience could certainly be made to increase the game’s entertainment factor.

The simulation allows the game to be played in two different modes, manual and automatic. You can choose to play the game manually, conducting each battle one at a time and seeing the results. Alternately, you can choose to have the program conduct all battles until a winner is determined.

#### Classes

The `abstract `base class, `CardGame`, contains generic fields, properties, and methods, which could be reused to create many types of card games. `CardGame `class contains methods for creating a new deck of 52 cards, shuffling a set of cards (see code below) and dealing cards to each player. Fields and properties in the base class include `Dictionary<> `objects to store the player’s cards, and player, battle, and game stats.

```protected Dictionary ShuffleCards(Dictionary cardsToShuffle)
{
protected Dictionary ShuffleCards(Dictionary cardsToShuffle)
{
Dictionary ShuffledCards = new Dictionary(cardsToShuffle);
Random RandomNumber = new Random();
ShuffledCards = ShuffledCards.Select(cards => cards)
.OrderBy(cards => RandomNumber.Next())
.ToDictionary(item => item.Key, item => item.Value);
return ShuffledCards;
}
}```

The `WarCardGame `class inherits the `CardGame `class, and adds additional fields, properties, and methods, specific to the game of War. `WarCardGame `class adds methods for starting a game, executing a battle (see code below), and determining the type of battles – single, double, etc. The `WarCardGame `class’s fields store values such as war tallies and number of cards to be placed face down in war. One might argue that more of the `WarCardGame `class’s fields, properties, and methods could be placed in the base class and overridden if necessary.

```public bool Battle()
{
//Fill temp decks with players cards
tempPlayer1Deck = Player1Deck.Select(cards => cards).ToDictionary(item =>
item.Key, item => item.Value);
tempPlayer2Deck = Player2Deck.Select(cards => cards).ToDictionary(item =>
item.Key, item => item.Value);

if (Player1Deck.Count() > 0) { Player1CardKey = Player1Deck.ElementAt(0).Key; }
if (Player2Deck.Count() > 0) { Player2CardKey = Player2Deck.ElementAt(0).Key; }

if (tempPlayer1Deck.Count() == 0)
{
StatusMessage = "Player 2 wins the game!";
OutcomeCode = 3;
return false;
}
else if (tempPlayer2Deck.Count() == 0)
{
StatusMessage = "Player 1 wins the game!";
OutcomeCode = 1;
return false;
}
else if (tempPlayer1Deck.Count() < (2 + cardsPlacedFaceDown) &&
tempPlayer1Deck.ElementAt(0).Value == tempPlayer2Deck.ElementAt(0).Value)
{
// Game ended in war (tied in battle)
// Player 1 doesn't have enough cards for a war
Player2TricksWon++;
TotalTricksWon++;
UpdateWarTotals(tempCardsOnTheTable.Count());
StatusMessage = "Player 2 wins the game! Game ended in a war.";
OutcomeCode = 4;
return false;
}
else if (tempPlayer2Deck.Count() < (2 + cardsPlacedFaceDown) &&
tempPlayer1Deck.ElementAt(0).Value == tempPlayer2Deck.ElementAt(0).Value)
{
Player1TricksWon++;
TotalTricksWon++;
UpdateWarTotals(tempCardsOnTheTable.Count());
StatusMessage = "Player 1 wins the game! Game ended in a war.";
OutcomeCode = 2;
return false;
}
else // Game hasn't ended so continue
{
if (CardsOnTheTable != null) // If last battle ended in war,
// collect cards left on table
{
tempCardsOnTheTable = CardsOnTheTable.Select(cards =>
cards).ToDictionary(item => item.Key, item => item.Value);
}

Player1CardValue = tempPlayer1Deck.ElementAt(0).Value;
Player2CardValue = tempPlayer2Deck.ElementAt(0).Value;

// Begin battle, each player lays down face-up card
tempCardsOnTheTable.Add(tempPlayer1Deck.ElementAt(0).Key,
tempPlayer1Deck.ElementAt(0).Value);
tempPlayer1Deck.Remove(tempPlayer1Deck.ElementAt(0).Key);
tempCardsOnTheTable.Add(tempPlayer2Deck.ElementAt(0).Key,
tempPlayer2Deck.ElementAt(0).Value);
tempPlayer2Deck.Remove(tempPlayer2Deck.ElementAt(0).Key);

// Randomizes order of card on table before they are placed
// back onto the bottom of the winning player's deck
tempCardsOnTheTable = ShuffleCards(tempCardsOnTheTable);

if (Player1CardValue > Player2CardValue)
{
StatusMessage = "Player 1 wins the battle.";
// Add cards on table to winning player's hand
var playerDeck1LINQ = tempPlayer1Deck.Select(cards =>
cards).Concat(tempCardsOnTheTable.Select(cards => cards));
tempPlayer1Deck = playerDeck1LINQ.ToDictionary(item =>
item.Key, item => item.Value);
Player1TricksWon++;
TotalTricksWon++;
UpdateWarTotals(tempCardsOnTheTable.Count());
tempCardsOnTheTable.Clear(); 	// There is a winner so clear
// the table of cards
}
else if (Player1CardValue < Player2CardValue)
{
StatusMessage = "Player 2 wins the battle.";
// Add cards on table to winning player's hand
var playerDeck2LINQ = tempPlayer2Deck.Select(cards =>
cards).Concat(tempCardsOnTheTable.Select(cards => cards));
tempPlayer2Deck = playerDeck2LINQ.ToDictionary(item =>
item.Key, item => item.Value);
Player2TricksWon++;
TotalTricksWon++;
UpdateWarTotals(tempCardsOnTheTable.Count());
tempCardsOnTheTable.Clear();
}
else if (Player1CardValue == Player2CardValue) // Players cards are
// of equal value - war
{
StatusMessage = "It's a tie. Time for war.";
// Lay card(s) face-down on table for war
for (int counter = 0; counter < cardsPlacedFaceDown; counter++)
{
tempCardsOnTheTable.Add(tempPlayer1Deck.ElementAt(0).Key,
tempPlayer1Deck.ElementAt(0).Value);
tempPlayer1Deck.Remove(tempPlayer1Deck.ElementAt(0).Key);
tempCardsOnTheTable.Add(tempPlayer2Deck.ElementAt(0).Key,
tempPlayer2Deck.ElementAt(0).Value);
tempPlayer2Deck.Remove(tempPlayer2Deck.ElementAt(0).Key);
}
}
// Battle over, reassign cards in correct locations
Player1Deck = tempPlayer1Deck.Select(cards =>
cards).ToDictionary(item => item.Key, item => item.Value);
Player2Deck = tempPlayer2Deck.Select(cards =>
cards).ToDictionary(item => item.Key, item => item.Value);
CardsOnTheTable = tempCardsOnTheTable.Select(cards =>
cards).ToDictionary(item => item.Key, item => item.Value);
return true; //Game is not over
}
}```

The instance of the `WarCardGame `object manages three sets of card during game play: Player One’s cards, Player Two’s cards, and the ‘cards on the table’ (the players have laid down during the battle). The card sets are stored in `Dictionary<string, int> `object fields. The `Dictionary`’s `KeyValuePair `elements represent individual cards. The `Key `represents the card as a `string`, for example the ‘9 ?’. The `Value `represents the value of the card as an integer, for example ‘9’.

During a battle, card sets are copied to one of three temporary Dictionaries using LINQ expressions (`System.LINQ `class). During the battle, cards are moved between the three temporary Dictionaries. After the battle, LINQ is used again to copy the cards back to the appropriate `Dictionary `fields. LINQ is also used to reverse the order of the cards and to concatenate the contents of Dictionaries. The temporary Dictionaries represent the state of the decks during the battle, while the original `Dictionary `fields represent the state of the decks prior to and proceeding a battle. I felt using this approach helped maintain the ‘state’ of the game, easier.

After each battle, and at the end of the game, the fields and properties that track the game’s status are updated to reflect the current game state — number on tricks, player wins, number of wars, etc. Statistics are returned to the UI from `DisplayResults()`. Contents of each deck are displayed as a comma-delimited `string `using `DisplayDeck(Dictionary< string, int >)`. The optional `DataToClipboard()` can be called to return the game’s statistics as a tab-delimited `string `for easy export to the clipboard and onto Excel for testing and statistic analysis.

## Points of Interest

#### Deck Weight

After completing the simulation, I read the PREDICTABILITY IN THE GAME OF WAR, an article by Jacob Haqq-Misra, which appeared online in Science Creative Quarterly. Reading Jocob’s article, I decided to add the deck weighting method he described to the simulation. The weight range for a player’s initial 26-card deck will be between +84 and -84. A deck with an ideal maximum weighting of +84 would have to contain only the highest values cards possible: 4 Aces, 4 Kings, 4 Queens, etc. down to and including 2 Eights. The weighting theory suggests that the higher a player’s deck weight, the higher the probability that player will win the game.

Interestingly (and logically), the opposite player’s deck (1/2 the original deck) will always have the inverse weight of the opposite player, since the weight of the entire 52-card deck is always 0 (zero). If Player One has a weight +25, Player Two’s deck will always have a weight of -25. This inverse rule is true even though the cards were shuffled, making each player’s decks totally random.

In the simulation, the `DeckWeight(Dictionary<string, int>) `method accepts a set of cards, to which it assigns individual cards, weights between -8 and +8, depending on the card’s face value (see code below). The `DeckWeight(Dictionary<string, int>) `method returns an integer representing the deck's weight — the sum of all the individual card’s weight.

```public int CalculateDeckWeight(Dictionary deckToWeigh)
{
int theDecksWeight = 0;
foreach (KeyValuePair kvp in deckToWeigh)
{
theDecksWeight += kvp.Value - 8;
}
return theDecksWeight;
}```

On a side note, in testing several thousand games, I was unable to achieve a player deck weight greater then +48. I theorize that the 52-card deck in the simulation is ‘shuffled’ in a manner that creates a greater randomized ordering of the cards than shuffling real cards by hand would achieve. Shuffling by hand is not as efficient in ensuring complete randomization the cards. The probability of a significantly higher- or lower-valued deck with hand-shuffling seems more likely.

#### Statistics

The next update of this program will contain the statistical results of 5,000 games played. Generating the data is quick and easy using the `GenerateGameData(int)` and `DataToClipboard()` methods included with the source code. It can be pasted in Excel and analyzed.

## History

• June 20, 2009 - version 1.0
• Initial version
• June 22, 2009 - version 1.1
• Fixed minor error in `UpdateWarTotals(int)` method
• Improved the way properties are reset after a game is complete. Effected `StartGame()` method
• Rewrote `ShuffleCards(Dictionary<string, int>)` and `DealCards(Dictionary<string, int>, int)` methods to take better advantage of LINQ

## License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

## About the Author

 Software Developer (Senior) Paychex United States
I am a senior software developer, architect, and project manager, specializing in .NET, JavaScript, Java, and database development, and build automation. I am currently a Lead Developer (.NET) / Developer IV for Paychex Enterprise Business Solutions. Paychex (PAYX) provides payroll, human resources, and benefits outsourcing and web-based solutions to business.

Prior to Paychex, I served as Lead Software Engineer, Operations Manager, and Technical Product Manager at Bio-Optronics. Bio-Optronics develops, deploys and operates information technology solutions to help healthcare professionals manage and optimize workflow to enhance quality, productivity, and patient and staff satisfaction and safety. Previously, I held positions of President, COO, Chief Technology Officer (CTO), and SVP of Technology for Lazer Incorporated. Lazer is a successful, digital imaging and Internet-based content management services provider.

## Comments and Discussions

 First Prev Next
 I mentioned this article in my recent writing and thought that you probably like to know w582811-Feb-12 12:29 w5828 11-Feb-12 12:29
 Re: I mentioned this article in my recent writing and thought that you probably like to know Gary Stafford24-Mar-12 1:41 Gary Stafford 24-Mar-12 1:41
 Interesting Member 46812978-Dec-09 10:55 Member 4681297 8-Dec-09 10:55
 My vote of 1 C#20101-Jul-09 6:35 C#2010 1-Jul-09 6:35
 It, seems not useful to me.
 Re: My vote of 1 C#20101-Jul-09 6:38 C#2010 1-Jul-09 6:38
 Re: My vote of 1 [modified] Gary Stafford1-Jul-09 7:20 Gary Stafford 1-Jul-09 7:20
 Last Visit: 31-Dec-99 18:00     Last Update: 25-May-17 9:44 Refresh 1

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170524.1 | Last Updated 22 Jun 2009
Article Copyright 2009 by Gary Stafford
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid