So after we hooked up and setup the skeleton of the game, it’s time to add some more meat – actually implementing the rules of the game.
Validating a Move
In our next commit1, we introduce the notion of validating a move:
The logic is simple: when we’re handling a cell click, i.e. a request to make a move, we first make sure the move is legal/valid. If it is, we make the move (line 8), and update the UI as before. If it’s not valid, we ask the UI to show some error message2. Note that some UI work – showing a message, displaying who is the current player – happen regardless of the canvas being there or not.
The validation function itself – isValidMove
– is fairly straightforward at this point. It merely checks that the move is of a cell that belongs to the current player. What’s more important is that we have a specific place to validate a move. We can augment it with further rules later. Another design choice made here is that the isValidMove
function returns a boolean result – a move is either valid or not, without any specification of the nature of the problem. A more robust mechanism would’ve returned some indication of the actual problem with the move; mostly for the purpose of showing the player a reason for rejecting the move.
Capturing Stones
Our next commit does two important things:
First, we fix an old “wrong”. We move the function the implements the game rule into the MancalaGame
class. Remember that when we reviewed the code in the past we took note of this issue; we’re now fixing it.
Next, we’re implementing another game rule – capturing stones. The logic isn’t very complicated, it’s basically encoding the game rule directly – see if we finished in an empty cell, and assuming it’s in the current player’s side, capture the stones from the opponent’s cell right across the board.
From a design point of view, we’re only doing here a change in the board’s state – mutating the local Board
instance using its mutating methods (addStoneTo
, setCellStoneCount
). So we maintain the separation of concerns as we intended: isValidMove
validates the move, playCell
changes the board as necessary and drawBoardState
updates the UI with the new state.
Extra Turn
Our next commit takes care of another rule – a player gets an extra turn if his move ended in his home (his Mancala):
The logic of the rule itself is in lines 9-14 above, and pretty straightforward to follow. Note how we create here an interaction with the playCell
function. The playCell
is primarily concerned with updating the board data structure (this.board
). And returns an indication of the last cell played. The rest of the logic is in its calling function (handleCellClick
), which checks for validity and tests for the extra turn. The game logic is still centralized in the same class, MancalaGame
, but we’re also keeping a separation between the different functions, trying to maintain the cohesion of each function on its own.
Another small point to take note of here is a small refactoring, mainly for code readability. There are now at least 2 places checking for a specific condition only if a specific player is currently playing (lines 9,10 and 33,34 in the snippet above). My “DRY itch”3 came alive when seeing this, so the pattern is extracted into separate functions – player1Playing
, player2Playing
. We’ll see later how this pattern of identifying a condition based on the current player that’s playing repeats itself so this will come in handy down the road as well.
Next we’re going to look into how we identify when the game is over, and implement it. We’ll also wrap up this basic version of the game, and look into what else can be done.
Notes
- I casually skipped this commit here, since it’s really just about aligning UI elements. But it’s there, have a look.
- The
showMsg
function is a callback to the UI (theindex.html
) that was fed to theMancalaGame
class during initialization. It’s introduced in the same commit. - or my “code smell nose”