Implementing a Tactics scoreboard in Vue

May 19, 2021, Update

This post was originally written as a React post. However, in recent years I focused mainly on the Vue framework. Therefore, this post has been rewritten for Vue.


Tactics (also known as Cricket) is a well known dart game played in many pubs and bars. The objective is to hit the numbers 20 to 10 and the bull three times each. A number is said to be complete once it has been hit three times and hitting it subsequently means scoring points (20 for hitting a 20, 19 for hitting a 19 and so forth) until your opponent completes it. The game ends when both players complete all numbers and the player with the highest score wins. 

Although there are dedicated scoreboards available, I thought it would be nice to create a digital scoreboard using Vue. For simplicity I decided to ignore hitting the bull even though it’s a nice feature of the game. In addition, I assume two players share one device when playing.

Here is a wireframe I sketched for the game. 

Wireframe for Tactics scoreboard

Let’s define

the components we need for this game are:

  • Tactics
  • Header
  • GameContainer
  • PlayerField
  • PlayerTurner
  • PlayerField
  • Finished

And let’s show what the end product will look like:

Tactics scoreboard at the start of a game. Gijs is on turn.

In this post I will discuss how we implemented this game in Vue. This consists mainly of carefully defining the state of the game, thinking about which user interactions may mutate the state and finally how the UI will react to these changes in the state.

Defining state

During the game one of the players is ‘on turn’. We’ll call this the active player. So we need a boolean to determine which player is active. For each player we have some meta information (name, number of rounds won) and a score object. The score object contains the numbers 20, 19, 18,…, 11, 10 as keys. Values are objects that look like this (for key 14):

{
    completed: false, // Boolean to know if number is completed
    hits: 0, // To count number of hits on this number
    number: 14,
}

Please note that the completed property might seem ambiguous as we can determine it from the simple expression: hits >= 3. However, it turned out that having it in a variable was easier, as we need to retrieve the complete state of this number of the opponent. For example, we need to know if the opponent completed number 14 once the active player hits a 14 on the board and his number of hits exceeds 3.

User interactions

During the game we define the following actions:

  • changeTurn: once the player has thrown 3 darts, the turn switches and the other player becomes the active player
  • hitNumber: the player hits a number that is not yet completed. With this action, either
    • his turn continues; 
    • turn is switched to the opponent, or; 
    • the game finishes
  • addPoints: the player hits a number which he has completed but the opponent not (yet)
  • resetGame: once the game is over it should be possible to reset to play again

Changing turn

A turn consists of 3 darts. If a player hits a number in the range 10 to 20, this number is clicked upon and the dartsThrown variable is incremented. This way we can automatically switch turns as dartsThrown=3. However, if the dart ends outside of the board or in a number below 10 there is no button to mark a miss. Therefore we need an additional change turn button. In practice, the active player will typically look at the scoreboard and decide where to aim his darts. Once the three darts are thrown the player will update the scoreboard, clicking the numbers that were hit and optionally the change turn button (if some dart missed a number in the 10 to 20 range).

Hitting a number

A variety of things may happen when hitting a number on the board:

  • When the active player hits a number with a current number of hits of either 0 or 1 hits, the number of hits simply increments. 
  • If the number of hits was 2, this number becomes completed, subsequent hits on this number results in points (but only if the opponent did not yet complete this number, see adding points below). Moreover, it is possible that the game finishes as it might be the last open number on the scoreboard.
  • Optionally, the players change turns.

Adding points

If the number of hits is greater or equal to 3, the active player scores points (assuming the opponent did not yet complete this number). The number of hits of the number is incremented and the score is updated automatically. The score is implemented as a computed property based on the player object: We simply retrieve all numbers for which hits > 3, subtract 3 and multiply by the number. These numbers are summed to yield the current player score.

let playerScore = 0
const numbersWithPoints = Object.values(this.player.score).filter(item => item.hits > 3)
numbersWithPoints.forEach(item => playerScore += item.number * (item.hits - 3)

As with hitting a number, optionally the turn is changed.

Game finished

As soon as the active player completes his last number, we say that the board is finished. However, this doesn’t necessarily mean that the game is finished too, because the opponent may still have open numbers. But we do know this: the opponent will no longer be able to score points. Based on this observation we have the following options:

  1. Points of the active player are greater than the opponent’s points. The game is won by the active player. The game finished screen is shown with the option to reset the score and play again. The active player’s roundsWon is incremented.
  2. Points of both players are exactly equal. There is no winner, but the game ends here and the game finished screen is shown.
  3. Points of the active player are lower than the opponent’s points and the opponent’s board was already completed. The game is won by the opponent and the game finished screen is shown. The opponent’s roundsWon is incremented.
  4. Points of the active player are lower than the opponent’s points and the opponent’s board is not completed yet. In this situation the active player can still win if he scores points in the next turn and the opponent does not complete his board.
A game is finished: the current standings are shown and you can either play again or return to the main menu.

Reset the game

Once a game is finished it is time for revenge. Hence, the player’s scores need to be reset and the opponent will start the next game. The current number of rounds won for each player is shown below the scoreboard.

Summary and conclusion 

As you can see, a digital scoreboard for a simple dart game seems rather straightforward but there are some nifty details. In particular, defining if the game is finished took some effort to understand the various situations that are possible.

In its current state, this digital scoreboard only works for two players at the same physical location. That is, the scoreboard works on a single screen for two players. In a future update we will likely improve this such that each player can login to a lobby, challenge an opponent to play independent from location. The individual scoreboards will need to communicate with each other to show the last hits from the players.

Additionally, more scoreboards will be developed, for example for playing a leg 501.


Mijn Twitter profiel Mijn Facebook profiel
Pim Hooghiemstra Webdeveloper and founder of PLint-sites. Loves to build complex webapplications using Vue and Laravel! All posts
View all posts by Pim Hooghiemstra

Leave a Reply

Your email address will not be published.