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.IntroductionThe
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
GameThe 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.ArchitectureAfter
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.ObjectsLet'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.ImplementationNow
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| name | class, % | method, % | block, % | line, % |
|---|
| all classes | 100% (11/11) | 84% (48/57) | 94% (1740/1858) | 94% (306.5/326) |
OVERALL STATS SUMMARY| total packages: | 2 | | total executable files: | 6 | | total classes: | 11 | | total methods: | 57 | | total executable lines: | 326 |
COVERAGE BREAKDOWN BY PACKAGECode
CoverageMuch 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| name | class, % | method, % | block, % | line, % |
|---|
| all classes | 100% (11/11) | 84% (48/57) | 94% (1745/1858) | 94% (307.1/326) |
OVERALL STATS SUMMARY| total packages: | 2 | | total executable files: | 6 | | total classes: | 11 | | total methods: | 57 | | total executable lines: | 326 |
COVERAGE BREAKDOWN BY PACKAGEIn
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.zipUser
InterfaceNow 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 pages2) You can now define tag
libraries in JSP rather than only in Java3)
You can restrict the use of scriptlets within your web
application4) The standard JSP tag library
includes many common tags that you will not have to write
yourselfThe 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.zipGoing
1.0There 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
Annotations2) Refactored the JSP controller
code into Filters using a 'code behind' type mechanism for the
pages3) Used a ServletContextListener to
inject dependencies into the Controller4)
Removed a number of game exploitsI
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.zipAfter
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.zipHere
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| name | class, % | method, % | block, % | line, % |
|---|
| all classes | 100% (10/10) | 88% (56/64) | 96% (1744/1823) | 97% (315.9/326) |
OVERALL STATS SUMMARY| total packages: | 2 | | total executable files: | 6 | | total classes: | 10 | | total methods: | 64 | | total executable lines: | 326 |
COVERAGE BREAKDOWN BY PACKAGEInterestingly,
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!ConclusionI
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.
Posted: Fri - May 6, 2005 at 08:27 PM
|
|
Quick Links
Categories
Yahoo! Tech
MyBlogLog
Calendar
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat |
Archives
XML/RSS Feed
License
My Flickr
Statistics
Total entries in this blog:
Total entries in this category:
Published On: Aug 27, 2007 05:57 PM
|