React toy project: Tactics (part 1)

dartboardIn the last blog I introduced JSX and the reminder list for working with JSX. Today it is time to get started with some real React code. Following the approach described in ‘thinking in React’, we start with the setup of a static version of the app. The app we consider is a dartgame called Tactics. In short, two players hit the numbers 20 to 10 three times each. Once a number is completed (i.e., you hit it three times) but your opponent did not, you can score points by hitting the number. When both players have all numbers completed the points are calculated and the player with the highest score wins.

My idea is that the Tactics game will be included in a bigger darts application with a variety of games, so apart from the actual game, we will need some navigational elements.

Wireframes

I drew some wireframes for the app and the first thing we have to do is defining the components of the app. Based on the wireframes we have the following hierarchical setup of the components:tactics-wireframes

  • Tactics [main component]
    • Header
    • TacticsGame
      • PlayerTurner
      • Standings
      • PlayerField
        • Row
          • PointsCounter

Static version of Tactics

For our implementation of Tactics we assume there are 2 players and hence, there will be two PlayerField components in the app. In this version, all code will be written in one main.js file. All code is available on Bitbucket.

Starting from scratch, I decided to create the components top-down (i.e., starting with the Tactics component). However, as soon as I reached the TacticsGame component I realized we need data to populate the various components. So, I decided to create an object for each player and passed those objects as props in the main component.

Player object

var player1 = { name: 'Klaas', roundsWon: 4, turn: false, score: { '20': { hits: 4, completed: false }, '19': { hits: 3, completed: true }, '18': { hits: 0, completed: true }, '17': { hits: 0, completed: true }, '16': { hits: 1, completed: false }, '15': { hits: 0, completed: true }, '14': { hits: 6, completed: true }, '13': { hits: 4, completed: false }, '12': { hits: 0, completed: true }, '11': { hits: 2, completed: true }, '10': { hits: 3, completed: false } } };

Some obvious parts of the player object are of course the name of the player and the number of rounds won. Since we also need to know which player is on turn, we need to add a boolean ‘turn’. The score object is probably the most complex part. I decided to use the keys 20, 19, …, 11, 10 for the 11 rows in the game and count the number of hits per number and whether the other player already completed a specific row.

The latter is needed because in the game of tactics you can only score points when you have a row complete (that is at least 3 hits on the number) and your opponent does not. One might say that this is superfluous, however given a single player’s score board, the only thing that is passed down to this component is its own score object (to keep things separated). Hence, to know the progress of the opponent, we have to store the completed boolean of the other player in the current player’s score object.

Creating the components

With this in mind I started writing the HTML with JSX. Most of it was rather straight forward, only the part containing the score and the Row object were a bit more involved. Below is the code for the interesting components. Technical parts of the code are explained below. Note that this is not React specific but rather app specific.

PlayerField component

The score of each player can be computed by multiplying the number of a row by the number of hits minus 3 (the first 3 hits don’t count). This can be achieved using the array function reduce. Since the score is an object we need to create an array from it (using Object.keys) and reduce it to a single number by executing a function on every element.

Rendering the rows was the next challenge. In my first attempt I just used Object.keys on the score object to create a new array and mapped this with the function renderRow. However, Object.keys does not necessarily return the keys in the same other as you think! It returned the keys as an array starting at 10 and incrementing up to 20 instead of the other way around as I wished…

The solution was to create my own array [20, 19,…, 11, 10] using the range function from underscore and applying the map function.

Row component

For the row component the challenge was to create classes for the three numbers such that depending on the number of hits, the correct amount of numbers could be crossed using CSS. By passing a class hit-x where x = min(3, hits) we ensure to get classes hit-0, hit-1, hit-2 and hit-3. In combination with the data attribute ‘item’ the CSS could be written.

The next thing is the counter for adding points to the score. Remember that you can only score points if you have hit the number 3 times, whileyour opponent did not. To ensure that we only show the counter button we need to pass the number of hits and the status of the opponent on this number to the PointsCounter component. Note that we use a max() function in order to have amount >= 0!

PointsCounter component

The component is wrapped up in a span with a class hidden when you have not completed the number. In addition, the score button is only shown when the opponent has not completed the number yet and the number of points gained sofar are only shown when you have completed the number yourself.

Tactics game so far

So far the description of the individual components. Here is how the static version of the app looks like. To be able to see all components in action, I started somewhere in the middle of the game!

react-tactics-static-version

Wrap up

At this point the app is still static. Next up is adding some interaction for the components. For this we need to define the state and update it accordingly when the users interact with the app. Stay tuned!

The complete code is available on Bitbucket.


Mijn Twitter profiel Mijn Facebook profiel
Pim Hooghiemstra Webdeveloper and founder of PLint-sites. Loves to build complex webapplications using Vue and Laravel! Latest post
Laravel-mix and the spread operator

Leave a Reply

Your email address will not be published. Required fields are marked *