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.
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. |
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: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
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. |
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 |
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);
}
}
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.
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.