Continuing our journey to creating a game, after drawing the board, we should make sure the UI is actually responsive and a user can actually play the game.
The first interesting bit here is translating a UI click (mousedown
event) to a change in the game state, and drive a redrawing of the new state.
So what’s going on here1?
If you remember, we already added an event listener to the mousedown
event, when creating the necessary UI objects. At this point, we’re making sure it’s actually responding. Since we want the concern of responding to events to be contained in the controller (the game.js
module), we add a callback function to the drawBoardState
function and make sure it’s invoked at the correct time – when the mousedown
event is fired. Note that at the point we attach the event, we’re already aware of the cell we’re attaching it to (the boardCell
parameter is part of the closure), so this saves us the need to translate a UI object (or coordinates) to a game object/concept – the cell.
Looking at the callback function implementation – boardClickHandler
in game.js
– we see a rather simple implementation. We ask the board module to “play” the cell, i.e. play the stones in that cell – make a move. This is a new mutating method in the Board
class. Then, assuming the canvas is available, the handler asks the drawing module to draw the new state again.
Finally, looking at the addition to the Board
class, we can see how it’s “taking” all stones in the cell and adding one stone to each of the next cells counter clockwise, as per the Mancala rules + empties the played cell (line 11 in board.js
above). The result of this method execution is a new state of the cells in the board data structures – a new count for some of the cells.
Note that all state changes in the board are expressed through one method – setStoneCellCount
, this is intentional and will prove useful later. When coding, you don’t always foresee the future and where some choices might be useful. In this case, the benefit wasn’t immediately apparent. Still, I stuck to a simple principle of trying to express higher level functionality (playing a cell) using lower level functionality that’s already defined. This is basic structuring of the code, in this case following the DRY principle. At the very least it helps with readability, which is important by itself. In this case it also helps with encapsulation.
A Short Code Review
It’s important to acknowledge the fact that design choices are made as we write the code, not only upfront. It’s therefore worth stopping and reviewing what choices were made here.
First, if you look at the code, you can see that the Board
module exposes the playCell
method. This is in fact encoding a game rule – how a certain move is made in the game. We should be asking ourselves whether this is the right place for this kind of concern to be implemented. Admittedly, when writing the code I simply wrote it this way w/o giving it a second thought. This goes to show how sometimes such choices are made when we’re not paying attention. We’ll get back to this point later, when it’s actually fixed.
Second thing to note is that the canvas
variable is in fact an Option type. Meaning, we could be running the game without a canvas to paint on. The code in the game module (the controller) is of course aware of this and explicitly asks whether the canvas is there, e.g. lines 6,16 in game.js
. This is intentional – we would like to be able to run the game even when technically there’s no way to draw it (can you think why?). Of course, one has to ask whether the way to represent the fact that we’re running w/o a GUI is by managing the underlying drawing object (the canvas
) in the controller…
Third thing to note is that throughout the code, we’re referring to the notion of the board cell by implementing it as a simple integer. In most cases this is not evident, we’re just passing a “board cell”, without specifying the type. It’s mostly evident in the board data structure itself (board.js
), where we answer some queries by simply comparing numbers to the given cell. This is an intentional design choice made for simplification. I do not see a lot of benefit in defining a separate class representing a board cell. If I was using a more explicitly typed language, I would probably be aliasing the integer to a more readable type, e.g. in Scala: type BoardCell = Int
. But this is not available nor needed in JS. As long as we keep that piece of knowledge (“board cell is in fact a simple integer”) confined to one module – the board data structure – we should be fine with this choice.
A Bit of Refactoring and Introducing Players
Our journey continues with a commit that is mostly about refactoring the controller. This is where the controller module (game.js
) is rewritten to expose a class – MancalaGame
, with an explicit interface:
This is done mostly for readability and for encapsulating whatever is necessary to run the game, as more and more pieces of logic and data come up. Note specifically that the board data structure, the canvas and associated callbacks are members of this class.
Another notion we’re introducing is that of the current player. The game has a current player in place, and the controller (MancalaGame
) keeps track of it. Since we’re working under the assumption that there’re always exactly 2 players in this game, we can simply code them as constant objects:
Note that while not explicitly exposed (from module.exports
) these objects are in fact an API for this module, since they will be used by other modules. The togglePlayer
method in MancalaGame
actually exposes these objects. Also note that players are not represented by mere numbers (1,2), but as actual objects2. This is mainly because I want to have an explicit interface that encapsulate the current player, and being able to send messages to that object in different scenarios. For example, I’d like to display the player name (“ONE”, “TWO”) in the UI, and also toggle the players easily. Encapsulating the player behavior in an object (vs. representing as a simple number) is therefore more useful. We’ll see later where this comes in handy in more places.
So far, we’ve setup how the game is displayed, and the main mechanics of interaction. We have a skeleton of the game and the needed data structures to support implementing the logic. Next, we’ll start implementing more and more logic of the game, and see how this affects the code design.