One of first steps I do when designing a game is code a model to test the mechanics. In part 1 we will code a model for a simple game and use that model to identify a problem. The value of writing a model is two fold you can find some hard to find problems (Dead Locks, Starvation, and infinite loops) but it also forces you to reduce the concepts of a game to pure mechanics. We're going to use a dirt simple game we all know to avoid having to learn a new game while learning to code. So were going to code a model for war.
Rules
The Base Line Code
So this example is based in Java, Maven and JUnit but by no means is that the only choice, its just one flavor. When writing a simulation start with two categories of items(Components and Concepts):
Components
Components are items that will ultimately be physical parts, collections of physical parts or a location in the game. To simulate War we only needed two:
Card
A card is the most basic element and has two values, rank (2,3,4,5,6,7,8,9,10,J,Q,K,A) and Suit(spade,club,heart,diamond). If you really boil it down, you don’t need suit for the base game of war but it is an element of the card. In java the use of an enum for the suit has performance benefits when comparing but you could use a string or a simple char just as well.
Deck
A deck is a simple collection of cards. The only important thing you need to decide on is does order of the collection matter. In this case it does so ArrayList is one option but a Queue might be more apt and you would have to less direct index manipulation.
Concepts
Concepts are any other part of the game and mechanics parts that have to make decisions or carry a larger state changes. Concepts should be light on data elements big on methods.
The base line simulation has two.
Player
The player has their own deck (deck being a collection of cards not a full deck) and a name so we tell them apart.
accept(Card) – take a card and add it to your deck
accept(Deck) – take a full deck and add it to your own
accept(List<Card>) – take a collection of a cards and add it your deck
topCard() – give me your top card
topCard(int) – give me your top x cards
Game
The game class represents one full play through. We have a list of players and some variables were going to use to count events
addPlayer(Player) – add a player to the game
deal() – build the main deck and distribute it o the players
isGameOver() – return true if the game has ended
playOneRound() – starts the playing of one round of the game
playOneRound(List<Card>) – recursive function because war can stack
findHighCardPlayer(Map<Player, Card>) – look at the cards played and find the winner
Exceptions
Exceptions are great for simulating and tracking events. Two advantages is they are easy to catch and record and if you throw one from a method the code will force you to deal with all the points it touches. You may see interactions you didn’t consider.
WarException
Thrown if the ranks of two cards match. Then when caught we go down a different path of logic for that situation
BoredomException
Always include this type of exception, it always pays dividends in the analysis, in this case it will be the crux of the problem. if the round count goes over 1000 throw this exception, basically representing players getting feed up with game and quitting.
OutOfCardsException
Thrown when a player draws there top card but is out of cards, this signifies them losing and the catch logic ends the game
Putting the elements in motion
JUnits give us a gram work to write test for our code. They are already designed to exercise cod and that's all we need to do now that we have the classes built.
The Single game test:
Run this test a lot before going on to the next test. Because randomization(pseudo randomization, sorry computers can’t really be random) you won’t find everything on the first pass.
The structure is very simple.
Instantiate a game
add two players
deal the cards
loop until the game ends – every 50 rounds print the status of the game
output the details
Output:
Ten Thousand Game Test
So our single player game is going well we have run it like 50 times, lets scale it up so we can get some real data, good data needs a large sample size.
Output:
Conclusion
If nearly half the games played ended with players quitting, that's a pretty boreing game. When testing for this kind of condition you have to decide on your own threshold. Its nice if you can eliminate the condition completely but if 10 games in 10,000 show it that's not statistically significant. So in part two well try adding some house rules to fix war.
Comments