StarCalc is an implementation of the STAR voting method.
It exists as a CMake consumable library and reference command-line application that demonstrates use of the library.
The original motivation for this project was to facilitate automated election result calculation of a single-round voting system for the Philadelphia Film Critics Circle annual awards, which was realized as the reference application.
- Reference command-line application for running elections
- Full implementation of the STAR voting system
- Supports Bloc STAR Voting (determining the winner(s) for one or more seats)
- Can be configured to allow true-ties instead of employing a random tiebreak when other tiebreaks have faield
- Optional extra Condorcet Protocol tiebreaker for Scoring Round ties
- Optional Qt signal connection that details calculation steps
The public API of the library is not currently documented (it likely will be in time or given interest), though its usage is fairly straight forward as shown in the following minimal example:
#include <star/election.h>
#include <star/calculator.h>
#include <iostream>
int main()
{
// Setup ballots
Star::Election::Voter jim{.name = "Jim", .anonymousName = "Voter 1"};
QList<Star::Election::Vote> jimVotes{
{.candidate = "Candidate 1", .score = 3},
{.candidate = "Candidate 2", .score = 1},
{.candidate = "Candidate 3", .score = 5}
};
Star::Election::Voter sarah{.name = "Sarah", .anonymousName = "Voter 2"};
QList<Star::Election::Vote> sarahVotes{
{.candidate = "Candidate 1", .score = 4},
{.candidate = "Candidate 2", .score = 0},
{.candidate = "Candidate 3", .score = 2}
};
Star::Election::Voter ted{.name = "Ted", .anonymousName = "Voter 3"};
QList<Star::Election::Vote> tedVotes{
{.candidate = "Candidate 1", .score = 3},
{.candidate = "Candidate 2", .score = 2},
{.candidate = "Candidate 3", .score = 1}
};
// Setup election
Star::Election::Builder eb("My Election");
eb.wBallot(ted, tedVotes)
.wBallot(sarah, sarahVotes)
.wBallot(ted, tedVotes);
eb.wSeatCount(3);
Star::Election election = eb.build();
assert(election.isValid());
// Setup Calculator
Star::Calculator calculator(&election);
//If you want calculation details...
QObject::connect(&calculator, &Star::Calculator::calculationDetail, [](const QString& detail){
std::cout << detail.toStdString() << s
8000
td::endl;
});
// Determine outcome
Star::ElectionResult result = calculator.calculateResult();
assert(!result.isNull());
std::cout << "Winner(s): " << result.winners().join(", ").toStdString() << std::endl;
return 0;
}
You can also refer to the reference application source to get a better understanding of how to use the library.
This application creates multiple elections from a specific input format (i.e. an election with multiple categories), calculates the results, and then prints them to stdout.
First, it expects a CSV of votes that consists of a header row, followed by one row per ballot. The first two fields of the header row don't matter, but the following fields should consist of the candidates for each category. The first field of a ballot row does not matter, while the second should contain the candidates name (for now it is unconditionally obfuscated), and finally the remainder should contain a score (0-5) that corresponds to the candidate in the above header row.
Example:
N/A,Voters,CatACan1,CatACan2,CatACan3,CatBCan1,CatBCan2
X,Jim,0,3,5,4,2
X,Sarah,5,2,2,1,4
X,Ted,3,0,0,5,0
Second, it expects an INI file that indicates how many candidates are in the category, in the same order as they are listed in the CSV. These details are to be place under the section "Categories". Additionally, a value for the singular key "Seats" must be specified under the section "General" in order to specify how many candidates are to be elected (Bloc voting). Simply use "1" for a typical election with a single winner.
Example:
[Categories]
Category A = 3
Category B = 2
[General]
Seats = 2
Lastly, the files are loaded via the application's command-line parameters as shown in the following section.
The application uses the following syntax scheme:
StarCalc <options>
Options:
- -h | --help | -?: Prints usage information
- -v | --version: Prints the current version of the tool
- -c | --config: Specifies the path to the category config INI file
- -b | --box: Specifies the path to the ballot box CSV file
- -t | --true-ties: Ends an election prematurely instead of using a random tiebreaker when an unresolvable tie occurs.
- -e | --extra-tiebreak: Uses the Condorcet protocol tiebreaker during the scoring round before the random tiebreaker if necessary
- -m | --minimal: Only show the results summary
Example:
StarCalc -c path/to/cat_config.ini -b path/to/ballot_box.csv
- C++20
- CMake >= 3.24.0
- Targets:
- Windows 10+
- Linux (Tested on Ubuntu 20.04)
Tested with:
- Windows: MSVC2022
- Linux: Clang 12
The source for this project is managed by a sensible CMake configuration that allows for straightforward compilation and consumption of its target(s), either as a sub-project or as an imported package. All required dependencies except for Qt6 are automatically acquired via CMake's FetchContent mechanism.
The configuration of this projects supports consumption both via find_package() and FetchContent.
CMake Options:
STARCALC_TESTS
set to ON in order to generate tests
CMake Targets
all
- Builds the library, reference application, and testsstarcalc_base
- Builds the librarystarcalc_frontend
- Builds the reference applicationstarcalc_tst_...
- Various test targets. To actually run tests, just build the general CMake tests targettest
CMake Install Components:
all
- Installs all built componentsstarcalc
- Installs top-level files (README.md, CMake package configuration files, etc.)starcalc_base
- Installs the librarystarcalc_frontend
- Installs the reference application