Last time we started really adding meat – the logic of the game rules. Today we’re going to look into some more logic, and how we wrap it up.
Ending a Game
It’s a game, but it does have to end at some point. This commit take care of exactly that – identifying the condition that ends the game and stops processing new moves:
The implementation is pretty straightforward: we add a simple flag (gameOver
) to the MancalaGame
class (line 6) and consult it before processing any move (line 12). Of course, we have to take care of setting the flag properly, which happens at lines 18-21.
The gameOver
function (lines 30-45) takes care of 3 main things1:
- Determining the winner (lines 36-41)
- Notifying the UI (lines 35,43)
- Setting the
gameOver
flag (line 44).
The implementation is pretty simple and straightforward, but admittedly, this function does a bit too much. We’ll take care of that right away.
Also, taking care of the message displayed to the user is not ideal thing to do here, but it’s not terrible, in my opinion, in this case.
Some Cleanup Is In Order
At this point, it has become quite clear that the handleCellClick
function was becoming convoluted. It was quickly approaching the point of being unmanageable, doing too many things and operating at different levels of abstraction; e.g. encoding game rules while also taking care of UI messages. It was time for some cleaning.
The next commit 2 did exactly that:
The gist of what we’re doing here is a simple refactoring of “extract method”: taking a few lines of code, extracting them into a separate function/method and invoking that function in the right location. The main motivation is breaking down a long function into more digestible pieces, making the code more readable. There’s not even a lot of code reuse going on around here, which might be another motivation for extracting a piece of code into another function. The resulting code is, in my opinion, easier to follow, and troubleshoot in the future. Function logic is expressed more succinctly and in a more consistent level of abstraction.
Take for example the new handleCellClick
function (lines 16-27 above). If you read it, its functionality can be summarized in one sentence: “reset the message panel, then assuming the game isn’t over and the move is valid, make the move”.
Similarly, the _makeMove
function (lines 29-35 above), that is being called from the handleCellClick
function, can be summarized in one sentence: “play the cell passed, then considering the last cell reached, decide if an extra turn is in place; check if we reached the end of the game and redraw the board.”3
This mental exercise of trying to describe a function’s implementation in a sentence or two4 is an important one when trying to assess the readability of the code, which from my experience is a crucial quality factor. I believe it’s hard to assess readability with an objective criteria, but when writing and reading my code, this is how I try to assess it.
Bug Fixing and a UI Improvement
The next two commits, affectionately known as 38feec5 and 8879f315, take care of fixing a bug, and making a small (but significant) addition to the user interface:
The logic for capturing the stones is pretty straightforward. One thing to note is that the bug fix is localized in one place6 – a testament to good code structure; though, to be fair, this isn’t really a cross-cutting concern. Another thing to note is the calculation of the cell across from the last cell – acrossCell
(line 9) – the simple calculation can be done because we rely on array indices. A better implementation would have deferred this to the Board
class, and exposed a method named something like getAcrossCell(fromCell)
, so we can let this implementation detail remain in the Board
class; this is an example of an abstraction leak (anyone up for a pull request to fix this?).
The second commit takes care of creating and toggling the highlighting of the current player:
Technically, the highlight itself is simply drawing a red line on the border of the current player’s side of the board. We create 2 instances of fabric.Line
in the drawing module, and then simply add and remove them to the canvas when necessary. Note that the toggleHighlights
function in the drawing module (line 13) receives the player’s number and queries it directly. I don’t see this as a case of using magic numbers since the numbers themselves are clearly representative of the player object they’re representing, 1
for player 1, and 2
for player 2. I preferred avoiding exposing directly the the PLAYER
objects in the game module (game.js
).
We’re almost done. Next time we’re going to add a feature, cleanup a bit, and fix a bug; at which point we should have a working version of the game.
Notes
- Some implementation helper functions are in the board module.
- To be honest, there was another more administrative commit in the middle, since it doesn’t add anything to the story, I conveniently skipped it
- Ok, that’s actually two sentences, but it’s still not much; you get the idea.
- and eventually trying to replicate that description in the code
- we tried humpty and dumpty, but it didn’t stick
- No other functions were harmed in the fixing of this bug