It’s been a while coming but I’ll explain that in a bit more detail as I discuss some of the decisions I made. Before I begin though, it is worth recapping on the rules. It’s here, too, that you will learn that the original creator of the game is a man called Mordecai Meirowitz which I think is the coolest name ever. Topically, there is also a film by the name of Mordecai out at the moment, too. #endoftrivia. Let’s begin with the two main things that I wanted to use for this project.
Compared with my previous website, Rock, Paper, Scissors, this one needed to maintain a state (or record, if you like) of what happened between page loads. I also wanted to practise using a unit testing framework, and that’s where NUNIT came in. As you will see soon, both were pretty integral to me completing the project.
To begin, let’s take a look at the main screen:
You can see that there is a heading, two buttons (the first is to make guesses and the second restarts the game), a block of four groups of radio buttons, some instructions and an area to show results underneath.
I went to OpenClipart again for some pictorial inspiration and found just what I needed in the work by jean_victor_balin on this page and this one. I did the usual clipping and resizing in GIMP to produce these:
You can clearly see the colours that I used for the choices of codes in the game, but what about the last two? The first one represents when a user chooses a colour that is the right colour AND right place. The blue one is for when you have picked a colour that has been used by the computer but it’s NOT in the right place in your guess.
Let’s illustrate it with an example: The computer has chosen this secret sequence :
and the user responds with this:
Straight away, you can see that that they are almost exactly the same except the last two: they have been switched. That means we have two correct colours in the right place (green and orange) and two correct colours that are in the wrong place (red and purple). This gives us:
If all guesses had been wrong, nothing would be shown. If you get all of them right, you are told via a “Winner!” graphic. Got that?
I use the ViewState for two things:
- To keep track of all previous guesses made and,
- So that the webpage knows what the currently chosen four-colour code is for the computer.
GuessesMade _ is where I store an _ArrayList of a class: GuessesMade. That is simply a guess and a result from scoring it, which you can see below:
I create one of these for each guess the user makes and store that in the ViewState. Note that I flagged the class as being Serializable. It needs to be so that we can store it inside the page’s state. As for the computer’s choice, that was really easy. This is the Page_Load method that get’s called when the user clicks the button making their guess.
protected void Page_Load(object sender, EventArgs e)
Simply put, this says that if this is the first time IIS has presented this page (line 3), then see if we have the key “Code” within the ViewState, and if not, add it by creating a computer code (via MastermindSequence) and assigning to it the output of ToString() – lines 6-11. The else statement near the bottom handles reloading of the classes when we have made a guess. If we didn’t, they data would be in ViewState but not accessible to all the other functions.
How do I know which guess was made? Each of the radio buttons represents one of the colours you can choose to try to guess what the computer has hidden. When you select one, it has a value like so:
Imagine that there are four of these in total (try really hard because there really are!). Originally, I had really wanted to use images rather than plain-old textual names, but that upset the XHTML checker, sadly. There was also a whole host of fun trying to get everything lined up, as ever :-) On that note, I spent absolutely ages trying to get the radio buttons to go in a line. No matter what CSS skullduggery I used, I just couldn’t manage it. That is, until I saw this property:
Don’t you just love it when the solution is right in your face? It seems that this property takes precedence over any CSS you might try and rain down on the ASP control. One HUGE lesson learnt. You can see what I set it to in the code segment above.
Within the code-behind, these radio buttons are all read within the button click event, passed to a class which holds the computer’s choice of code (MastermindSequence) and scored. The results of that are then used to dynamically update the results table at the bottom of the page, via the GuessesMade ArrayList, mentioned above.
You can see from the above code that the guess made by the user is passed in as a string made up of the four colours (such as “ROGY” for red, orange, green and a yellow). Let’s next look at how I work out the score (or result) for a guess.
// Used to ignore colour codes when checking
The basic algorithm is this:
- For each colour code (character) of the guess, go through the code stored for the computer and if they match and are in the right place, add an “X” (right colour, right place) to the results. Then blank off both characters so they don’t get re-used/double counted.
- For each colour code in the guess that remains, see if it’s anywhere in the computer’s code. If it is, add a “-“ representing a right colour, wrong place, to the results, and blank off both characters, as above.
- Do this until there are no more guess colours left.
A point worth mentioning is that I needed a copy of the guess and computer’s code because I change it. You can see that happen on lines 37 and 38.
This part really, REALLY, helped me catch bugs. What I needed to do was to find all (most? but I tried to find all) of the kind of scenarios that could occur. I used Pragmatic Unit Testing in C# with NUNIT by Andrew Hunt, David Thomas and Matt Hargett to inspire me for this; it’s had a few rough reviews, but has helped me so far. Anyway, they advocate something called Right-BICEP, which sounds pretty powerful, for sure. Here’s what it means:
- Right — Are the results right?
- B — Are all the boundary conditions CORRECT?
- I — Can you check inverse relationships?
- C — Can you cross-check results using other means?
- E — Can you force error conditions to happen?
- P — Are performance characteristics within bounds?
The areas of that that I focussed on were Right, boundary conditions (B) and forcing error conditions (E). I basically wrote my tests, watched them fail, created a working test and then refactored. Here’s a breakdown showing the kinds of things that I tested for:
- That ToString produced a proper representation of the code (i.e. ROGY for Red, Orange, Green and Yellow).
- Passing in guesses that were too small, too big or null (causing an exception), compared to the size of the code chosen by the computer.
- That it was able to detect when the guessed colour was in the right place.
- That it could find all examples where a right colour had been chosen, but placed in the wrong place.
- A mix of the above two.
- An example where the guess was a complete wash out (totally wrong).
- That it created codes of a proper length (i.e. ask for four colours, get four colours).
- I looked at this on my Windows Phone (Lumia 925) and it looks like a shocker! The sizes of the elements are all over the place and nothing looks as neatly lined up as it does on the big screen. There is clearly no responsive aspect to this website having been developed, tested and only used, on the desktop. I really want to get better at this.
- It does tell the player that they have won but there isn’t exactly a fanfare. Perhaps it could make more of a song-and-dance about it with some video played? I don’t know.
- I swapped things so that strings and characters were passed around to represent the colours chosen and results obtained for guesses when originally, I created a series of enumerated types. This made it so much easier to test and link to the main page, but I am left feeling that I really ought to have been more prescriptive and stuck with the structured types.
- This could be immensely improved if I separated the view from the model. I will definitely make a more structured design in my later ones.
- It seemed like I could use [TestFixtureSetup] as my attribute on a method even though [TestFixtureTearDown] worked as typed. I’m still not quite sure why that is, but to get around it, I used: [NUnit.Framework.TestFixtureSetUp].
- I needed to use a serializable class for my ViewState. Prior to this project, I had been pushing around strings in my test project but of course, that feature is built into the String class.
- NUNIT is the greatest! In fact, TDD is the greatest! I picked up so many errors and misconceptions through having to create the tests first that its value was immediately apparent. I will be using it for all projects going forward but will try to incorporate a variety of test types for practice purposes.
- The RepeatDirection property controls how radio buttons are aligned. Repeat. It WILL WIN IN A FIGHT!
You can find the source in the GitHub repository.
Hi! Did you find this useful or interesting? I have an email list coming soon, but in the meantime, if you ready anything you fancy chatting about, I would love to hear from you. You can contact me here or at stephen ‘at’ logicalmoon.com