My friend is quite fond of a game named ‘Kniffel’ or ‘Yahtzee’ and as we are seldom in the same place, I started thinking about a way to play it via internet. Of course I want to write the application myself (if not where’s the fun for me?) and take the chance to report about the progress or stuff I think might be also of interest to others.
In the beginning there’s always a user story:
- We want to play at dices with n players , n>=2 and n <= 8 (at least that's the standard).
- We want to play on different computers while being in different places. No Internet is a no-go anyway.
- The game rules are the standard rules of Kniffel (for later we might think about extending it with user defined rules).
- The game must be able to pause in between and resume where stopped the last time.
Now read this Wikipedia article and try to get the idea of object-oriented programming, cause that’s what we’ll do now.
We look at the game how it is in real life and then try to model the computer code after the real thing. So once you have the user story you first need a rough sketch about the structures before starting to code.
What do we need to start:
- 5 dices
- a dice cup
- two or more users
- a sheet of paper to track the game status
- a set of rules
- the GAME that holds all of these together
We first implement the core structure of the game, the nice gui stuff comes later on top of an already working though basic application. We might decide to interact with the game via browsers (the most possible solution) or could even think about some fancy way of sending mails back and forth. What we display to the end user at a later point has nothing to do with the game core. We will certainly not hard wire anything about the gui stuff into the game core.
The game (object)
- holds 2 to 8 users
- has a cup
- has 5 D6 dices
- has a set of rules
- has a sheet of paper where all results are marked down
- knows whose turn it is
- gets nasty when someone tries to cheat 😉
The user (object)
- has a unique user id
- is told when it is his turn to play
- is given the cup
- is given the dices
- can put dices into cup
- can roll the dices
- decides to keep dices fixed and roll the remainder again up to 3 times.
- can decide to make ‘rule objects’ from the dice results (kniffel, full house, etc.)
The cup (object)
- can be filled with dices
- can roll all dices it holds
- can show the dices result
- dices can be removed
Note: the cup object is unaware of which user currently holds the cup or what
kind of dices and how many it contains. It does its part and that’s it.
The D6 dice (object):
- is of type D6, that means it has 6 faces with values 1-6
- can be rolled
- holds its current face until rolled again
- knows if it was looked at after rolling
Memo to self: beware of random numbers and statistics here, at least check the distribution in a unit test. There was something about random seeds and pseudo random numbers.
Note: The dice is unaware of which user currently holds the cup or how many other dices are in the cup. It does it’s part and that’s it.
A little more on dices:
I’m bored to death when having to implement the same code slightly altered over and over again. Reusing classes spares you a lot of time. We could want to play a game with different rules and D20 later, but this will also be a game that holds users, rules though different, sheet of paper, cup and dices.
The cup does not care about how many dices it holds and if the return values range from 1 to 6 or from bla to foo. So we will generate the dices in a factory that gives us D6 for the Kniffel game but might later be extended to give us D20 as well.
To make sure the cup gets what it needs, we define a common interface for the dices that all dices must implement.
The Dice factory:
- is told we want a W6 with ID 1
- might also be told we want a D20 with values 1 to 20
- generates the correct dice object for us
Btw, we just started using interfaces and design patterns 😉
Hope you get the idea, same way as cup and dices are defined, we will define the set of rules, ‘sheet of paper’ etc.
Different persons could now start to implement for example cup and dices. The cup developer needs to know about the public interface the dice will have, as he needs to call this functionality in the dice object. But for developing the cup, he does not need to wait until the dice class is completely implemented. Remember the cup is ‘given the dices’? That is dependency injection, the cup can get mocked or stubbed dices for unit tests, so the cup can be completed even if the dice is not yet finished.
If you do this (and you will do this on a larger scale at some point), make sure you run some full integration tests on the whole application later. Happens too often that someone changes e.g. the return value of a public function without remembering that the calling class expects an array and not the boolean it gets now. If you have mocked the return value in all the related unit tests, you are in big trouble. Your tests are all ok but the application is not behaving as expected, yeah.
Oh, before my boss might stumble on this and starts complaining I did not mention: use TDD (test driven development), then you always end up with a completely tested application.
Enough for now, guess what I’ll start doing right now :p
p.s.: example code will be seen in later blog posts, stay tuned.