#!/usr/bin/perl
#Copyright (C) 2003 Ben McIlwain
#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; either version 2
#of the License, or (at your option) any later version.
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
#USA., or see their website at http://www.gnu.org/copyleft/gpl.html .
#This program uses the Perl CGI.pm module to imitate the workings of CGI scripts on a webpage.
# use Term::ReadKey;
use CGI qw(:standard);
#Assigns strings to print out the colored characters.
$A='A';
$B='B';
$C='C';
$D='D';
#Begins outputting HTML - most of this is just formatting.
print header;
print start_html(-title=>'Voting Methods', -bgcolor=>'white'),
div({-align=>'center'}, h1('Voting Methods'), i('By Ben McIlwain')),
p({-align=>'left'}, "Four candidates are running for an election. They have the rather boring names
$A, $B, $C, and $D. This simulation is designed to show how different voting tabulation methods will result
in different winners of the election. Four different methods are used: simple plurality, majority with
run-offs, Borda Count, and Condorcet criterion. Your entry consists of the number of people who are in
favor of voting $A>$B>$C>$D, $B>$D>$C>$A, $D>$C>$A>$B, etc. Thus there are twenty-four columns in this
program. At the bottom of each column enter the number of people who hold that opinion. In most cases with
reasonably small populations far less than twenty-four of the columns will be full. This is perfectly fine,
as the other columns will default to zero. Also, note that in the Borda count, a 1st pref is worth 3 points,
a 2nd pref is worth 2 points, a 3rd pref is worth 1 points, and a 4th pref is worth zero points."),
start_form,
#Outputs the 24 data entry columns.
table({-border=>'1'},
Tr({-align=>CENTER, -valign=>TOP},
[
#Displays table headings.
th(['Col. #:', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24']),
th('1st Pref').td([$A, $A, $A, $A, $A, $A, $B, $B, $B, $B, $B, $B, $C, $C, $C, $C, $C, $C, $D, $D, $D, $D, $D, $D]),
th('2nd Pref').td([$B, $B, $C, $C, $D, $D, $A, $A, $C, $C, $D, $D, $A, $A, $B, $B, $D, $D, $A, $A, $B, $B, $C, $C]),
th('3rd Pref').td([$C, $D, $B, $D, $B, $C, $C, $D, $A, $D, $A, $C, $B, $D, $A, $D, $A, $B, $B, $C, $A, $C, $A, $B]),
th('4th Pref').td([$D, $C, $D, $B, $C, $B, $D, $C, $D, $A, $C, $A, $D, $B, $D, $A, $B, $A, $C, $B, $C, $A, $B, $A]),
th('Count:').td([
textfield(-name=>'ABCD', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'ABDC', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'ACBD', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'ACDB', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'ADBC', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'ADCB', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'BACD', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'BADC', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'BCAD', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'BCDA', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'BDAC', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'BDCA', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'CABD', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'CADB', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'CBAD', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'CBDA', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'CDAB', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'CDBA', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'DABC', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'DACB', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'DBAC', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'DBCA', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'DCAB', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0'),
textfield(-name=>'DCBA', -default=>'0', -size=>'2', -maxlength=>'10', -override=>'0')
]),
]
));
#Form submit buttons.
print "Once you have finished entering data, click the button to see the results!", br,
submit(-name=>'Submit', -label=>'Tabulate the vote'),
defaults(-name=>'Reset all fields to 0'),
end_form,
hr;
#If parameters have been entered then the votes are tabulated.
if (param()) {
my_plurality_vote();
my_majority_vote();
my_borda_count_vote();
my_condorcet_criterion_vote();
}
#Prints leading-out section of the HMTL.
print
i('Note: this program is in its completed, post-beta stage.'),
"See the source here or go back to the main site.
",
"
";
print end_html;
#Sub-routine that calculates plurality counts.
sub my_plurality_vote {
#Sums up totals of columns.
my $A_sum = param('ABCD') + param('ABDC') + param('ACBD') + param('ACDB') + param('ADBC') + param('ADCB');
my $B_sum = param('BACD') + param('BADC') + param('BCAD') + param('BCDA') + param('BDAC') + param('BDCA');
my $C_sum = param('CABD') + param('CADB') + param('CBAD') + param('CBDA') + param('CDAB') + param('CDBA');
my $D_sum = param('DABC') + param('DACB') + param('DBAC') + param('DBCA') + param('DCAB') + param('DCBA');
print b("Plurality vote method: "), br
"$A: ", b("$A_sum"), " $B: ", b("$B_sum"), " $C: ", b("$C_sum"), " $D: ", b("$D_sum"), br;
#If statements to determine who to declare as the winner.
if ($A_sum > $B_sum && $A_sum > $C_sum && $A_sum > $D_sum) {
print i("$A wins with $A_sum votes.");
}
elsif ($B_sum > $A_sum && $B_sum > $C_sum && $B_sum > $D_sum) {
print i("$B wins with $B_sum votes.");
}
elsif ($C_sum > $A_sum && $C_sum > $B_sum && $C_sum > $D_sum) {
print i("$C wins with $C_sum votes.");
}
elsif ($D_sum > $A_sum && $D_sum > $B_sum && $D_sum > $C_sum) {
print i("$D wins with $D_sum votes.");
}
else {
print i("It's a tie!");
}
print hr;
}
#Sub-routine that determines a winner using the majority vote method.
sub my_majority_vote() {
#Level one vote tabulation.
my $A_sum = param('ABCD') + param('ABDC') + param('ACBD') + param('ACDB') + param('ADBC') + param('ADCB');
my $B_sum = param('BACD') + param('BADC') + param('BCAD') + param('BCDA') + param('BDAC') + param('BDCA');
my $C_sum = param('CABD') + param('CADB') + param('CBAD') + param('CBDA') + param('CDAB') + param('CDBA');
my $D_sum = param('DABC') + param('DACB') + param('DBAC') + param('DBCA') + param('DCAB') + param('DCBA');
my $total_votes = $A_sum + $B_sum + $C_sum + $D_sum;
print b("Majority vote method: "), br
"$A: ", b("$A_sum"), " $B: ", b("$B_sum"), " $C: ", b("$C_sum"), " $D: ", b("$D_sum"), br;
#A run-off might not be necessary if someone already has >50% of the votes.
if ($A_sum * 2 > $total_votes) {
print i("$A wins with $A_sum votes.");
}
elsif($B_sum * 2 > $total_votes) {
print i("$B wins with $B_sum votes.");
}
elsif($C_sum * 2 > $total_votes) {
print i("$C wins with $C_sum votes.");
}
elsif($D_sum * 2 > $total_votes) {
print i("$D wins with $D_sum votes.");
}
else {
if ($A_sum == $B_sum && $B_sum == $C_sum || $A_sum == $B_sum && $B_sum == $D_sum || $A_sum == $C_sum && $C_sum == $D_sum || $B_sum == $C_sum
&& $C_sum== $D_sum) {
#The contingency for a 3 or 4-way tie in which the run-off is not possible and the outcome is a tie.
print i("It was a true tie.");
}
else {
#A run-off between only two candidates is run.
print "No one won by simple majority, going to run-off ... ", br;
$one = "Z";
$oneSum = 0;
$two = "Z";
$twoSum = 0;
#These conditionals determine the two candidates in the "run-off election" and then compare them against each other.
if ($A_sum > $C_sum && $A_sum > $D_sum && $B_sum > $C_sum && $B_sum > $D_sum) {
$one = "$A";
$two = "$B";
$oneSum = $A_sum + param('CABD') + param('CADB') + param('CDAB') + param('DABC') + param('DACB') + param('DCAB');
$twoSum = $B_sum + param('CBAD') + param('CBDA') + param('CDBA') + param('DBAC') + param('DBCA') + param('DCBA');
}
elsif ($A_sum > $B_sum && $A_sum > $D_sum && $C_sum > $B_sum && $C_sum > $D_sum) {
$one = "$A";
$two = "$C";
$oneSum = $A_sum + param('BACD') + param('BADC') + param('BDAC') + param('DABC') + param('DACB') + param('DBAC');
$twoSum = $C_sum + param('BCAD') + param('BCDA') + param('BDCA') + param('DBCA') + param('DCAB') + param('DCBA');
}
elsif ($A_sum > $B_sum && $A_sum > $C_sum && $D_sum > $B_sum && $D_sum > $C_sum) {
$one = "$A";
$two = "$D";
$oneSum = $A_sum + param('BACD') + param('BADC') + param('BCAD') + param('CABD') + param('CADB') + param('CBAD');
$twoSum = $D_sum + param('BCDA') + param('BDAC') + param('BDCA') + param('CBDA') + param('CDAB') + param('CDBA');
}
elsif ($B_sum > $A_sum && $B_sum > $D_sum && $C_sum > $A_sum && $C_sum > $D_sum) {
$one = "$B";
$two = "$C";
$oneSum = $B_sum + param('ABCD') + param('ABDC') + param('ADBC') + param('DABC') + param('DBAC') + param('DBCA');
$twoSum = $C_sum + param('ACBD') + param('ACDB') + param('ADCB') + param('DACB') + param('DCAB') + param('DCBA');
}
elsif ($B_sum > $A_sum && $B_sum > $C_sum && $D_sum > $A_sum && $D_sum > $C_sum) {
$one = "$B";
$two = "$D";
$oneSum = $B_sum + param('ABCD') + param('ABDC') + param('ACBD') + param('CABD') + param('CBAD') + param('CBDA');
$twoSum = $D_sum + param('ACDB') + param('ADBC') + param('ADCB') + param('CADB') + param('CDAB') + param('CDBA');
}
elsif ($C_sum > $A_sum && $C_sum > $B_sum && $D_sum > $A_sum && $D_sum > $B_sum) {
$one = "$C";
$two = "$D";
$oneSum = $C_sum + param('ABCD') + param('ACBD') + param('ACDB') + param('BACD') + param('BCAD') + param('BCDA');
$twoSum = $D_sum + param('ABDC') + param('ADBC') + param('ADCB') + param('BADC') + param('BDAC') + param('BDCA');
}
#Outputs the winner of the run-off.
if ($oneSum > $twoSum) {
print i(" $one wins the run-off against $two, $oneSum to $twoSum!");
}
elsif ($twoSum > $oneSum) {
print i(" $two wins the run-off against $one, $twoSum to $oneSum!");
}
else {
#The contingency for yet another tie: when the run-off results are tid and there is no winner.
print i(" The run-off is tied! There are no winners here! $one and $two are tied at $oneSum!");
}
}
}
print hr;
}
#Sub-routine for calculating Borda counts.
sub my_borda_count_vote() {
#Adds up first prefs.
my $A_3_sum = param('ABCD') + param('ABDC') + param('ACBD') + param('ACDB') + param('ADBC') + param('ADCB');
my $B_3_sum = param('BACD') + param('BADC') + param('BCAD') + param('BCDA') + param('BDAC') + param('BDCA');
my $C_3_sum = param('CABD') + param('CADB') + param('CBAD') + param('CBDA') + param('CDAB') + param('CDBA');
my $D_3_sum = param('DABC') + param('DACB') + param('DBAC') + param('DBCA') + param('DCAB') + param('DCBA');
#Adds up second prefs.
my $A_2_sum = param('BACD') + param('BADC') + param('CABD') + param('CADB') + param('DABC') + param('DACB');
my $B_2_sum = param('ABCD') + param('ABDC') + param('CBAD') + param('CBDA') + param('DBAC') + param('DBCA');
my $C_2_sum = param('ACBD') + param('ACDB') + param('BCAD') + param('BCDA') + param('DCAB') + param('DCBA');
my $D_2_sum = param('ADBC') + param('ADCB') + param('BDAC') + param('BDCA') + param('CDAB') + param('CDBA');
#Adds up third prefs.
my $A_1_sum = param('BCAD') + param('CBAD') + param('BDAC') + param('DBAC') + param('CDAB') + param('DCAB');
my $B_1_sum = param('ACBD') + param('CABD') + param('ADBC') + param('DABC') + param('CDBA') + param('DCBA');
my $C_1_sum = param('ABCD') + param('BACD') + param('ADCB') + param('DACB') + param('BDCA') + param('DBCA');
my $D_1_sum = param('ABDC') + param('BADC') + param('ACDB') + param('CADB') + param('BCDA') + param('CBDA');
#Adds up sums of prefs (note that 4th prefs aren't counted because they are worth 0 points).
my $A_sum = 3 * $A_3_sum + 2 * $A_2_sum + $A_1_sum;
my $B_sum = 3 * $B_3_sum + 2 * $B_2_sum + $B_1_sum;
my $C_sum = 3 * $C_3_sum + 2 * $C_2_sum + $C_1_sum;
my $D_sum = 3 * $D_3_sum + 2 * $D_2_sum + $D_1_sum;
#Outputting of results.
print b("Borda count method: "), br
"$A: ", b("$A_sum"), " $B: ", b("$B_sum"), " $C: ", b("$C_sum"), " $D: ", b("$D_sum"), br;
if ($A_sum > $B_sum && $A_sum > $C_sum && $A_sum > $D_sum) {
print i("$A wins with $A_sum count.");
}
elsif ($B_sum > $A_sum && $B_sum > $C_sum && $B_sum > $D_sum) {
print i("$B wins with $B_sum count.");
}
elsif ($C_sum > $A_sum && $C_sum > $B_sum && $C_sum > $D_sum) {
print i("$C wins with $C_sum count.");
}
elsif ($D_sum > $A_sum && $D_sum > $B_sum && $D_sum > $C_sum) {
print i("$D wins with $D_sum count.");
}
else {
#Like all other voting methods it is possible to get the result of a tie.
print i("It's a tie!");
}
print hr;
}
#Sub-routine for calculating the winning candidate based on the Condorcet Criterion. In the Condorcet Criterion,
#for a candidate to win, he must win individually against each other candidate as if it were only those two in
#the election. Most often the Condorcet Criterion results in a tie, unless the vote counts are incredibly skewed
#in favor of a single candidate.
sub my_condorcet_criterion_vote() {
#Calculates overall prefs and then alternative prefs for when only 2 of the 4 candidates can be picked.
my $A_sum = param('ABCD') + param('ABDC') + param('ACBD') + param('ACDB') + param('ADBC') + param('ADCB');
my $B_sum = param('BACD') + param('BADC') + param('BCAD') + param('BCDA') + param('BDAC') + param('BDCA');
my $C_sum = param('CABD') + param('CADB') + param('CBAD') + param('CBDA') + param('CDAB') + param('CDBA');
my $D_sum = param('DABC') + param('DACB') + param('DBAC') + param('DBCA') + param('DCAB') + param('DCBA');
my $A_vs_B_sum = $A_sum + param('CABD') + param('CADB') + param('CDAB') + param('DABC') + param('DACB') + param('DCAB');
my $B_vs_A_sum = $B_sum + param('CBAD') + param('CBDA') + param('CDBA') + param('DBAC') + param('DBCA') + param('DCBA');
my $A_vs_C_sum = $A_sum + param('BACD') + param('BADC') + param('BDAC') + param('DABC') + param('DACB') + param('DBAC');
my $C_vs_A_sum = $C_sum + param('BCAD') + param('BCDA') + param('BDCA') + param('DBCA') + param('DCAB') + param('DCBA');
my $A_vs_D_sum = $A_sum + param('BACD') + param('BADC') + param('BCAD') + param('CABD') + param('CADB') + param('CBAD');
my $D_vs_A_sum = $D_sum + param('BCDA') + param('BDAC') + param('BDCA') + param('CBDA') + param('CDAB') + param('CDBA');
my $B_vs_C_sum = $B_sum + param('ABCD') + param('ABDC') + param('ADBC') + param('DABC') + param('DBAC') + param('DBCA');
my $C_vs_B_sum = $C_sum + param('ACBD') + param('ACDB') + param('ADCB') + param('DACB') + param('DCAB') + param('DCBA');
my $B_vs_D_sum = $B_sum + param('ABCD') + param('ABDC') + param('ACBD') + param('CABD') + param('CBAD') + param('CBDA');
my $D_vs_B_sum = $D_sum + param('ACDB') + param('ADBC') + param('ADCB') + param('CADB') + param('CDAB') + param('CDBA');
my $C_vs_D_sum = $C_sum + param('ABCD') + param('ACBD') + param('ACDB') + param('BACD') + param('BCAD') + param('BCDA');
my $D_vs_C_sum = $D_sum + param('ABDC') + param('ADBC') + param('ADCB') + param('BADC') + param('BDAC') + param('BDCA');
#Outputs results.
print b("Condorcet Criterion method: "), br,
"$A vs: $B, $C, $D: ";
#This next LONG section formats the numbers: bold if they were larger than their opponent, italics if they were smaller, and normal font if they were the same.
if ($A_vs_B_sum > $B_vs_A_sum) {
print b($A_vs_B_sum);
}
elsif ($A_vs_B_sum == $B_vs_A_sum) {
print $A_vs_B_sum;
}
else {
print i($A_vs_B_sum);
}
print ' ';
if ($A_vs_C_sum > $C_vs_A_sum) {
print b($A_vs_C_sum);
}
elsif ($A_vs_C_sum == $C_vs_A_sum) {
print $A_vs_C_sum;
}
else {
print i($A_vs_C_sum);
}
print ' ';
if ($A_vs_D_sum > $D_vs_A_sum) {
print b($A_vs_D_sum);
}
elsif ($A_vs_D_sum == $D_vs_A_sum) {
print $A_vs_D_sum;
}
else {
print i($A_vs_D_sum);
}
print br;
print "$B vs: $A, $C, $D: ";
if ($B_vs_A_sum > $A_vs_B_sum) {
print b($B_vs_A_sum);
}
elsif ($B_vs_A_sum == $A_vs_B_sum) {
print $B_vs_A_sum;
}
else {
print i($B_vs_A_sum);
}
print ' ';
if ($B_vs_C_sum > $C_vs_B_sum) {
print b($B_vs_C_sum);
}
elsif ($B_vs_C_sum == $C_vs_B_sum) {
print $B_vs_C_sum;
}
else {
print i($B_vs_C_sum);
}
print ' ';
if ($B_vs_D_sum > $D_vs_B_sum) {
print b($B_vs_D_sum);
}
elsif ($B_vs_D_sum == $D_vs_B_sum) {
print $B_vs_D_sum;
}
else {
print i($B_vs_D_sum);
}
print ' ';
print br;
print "$C vs: $A, $B, $D: ";
if ($C_vs_A_sum > $A_vs_C_sum) {
print b($C_vs_A_sum);
}
elsif ($C_vs_A_sum == $A_vs_C_sum) {
print $C_vs_A_sum;
}
else {
print i($C_vs_A_sum);
}
print ' ';
if ($C_vs_B_sum > $B_vs_C_sum) {
print b($C_vs_B_sum);
}
elsif ($C_vs_B_sum == $B_vs_C_sum) {
print $C_vs_B_sum;
}
else {
print i($C_vs_B_sum);
}
print ' ';
if ($C_vs_D_sum > $D_vs_C_sum) {
print b($C_vs_D_sum);
}
elsif ($C_vs_D_sum == $D_vs_C_sum) {
print $C_vs_D_sum;
}
else {
print i($C_vs_D_sum);
}
print br;
print "$D vs: $A, $B, $C: ";
if ($D_vs_A_sum > $A_vs_D_sum) {
print b($D_vs_A_sum);
}
elsif ($D_vs_A_sum == $A_vs_D_sum) {
print $D_vs_A_sum;
}
else {
print i($D_vs_A_sum);
}
print ' ';
if ($D_vs_B_sum > $B_vs_D_sum) {
print b($D_vs_B_sum);
}
elsif ($D_vs_B_sum == $B_vs_D_sum) {
print $D_vs_B_sum;
}
else {
print i($D_vs_B_sum);
}
print ' ';
if ($D_vs_C_sum > $C_vs_D_sum) {
print b($D_vs_C_sum);
}
elsif ($D_vs_C_sum == $C_vs_D_sum) {
print $D_vs_C_sum;
}
else {
print i($D_vs_C_sum);
}
print br;
if ($A_vs_B_sum > $B_vs_A_sum && $A_vs_C_sum > $C_vs_A_sum && $A_vs_D_sum > $D_vs_A_sum) {
print i("$A wins!");
}
elsif ($B_vs_A_sum > $A_vs_B_sum && $B_vs_C_sum > $C_vs_B_sum && $B_vs_D_sum > $D_vs_B_sum) {
print i("$B wins!");
}
elsif ($C_vs_A_sum > $A_vs_C_sum && $C_vs_B_sum > $B_vs_C_sum && $C_vs_D_sum > $D_vs_C_sum) {
print i("$C wins!");
}
elsif ($D_vs_A_sum > $A_vs_D_sum && $D_vs_B_sum > $B_vs_D_sum && $D_vs_C_sum > $C_vs_D_sum) {
print i("$D wins!");
}
else {
#This occurs the most in a Condorcet Criterion tabulation.
print i("It's a tie!");
}
print hr;
}