Introduction

This software allows to play the classic X01 darts game with up to 9 players and single or double out. It supports both the keyboard and an actual dartboard as input and features logging which can be used for statistics. It also includes a CGI application, which provides a web interface for additional tools.

Command Line Arguments

The following options may be passed to the Darts.exe as command line arguments in any order. Each option is specified by passing the Short or Long identifier as argument, possibly followed by the value for this option as another argument according to the following table.

Short Long Argument Description
-cp --com-port Name of serial port Specifies the serial port from which to read commands as described below. If this option is missing, no serial port is opened and only the keyboard is used as input.
-s --score Initial score Specifies the initial score from which each player starts. It may be any number from 1 to 999 in mode Single Out, or 2 to 999 in mode Double Out. If this option is not specified, the default value 301 is used.
-do --double-out - If this option is specified, game mode Double Out is enabled, which requires to finish the game by scoring on a double section. If unspecified, the default mode Single Out is used, which allows to finish with any score.
-p --player Name of player Specifies another player. This option must appear between 1 and 9 times according to the number of players and each name must be unique. Their order within the command line arguments determines the order of the players within the game. Restrictions for player names are described below.

Player Names

Each name must consist of 1 to 20 characters and must not begin or end with a whitespace. In addition to alphanumeric characters and the space character, any of the following characters is allowed: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

Input

The application takes all of the 62 scoring sections as well as 3 buttons for navigation as input. They are named according to the following table.

Type Identifier Description
Score S1 - S20 Single sections
Score D1 - D20 Double sections
Score T1 - T20 Triple sections
Score S25 Single bull counting as 25
Score D25 Double bull counting as 50
Button EXIT If this button is pressed twice, the application quits. If pressed once followed by button START, the game is restart.
Button START If this button is pressed while dart throws are remaining, they are skipped to end the current turn. If pressed again, the score is confirmed and the game goes on with the next player.
Button BOUT If this button is pressed, the last action within the current turn is undone. This can be either a dart throw or the action of skipping remaining darts using START.

Keyboard

The following keyboard mapping is used. If a key is specified in the first column, it must be hold down while the key in the second column is pressed.

Modifier Key Name
- 1 - 9 S1 - S9
- 0 S10
- F1 - F10 S11 - S20
Ctrl 1 - 9 D1 - D9
Ctrl 0 D10
Ctrl F1 - F10 D11 - D20
Shift 1 - 9 T1 - T9
Shift 0 T10
Shift F1 - F10 T11 - T20
- B S25
Ctrl B D25
- Escape EXIT
- Return START
- Backspace BOUT

Serial Port

If a real dartboard is connected to the PC using a serial port, it can be used as input. The application reads from this port and turns any received identifier from the table above into the corresponding command. Any two consecutive identifiers read from the stream must be separated by at least one whitespace, which includes line breaks. Any non-whitespace character must be part of a valid identifier.

The following sample code is a program written for the Arduino Uno used to turn the signals from the dartboard into commands for the darts application.

const int outN = 7;
const int inN = 10;

const int outPins[outN] = { 6, 7, 8, 9, 10, 11, 12 };
const int inPins[inN] = { A0, A1, A2, A3, A4, A5, 2, 3, 4, 5 };

const char* const mapping[outN][inN] = {
    "T6",   "T10",  "T20", "T5", "T12",  "T9", "T13",  "T4", "T18",  "T1",
    "D6",   "D10",  "D20", "D5", "D12",  "D9", "D13",  "D4", "D18",  "D1",
    "S6",   "S10",  "S20", "S5", "S12",  "S9", "S13",  "S4", "S18",  "S1",
  "BOUT", "START", "EXIT", NULL, "S25", "D25",  NULL,  NULL,  NULL,  NULL,
    "S2",   "S15",  "S16", "S8", "S11", "S14", "S17",  "S3", "S19",  "S7",
    "D2",   "D15",  "D16", "D8", "D11", "D14", "D17",  "D3", "D19",  "D7",
    "T2",   "T15",  "T16", "T8", "T11", "T14", "T17",  "T3", "T19",  "T7"
};

void setup() {
  for (int i = 0; i < inN; i++) {
    pinMode(inPins[i], INPUT_PULLUP);
  }
  for (int i = 0; i < outN; i++) {
    digitalWrite(outPins[i], HIGH);
    pinMode(outPins[i], OUTPUT);
  }
  Serial.begin(9600);
}

void loop() {
  for (int i = 0; i < outN; i++) {
    digitalWrite(outPins[i], LOW);
    for (int j = 0; j < inN; j++) {
      if (digitalRead(inPins[j]) == LOW && mapping[i][j]) {
        Serial.println(mapping[i][j]);
        delay(500);
      }
    }
    digitalWrite(outPins[i], HIGH);
  }
}

Logging

The application logs each played game and each round of a player to two separate CSV files. Both files are written to the working directory and are named Games.csv and Rounds.csv, respectively. The first file contains a single row per played game containing the game settings passed as command line arguments. It consists of the following columns.

Timestamp Score Mode 1st Player 2nd Player 3rd Player 4th Player 5th Player 6th Player 7th Player 8th Player 9th Player
Time when this game was started Initial score for each player Single Out or Double Out Name of 1st player Name of 2nd player, if existing Name of 3rd player, if existing Name of 4th player, if existing Name of 5th player, if existing Name of 6th player, if existing Name of 7th player, if existing Name of 8th player, if existing Name of 9th player, if existing

The second log file Rounds.csv contains a single row per round, which is a three-dart turn of a player. It consists of the following columns.

Timestamp Round Player 1st Dart 2nd Dart 3rd Dart Score Before After
Time when this round was completed Number of round within the current game starting from 0 Name of player First section hit with a dart, if any Second section hit with a dart, if any Third section hit with a dart, if any Score made within this turn Remaining score before this round Remaining score after this round, or place if score reached zero, or BUST if remaining score was exceeded or score reached 1 while in mode Double Out

Each round is logged as soon as it is confirmed, which either happens when START is pressed to go to the next player or when the game is restarted or quit while there are no remaining darts in this round. A game is logged together with its first round, but the logged timestamp represents the time when the game was started. So only games with at least one played round are recorded and the beginning of each game is marked by a round, whose second column is 0. This allows to associate games with their corresponding rounds.

The timestamps in both files are generated using the standard C function ctime with a small modification: During daylight saving time, a whitespace followed by DST is appended to allow for disambiguation during time changes. The timestamps therefore match the format Www Mmm dd hh:mm:ss yyyy[ DST] and represent the local system time.

CGI Application

The DartsServer.exe implements the Common Gateway Interface and provides a web interface for configuring and launching the Darts.exe and for showing statistics about played games. It has been tested successfully with the minimalistic web server TinyWeb, but should as well run with any CGI compatible web server. It expects the Darts.exe as well as the two CSV files to be in the working directory to run properly. The package also includes a folder named DartsServer which contains additional resources like CSS and javascript files in a subfolder Resources. It should be placed on the web server as well for the web interface to look and function properly.