Writing a web-based poker game in Java


Writing a web-based poker game in Java

In this entry I’m going to decompose an application I have written using Java 5, JSP 2.0, and a little bit of Hibernate. I’ll try and track all the work done including the changes as I went along by using my trusty source control system.

Update 5/21/2005:

A friend of mine, Peter Seibel, of Practical Common Lisp fame, noticed a bug. Basically I wasn’t properly disambiguating hands that were both Flushes and either a Full House or Four of a Kind. I fixed the bug, though it never comes up in a 7 card game. In order to have 5 cards that are the same suit, you need 5 unique ranks, with 7 total cards the best you could do would be three of a kind in addition to a flush. I think I remember thinking about this at the time and treating it as an optimization, but the poker engine should be more general and handle this case. I can retain the optimization by just checking that there are 7 or fewer cards after I find the Flush.

Introduction

The application I’m going to discuss is an online game I created. It is essentially my own invented version of video poker but using Texas Hold’em as its base rather than 5-card Draw. I’m calling it Texas Solitaire. The idea for the game was entirely developed using real cards and chips rather than a whiteboard. I played hundreds of hands using several different variants before settling on the rules that were implemented in the first version of the game. Additionally, after getting some feedback from friends and family, I tweaked the electronic version to get what is deployed now.

The Game

The rules of the game started out as follows:

1) You begin with 4 2-card poker hands. You get $4 total to distribute bets among the various hands in integer increments. You must bet the entire $4.
2) The “flop” is dealt. 3 more cards, face up. You now must bet another $4 among the hands that you bet on in stage 1.
3) The “turn” is dealt. 1 more card, face up. You now must bet another $4 among the hands that you be t on in stage 1.
4) The “river” is dealt. The best 5-card poker hand for each player hand is determined. In the case of tie they all are considered winners. You win an equal amount that you bet for each winning hand and you lose the money you bet on losing hands.

Architecture

After vetting the game itself, I then laid out what I thought the architecture should look like based on my previous experience building web applications. I also took into account that there may be other poker games built on the poker engine and other interfaces built for the game so I tried to make sure that concerns were separated. Here was my first (and as it turns out, final) cut at the architecture:

This should give us the flexibility we need in the future if we decide to make a new Game or design a new user interface as long as the separation between the View, Model, and Controller are done properly. Being a bottom-up kind of developer I decided to go after the poker engine first. The first thing we need to do is look at the requirements and decide the easiest way to build it. I chose to use Java 5 for this project since looking at the requirements I was going to have a lot of collections of domain objects that would need to be type-safe and I would probably want to refactor the engine later once I understand how to optimize it. One of the things I was worried about was ensuring that the engine would scale down to small devices so it would have to eventually be very efficient. By choosing a nice refactorable language I could build in a little bit of protection as I didn’t want to prematurely optimize and lose flexibility. My Java library development tool of choice is IntelliJ IDEA 4.5.4. It’s a very productive environment and gives me easy access to refactoring, unit testing, code analysis, an ant-based build, and good SCM support. For this particular thing I could have probably also used Eclipse, but since I also knew that I would use IDEA for the interface part of the project, it gets the nod.

Objects

Let’s begin where most projects begin, the domain object model. We are going to need some software representations of the real world artifacts in the game:

I’ve called out Hand Rank separately here to show that it is not a physical domain object but rather an concept that you must make concrete in order to understand and evaluate the relationships between hands. One might argue it doesn’t belong in the domain object model at all, but I thought I would put it here to point out its dependency on the board and the players hand. As it turns out, Hand Rank is the heart of the poker engine and nearly all the programming effort will be going into implementing that class. The core pieces of the domain model are now defined. When we implement them we will need each of them to be able to print themselves out so we should implement toString(). Additionally, Cards will need to be compared often, so they should implement Comparable. The Deck itself should have be able to deal, burn, and shuffles the cards. Any Hand should be combinable with the Board in order to get a Hand Rank. Hand Ranks should be comparable to one another and immutable.

The next thing on the agenda will be the design of the Texas Solitaire Game model. As it has definite turns, actions, and events I decided to implement it as a simple state machine that can then be driven from a Game Controller. I didn’t make it too limiting though since I know that I may want to change it later. It ends up having a number of verbs that correspond to the actions in the game like firstBet(), addBet(), flop(), turn(), river(), and resolve(). It keeps a lot of state about the game that the View can retrieve if it needs to but in general remains hidden.

Implementation

Now we get down to the nitty-gritty of implementation. Up front I decide to use JUnit and EMMA to track my progress. It took me about 2 hours to implement the Poker Engine layer and the Texas Solitaire Game model along with a set of tests and decent coverage. I’m pretty sure there are still bugs in the Hand Rank algorithm at this point, but I decide to move forward to the Game Controller and View so I can start to get a feel for how it is turning out. Here is the state of the world as I start the interface side of the game:

