From Scratch

As it stood, the game was an interesting example, but the single player experience gets boring after a while - the game had made no money, and asking for more assets was a hard call - everything I wanted to add had to be made with the assets I had, until I could generate some revenue and push out with creative content, rather than mechanical. The obvious choice was some multiplayer functionality.

I started the game from scratch, by writing a series of tests. Poker is a very large problem, more-so than chess, and Poker Kingdoms extends the problem size significantly. But it’s made of some very basic things, and which fit very well into OOP design.

A card can be defined with a couple of ENUMs, and a Bool (Face, Suit, and Flipped). A deck of cards is no more than a first in, last out queue of cards. The playboard is a 2D array of cards, and a selection, is a set of positions on that board.

This design leaves 90% of the code in the Hand class, a Hand is any set of 5 cards. Defining a hand is a monumental task. The Hand logic runs through hand types in descending order of strength, and calls wrapper functions like IsStraightFlush() which in turn tests IsStraight() and IsFlush(). There’s a bit of weird logic in IsStraight()because you can have an Ace, 2, 3, 4, 5 straight, or you can have a Ten, Jack, Queen, King, Ace straight so you can’t just test for congruent numbers.

This is where Test Driven Development really shone - I could write a series of tests and make sure my code always evaluated them as the correct hands.

[Test]
[Category("Straight")]
public void AceHighStraight() {
Hand hand = new Hand();

hand.Add(new Card(Suits.HEARTS, Faces.JACK));
hand.Add(new Card(Suits.SPADES, Faces.TEN));
hand.Add(new Card(Suits.CLUBS, Faces.KING));
hand.Add(new Card(Suits.DIAMONDS, Faces.QUEEN));
hand.Add(new Card(Suits.SPADES, Faces.ACE));

Assert.AreEqual(
Hands.STRAIGHT,
hand.GetHand()
);
}

These tests are very quick to write, because I’ve built with the idea of testing from scratch, there are no magic numbers, and everything is human readable.

Because I’ve nested my hand detection, a problem in IsStraight() will break IsStraightFlush(), and IsRoyalFlush(), but the fix will only be in one place. This is important, because I added the concept of Jokers Wild. Because of the implementation, any number of cards could be a joker, and the hand eval would have to work, this increases the logic paths significantly, detecting a straight when there are some number of missing cards is tricky, a Full House (3 & 2 matching faces) is also not trivial, especially when you start working with 4 jokers.

Again, a series of tests;

[Test]
[Category("Straight")]
public void TwoJokerStraight() {
Hand hand = new Hand();

hand.Add(new Card(Suits.SPADES, Faces.TWO));
hand.Add(new Card(Suits.HEARTS, Faces.THREE));
hand.Add(new Card(Suits.DIAMONDS, Faces.FOUR));
hand.Add(new Card(Suits.SPECIAL, Faces.JOKER));
hand.Add(new Card(Suits.SPECIAL, Faces.JOKER));

Assert.AreEqual(
Hands.STRAIGHT,
hand.GetHand()
);
}

and massage the code until it all falls into place - sometimes you’ll see an edge case bug, rather than fix the bug directly, build a test to support it, and then fix it. For instance, you cannot have a Straight with more than 2 Jokers, because 4 of a kind is stronger, and once you have 3 jokers, you can always make 4 of a kind. The astute will notice that these tests are incomplete, I test what hand I’m returned, but I don’t test if the hand evaluates correctly. 3 jokers should evaluate “IsStraight()” but not return that hand when tested.

I rebuilt the entire logic of Poker Kingdoms with this mind-set, but also completely in pure c# outside of Unity3d or any other framework.