Election software/tally.inc (Hare-Clark)
(added countback) |
(add) |
||
Line 1: | Line 1: | ||
[[Category:2013]] | |||
[[Category:Elections]] | |||
<pre> | <pre> | ||
<?php | <?php |
Latest revision as of 13:50, 2 August 2019
<?php $rowlength = 40; // -------------------------------------------------------------------- // Step 0: Grab the category details from the database. // Nothing special here: each category in this case is a position. $sql = "SELECT * FROM Categories WHERE id=".$category; $result = mysql_query($sql); $row = mysql_fetch_array($result, MYSQL_ASSOC); $output .= "<h2>".$row['name']."</h2>"; // Step 1: Grab the nominees $sql = "SELECT * FROM Nominees WHERE category=".$category; $numberOfNominees = 0; $result = mysql_query($sql); if ($result && mysql_num_rows($result) > 0) { while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { $nomineeID[$numberOfNominees] = $row['id']; $nomineeDescription[$numberOfNominees] = $row['description']; $nomineePrecluded[$numberOfNominees] = 0; $numberOfNominees++; } } $output .= "<ol>"; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { $output .= "<li>".$nomineeDescription[$nominee]."</li>"; } $output .= "</ol>"; // Step 2: Let's load all the votes into an array. for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { $sql = "SELECT * FROM Votes, Voters WHERE voterid = Voters.id AND approved = 1 AND nomineeid = ".$nomineeID[$nominee]; $result = mysql_query($sql); if ($result && mysql_num_rows($result) > 0) { while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { $vote[$row['voterid']][$row['nomineeid']] = $row['vote']; } } } // ------------------------------------------------------------------------------------ // Ok. It is possible that we need to preclude nominees from the election results. // This code drops them from the election. $finalNominees = 0; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { if ($_REQUEST[$nomineeID[$nominee]."_checked"] == "on") { $vote = removeNominee($vote,$nomineeID[$nominee]); $output .= "<li>".$nomineeDescription[$nominee]." has been removed from the election.</li>"; $nomineePrecluded[$nominee] = 1; } else { $finalNominees++; } } $round = 0; $finished = false; // ------------------------------------------------------------------------------------ // Calculate the required quota. $output .= "<h3>Calculating Quota</h3>\n"; $output .= "<p>Total non-precluded nominees: ".$finalNominees."</p>\n"; $positions = stripslashes($_REQUEST['positions']); $output .= "<p>Positions available: ".$positions."</p>\n"; // Check for valid votes $validVotes = 0; foreach ($vote as $voterID => $votes) { if (checkValidity($votes,1) == "valid") $validVotes++; $voterWeight[$voterID] = 1; } $output .= "<p>Valid votes: ".$validVotes."</p>\n"; if ($validVotes > 0 && $positions > 0 && $finalNominees > 0) { $output .= "<p>Quota: Quota = (valid votes / (positions + 1)) + 1 (Droop’s Quota)<br />\n"; $output .= "Quota: Quota = (".$validVotes." / (".$positions." + 1)) + 1<br />\n"; $quota = floor(($validVotes / ($positions + 1)) + 1); $output .= "Quota: ".$quota."</p>\n"; // ------------------------------------------------------------------------------------ $output .= "<h3>Calculating Result</h3>\n"; while (!$finished) { if (++$roundCount > 50) ($finished = 1); $output .= "<h3>Round ".($round + 1)."</h3>\n"; // Step 3: We need to confirm that the votes are good. // This checks for bad votes. foreach ($vote as $voterID => $votes) { $vote[$voterID]['Valid'] = checkValidity($votes,1); } $votercount = 0; $tablecount = 0; $tables = ""; // I need to list the voter IDs so that they can be checked. This displays them, // and also initialises the tables I'll need for display - I want to show all the results, // but don't want to do a next/previous thing as it want it possible to print everything, // so this breaks them up into separate tables based on a preset number of voters per table. $tables[$tablecount] .= "<tr bgcolor=\"#cccccc\">\n"; $tables[$tablecount] .= "<td><br /></td>\n"; foreach ($vote as $voterID => $votes) { if (($votercount > 0) && (($votercount % $rowlength) == 0)) { $tables[$tablecount] .= "</tr>\n"; $tablecount++; $tables[$tablecount] .= "<tr><td colspan=\"".($rowlength + 1)."\"><br /></td></tr><tr bgcolor=\"#cccccc\">\n"; $tables[$tablecount] .= "<td><br /></td>\n"; } $tables[$tablecount] .= "<td>".$voterID."</td>\n"; $votercount++; } $tables[$tablecount] .= "</tr>\n"; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { $tablecount = -1; $votercount = 0; $nomineeCount[$nominee][$round] = 0; foreach ($vote as $voterID => $votes) { if ((($votercount % $rowlength) == 0)) { $tables[$tablecount] .= "</tr>\n"; $tablecount++; $tables[$tablecount] .= "<tr>\n<td bgcolor=\"#cccccc\">".($nominee + 1)."</td>\n"; } $colour = "#ffffff"; if ($votes[$nomineeID[$nominee]] == 1 && $vote[$voterID]['Valid'] == "valid") { $colour = "#33ff66"; $nomineeCount[$nominee][$round] += 1 * $voterWeight[$voterID]; if ($voterWeight[$voterID] != 1) { $colour = "#ffff00"; } } if ($vote[$voterID]['Valid'] == "invalid") { $colour = "#ff9900"; } $tables[$tablecount] .= "<td bgcolor=\"".$colour."\">".$votes[$nomineeID[$nominee]]."</td>\n"; $votercount++; } $tables[$tablecount] .= "</tr>\n"; } $output .= '<table cellpadding="2" cellspacing="0" border="1">'."\n"; for ($table = 0; $table <= $tablecount; $table++) { $output .= $tables[$table]; } $output .= "</table>\n"; //Step : Work out if we have a winner. $biggest = 0; $noscoreCount = 0; $biggestCount = 0; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { if($nomineeCount[$nominee][$round] > $biggest) { $biggest = $nomineeCount[$nominee][$round]; $biggestCount = 1; } else if ($nomineeCount[$nominee][$round] == $biggest) { $biggestCount++; } if ($nomineeCount[$nominee][$round] == 0) { $noscoreCount++; } } $lowest = $biggest; $lowestCount = 0; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { if ($nomineePrecluded[$nominee]) { // Used for debugging to confirm that a nominee is now precluded } else if($nomineeCount[$nominee][$round] < $lowest) { $lowest = $nomineeCount[$nominee][$round]; $lowestCount = 1; } else if ($nomineeCount[$nominee][$round] == $lowest) { $lowestCount++; } } $output .= '<br \><table cellpadding="2" cellspacing="0" border="1">'."\n<tr>\n<td><br /></td>\n"; $round++; for ($roundnumber = 0; $roundnumber < $round; $roundnumber++) { $output .= "<td bgcolor=\"#cccccc\">Round ".($roundnumber + 1)."</td>\n"; } $output .= "</tr>\n"; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { $output .= "<tr><td bgcolor=\"#cccccc\">".$nomineeDescription[$nominee]."</td>\n"; for ($roundnumber = 0; $roundnumber < $round; $roundnumber++) { $output .= "<td"; if ($roundnumber == $round - 1) { if ($nomineePrecluded[$nominee]) { $output .= " bgcolor=\"#ff0000\""; } else { if ($nomineeCount[$nominee][$roundnumber] == $biggest) { $output .= " bgcolor=\"#33ff66\""; } if ($nomineeCount[$nominee][$roundnumber] == $lowest) { $output .= " bgcolor=\"#ff9900\""; } } } $output .= ">".$nomineeCount[$nominee][$roundnumber]."</td>\n"; } $output .= "</tr>\n"; } $output .= "</table>\n"; //Step : Report on the end of the first round. $output .= "<p>At the end of Round ".$round.", "; // See if anyone passed the quota if ($biggest >= $quota) { $output .= "there is a winner.</p>"; $output .="<h3>Winner</h3>\n"; $output .= "<ul>"; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { if ($nomineeCount[$nominee][$round-1] >= $quota) { $output .= "<li>".$nomineeDescription[$nominee]."</li>\n"; // Increase the winners count $winners++; // Have we enough winners to stop now? if ($winners == $positions) { $finished = true; } else { $nomineeWon[$nominee] = 1; $voteWeight = ($nomineeCount[$nominee][$round-1] - $quota) / $nomineeCount[$nominee][$round-1]; foreach ($vote as $voterID => $votes) { if ($vote[$voterID]['Valid'] == "valid" AND $votes[$nomineeID[$nominee]] == 1) { $voterWeight[$voterID] = $voteWeight; } } $vote = removeNominee($vote, $nomineeID[$nominee]); $nomineePrecluded[$nominee] = 1; } } } $output .= "</ul>\n"; if ($winners < $positions) $output .= "<p>As there are still positions to fill, preferences for successful voters have been reallocated.</p>"; } else { $output .= " there is no clear winner.</p>"; if ($lowestCount >= 1) { $lowestID = -1; $target = 0; // By default, we are going to remove the nominee to get the lowest score. $targetCount = 0; // First, find out the ID of the lowest score. // This is a bit tricky, as there may be a tie, and that will require a // count back. $tempRound = $round - 1; $lowestIDs = array(); $lowestCount = 0; for ($nominee = 0; $nominee < $numberOfNominees; $nominee++) { if ($nomineeCount[$nominee][$tempRound] == $lowest && !$nomineePrecluded[$nominee]) { $lowestIDs[] = $nominee; $lowestCount++; } } // Did we need to do some back counting to find who to exclude? if ($lowestCount > 1) { $tempRound--; $foundID = 0; while ($tempRound >= 0 && !$foundID) { // Find the lowest $tempLowest = $nomineeCount[$lowestIDs[0]][$tempRound]; foreach($lowestIDs as $tempNominee) { if ($nomineeCount[$tempNominee][$tempRound] < $tempLowest) { $tempLowest = $nomineeCount[$tempNominee][$tempRound]; } } $tempLowestIDs = array(); $lowestCount = 0; foreach($lowestIDs as $tempNominee) { if ($nomineeCount[$tempNominee][$tempRound] == $tempLowest) { $tempLowestIDs[] = $tempNominee; $lowestCount++; } } $lowestIDs = $tempLowestIDs; if ($lowestCount == 1) { $foundID = 1; } else { $tempRound--; } } } if ($lowestCount == 1) { $lowestID = $lowestIDs[0]; } else { $output .= "<p>As there were ".$lowestCount." nominees with the lowest "; $output .= "score after countback, one will randomly be removed.<p>"; $lowestID = $lowestIDs[rand(0,$lowestCount-1)]; } $output .= "<p>Removing votes for ".$nomineeDescription[$lowestID].".</p>"; $nomineePrecluded[$lowestID] = 1; // Removes them from the count. $output .= "<p>Reset the weighting, as a candidate has been excluded.</p>"; foreach ($vote as $voterID => $votes) { if (checkValidity($votes,1) == "valid") $validVotes++; $voterWeight[$voterID] = 1; } // Code to remove votes. foreach ($vote as $voterID => $votes) { if ($vote[$voterID]['Valid'] == "invalid") { unset($vote[$voterID]); } else { $found = false; $target = $votes[$nomineeID[$lowestID]]; if ($target > 0) { $vote[$voterID][$nomineeID[$lowestID]] = 0; foreach($votes as $voteNomineeID => $voteScore) { if ($vote[$voterID][$voteNomineeID] >= $target) { $vote[$voterID][$voteNomineeID]--; } if ($vote[$voterID][$voteNomineeID] <= 0) { unset($vote[$voterID][$voteNomineeID]); } } } } } $output .= "<h4>Calculating new quota</h4>\n"; $output .= "<p>Positions remaining: ".($positions - $winners)."</p>\n"; // Check for valid votes $validVotes = 0; foreach ($vote as $voterID => $votes) { if (checkValidity($votes,1) == "valid") $validVotes++; } $output .= "<p>Valid votes: ".$validVotes."</p>\n"; if ($validVotes > 0 && $positions > 0) { $output .= "<p>Quota: Quota = (valid votes / (positions + 1)) + 1 (Droop’s Quota)<br />\n"; $output .= "Quota: Quota = (".$validVotes." / (".($positions - $winners)." + 1)) + 1<br />\n"; $quota = floor(($validVotes / (($positions - $winners) + 1)) + 1); $output .= "Quota: ".$quota."</p>\n"; } } } } } // -------------------------------------------------------------------------------------- function removeNominee($vote, $nominee) { // Code to remove votes. foreach ($vote as $voterID => $votes) { if ($vote[$voterID]['Valid'] == "invalid") { unset($vote[$voterID]); } else { $found = false; $target = $votes[$nominee]; if ($target > 0) { $vote[$voterID][$nominee] = 0; foreach($votes as $voteNomineeID => $voteScore) { if ($vote[$voterID][$voteNomineeID] >= $target) { $vote[$voterID][$voteNomineeID]--; } if ($vote[$voterID][$voteNomineeID] <= 0) { unset($vote[$voterID][$voteNomineeID]); } } } } } return $vote; } // -------------------------------------------------------------------------------------- function checkValidity($votes, $value) { $validvotes = 0; foreach ($votes as $key => $vote) { if ($vote == $value) { ++$validvotes; } } $result = "invalid"; if ($validvotes == 1) { $result = "valid"; } return $result; } ?>