[junit] Running com.sampullara.poker.games.TexasSolitaireModelTest     [junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.366 sec     [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.036 sec     [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.049 sec

OVERALL COVERAGE SUMMARY

nameclass, %method, %block, %line, %all classes100% (11/11)84% (48/57)94% (1740/1858)94% (306.5/326)

OVERALL STATS SUMMARY

total packages:2total executable files:6total classes:11total methods:57total executable lines:326

COVERAGE BREAKDOWN BY PACKAGE

nameclass, %method, %block, %line, %com.sampullara.poker100% (9/9)80% (32/40)93% (1442/1547)94% (248.6/265)com.sampullara.poker.games100% (2/2)94% (16/17)96% (298/311)95% (57.9/61)
Code Coverage

Much of the missing coverage are accessors and toString() methods though there are some missing pieces in Hand Rank that we will have to return to later in order to ensure the correct evaluation of hands. For instance, if I sync the tests to the current set but leave the initial code that I checked into the system in place, we get these results:

[junit] Tests run: 10, Failures: 3, Errors: 0, Time elapsed: 0.244 sec     [junit] TEST com.sampullara.poker.HandRankTest FAILED     [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.017 sec     [junit] Tests FAILED

OVERALL COVERAGE SUMMARY

nameclass, %method, %block, %line, %all classes100% (11/11)84% (48/57)94% (1745/1858)94% (307.1/326)

OVERALL STATS SUMMARY

total packages:2total executable files:6total classes:11total methods:57total executable lines:326

COVERAGE BREAKDOWN BY PACKAGE

nameclass, %method, %block, %line, %com.sampullara.poker100% (9/9)80% (32/40)94% (1447/1547)94% (249.2/265)com.sampullara.poker.games100% (2/2)94% (16/17)96% (298/311)95% (57.9/61)
In this case we only cover 5 more blocks of code but expose 3 new bugs that we didn’t see before. Systematic increases in code coverage would have led me to fix those bugs. Additionally, we’ll see later where I have refactored the code in order to increase its testability. Before we move onto the View and Controller part of the game, lets grab a snapshot of the current setup:

Texas Solitaire v0.1 texassolitaire198.zip

User Interface

Now on to the user interface. I’m somewhat of a purest when it comes to using Java software so my web framework of choice is currently JSP 2.0. The newest version of JSP improves upon its previous implementations in a number of crucial ways:

1) You no longer need to have taglib and other declarations in every page, you can instead use include-prelude for you JSP pages
2) You can now define tag libraries in JSP rather than only in Java
3) You can restrict the use of scriptlets within your web application
4) The standard JSP tag library includes many common tags that you will not have to write yourself

The other nicety is that IDEA comes bundled with very good core JSP support along with the XML support required in order to edit XML descriptors without the documentation sitting front of you. Also, the edit/refresh development model of JSP lets you get work done rapidly. Another nice thing is that you don’t need tons of libraries in order to support JSP 2.0 development on your favorite application server which reduces your application complexity by quite a bit. The initial cut at the web application was pretty rough. I basically used a JSP as my controller and a set of game state based JSPs as my view of the game. This let me quickly get it up and running and using IDEA I knew it would be pretty easy to refactor it later into a form I could be proud of and maintainable. This initial version also didn’t have the any persistence mechanism for scores, users, etc. It was designed purely as a way to exercise the poker engine under real life conditions. Its dependency on the poker engine itself was done by including the built .jar file from the Texas Solitaire project within the WEB-INF/lib directory of the web application. This separation between the two parts of the system increases the maintainability of the system by quite a bit. Here is the initial version that I was using after about an hour of work:

Web Texas Solitaire v0.1 webtexassolitaire.zip

Going 1.0

There are many weaknesses in this web application that I then set out to fix. Here are the things that I did over time that made it much better:

1) Added a persistent high score list with users and logins using Hibernate 3.0 with Annotations
2) Refactored the JSP controller code into Filters using a ‘code behind’ type mechanism for the pages
3) Used a ServletContextListener to inject dependencies into the Controller
4) Removed a number of game exploits

I iterated pretty quickly on the system after getting it up and running and actually went through a couple of different methods for the Controller and persistence. I settled on the ‘code behind’ model because it was very much in tune with my clear separation of concerns and I went with Hibernate because of its flexibility and ease of use. For instance, all the persistence code is within either Annotations or is completely abstracted out of the interfaces used by the Controller. In fact, if the database is not present, the game will still run using an in memory implementation of the list of users. Here is the current version of the game:

Web Texas Solitaire v1.0 webtexassolitaire-1.0.zip

After the game was released a couple of new bugs came to light that basically went back to where my testing was still weak. I’ve fixed all the reported bugs and have added regression tests for them. Not much besides Hand Rank has changed in the engine since I finished writing it. I refactored it a bit and optimized it somewhat so it wasn’t doing the same calculations twice. Much of the near duplicate code has been removed as well.

Texas Solitaire Engine v1.0 texassolitaire-1.0.zip

Here is the final JUnit and EMMA report:

[junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.222 sec     [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.013 sec     [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.027 sec

OVERALL COVERAGE SUMMARY

nameclass, %method, %block, %line, %all classes100% (10/10)88% (56/64)96% (1744/1823)97% (315.9/326)

OVERALL STATS SUMMARY

total packages:2total executable files:6total classes:10total methods:64total executable lines:326

COVERAGE BREAKDOWN BY PACKAGE

nameclass, %method, %block, %line, %com.sampullara.poker100% (8/8)85% (40/47)95% (1450/1522)97% (259/268)com.sampullara.poker.games100% (2/2)94% (16/17)98% (294/301)98% (56.9/58)
Interestingly, I found a bug in EMMA using this codebase. There is a line in the source that it claims is not covered but it is. Have to get that fixed!

Conclusion

I hope you enjoyed this exposition of the building of a Java based web application from start to finish. Basically, I wanted to show people that you can write really nice JSP based applications without depending heavily on 3rd party frameworks. Additionally I wanted people to see some of the new features in Java 5 in use like generics, enumerations, and annotations. Also, the Hibernate usage within this application mirrors very closely what you will see in EJB 3. Also, being able to run this application within a simple container like Tomcat 5.5.9 is also nice.