# C language: multi-round voting

In the third week of the Computer Science CS50 (Harvard) course, it is proposed to do homework in Problem Set 3 called runoff.c – you need to write a program code that does not just calculate which candidate received the most votes, like last time, in the runoff program you need to implement voting until more than 50% of the votes of all voters are collected.

The winner will be announced only when one of the candidates receives more than half of the votes. To do this, each voter ranks all candidates from highest preference (rank 1) to least preference (last rank). Next, the sum of votes in the first round (rank 1) is calculated. If one of the candidates receives more than 50% of the votes at this stage, he is declared the winner.

If there is no winner in the first round, then you need to find the candidate who received the least number of votes and remove him from the list of candidates so that only the remaining candidates participate in the counting of votes in the next round. And so on until there is a winner. If it turns out that everyone in the election has the same number of votes, then a draw is declared and they are all winners.

Igroglaz’s first solution. The initial tests passed, but cs50 showed a lot of jambs:

```#include <stdio.h>
#include <string.h>
#include <stdbool.h>

// Max voters and candidates
#define MAX_VOTERS 100
#define MAX_CANDIDATES 9

// preferences[i][j] is jth preference for voter i
int preferences[MAX_VOTERS][MAX_CANDIDATES]; // предпочтения

// Candidates have name, vote count, eliminated status
typedef struct
{
char name;
bool eliminated;
}
candidate;

// Array of candidates
candidate candidates[MAX_CANDIDATES];

// Numbers of voters and candidates
int voter_count;
int candidate_count;

// Function prototypes
bool vote(int voter, int rank, char *name);
void tabulate(void);
bool print_winner(void);
int find_min(void);
bool is_tie(int min);
void eliminate(int min);

int main(int argc, char *argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: runoff [candidate ...]\n");
return 1;
}

// Populate array of candidates
candidate_count = argc - 1;
if (candidate_count > MAX_CANDIDATES)
{
printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
return 2;
}
for (int i = 0; i < candidate_count; i++)
{
strcpy (candidates[i].name, argv[i + 1]);
candidates[i].eliminated = false;
}

printf("Number of voters: ");
scanf("%d", &voter_count);
while (getchar() != '\n') ; // clear buffer

if (voter_count > MAX_VOTERS)
{
printf("Maximum number of voters is %i\n", MAX_VOTERS);
return 3;
}

for (int i = 0; i < voter_count; i++)
{

// Query for each rank
for (int j = 0; j < candidate_count; j++)
{

char name; // there user enters name of candidate when voting

printf("Rank %i: ", j + 1);
scanf("%s", name);

// Record vote, unless it's invalid
if (!vote(i, j, name))
{
printf("Invalid vote.\n");
return 4;
}
}

printf("\n");
}

// Keep holding runoffs until winner exists
while (true)
{
// Calculate votes given remaining candidates
tabulate();

// Check if election has been won
bool won = print_winner();
if (won)
{
break;
}

// Eliminate last-place candidates
int min = find_min();
bool tie = is_tie(min);

// If tie, everyone wins
if (tie)
{
for (int i = 0; i < candidate_count; i++)
{
if (!candidates[i].eliminated)
{
printf("%s\n", candidates[i].name);
}
}
break;
}

// Eliminate anyone with minimum number of votes
eliminate(min);

// Reset vote counts back to zero
for (int i = 0; i < candidate_count; i++)
{
}
}
getchar();
getchar();
return 0;
}

// Record preference if vote is valid
bool vote(int voter, int rank, char *name)
{
for (int i = 0; i < candidate_count; i++)
{
if (!strcmp(name, candidates[i].name))
{
preferences[voter][rank] = i;
//debug printf("%c - voter:%d rank:%d pref:%d\n", *name, voter, rank, preferences[voter][rank]);
return true;
}

}

return false;
}

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
// go through all voters and check whom they give highest preference
// 1) take voter
// 2) find this voter 'rank 0' preffered candidate
// 3) increase votes number of this candidate

for (int i = 0; i < voter_count; i++)
{
for (int j = 0; j < candidate_count; j++)
{
if (preferences[i] == j && !candidates[j].eliminated)
{
break;
}
}
}
}

// Print the winner of the election, if there is one
bool print_winner(void)
{
// 1) go through candidates array and check who got highest vote
// 2) if there is a candidate who > 1/2 votes - he wins

int top = 0; // candidate who has most votes

for (int i = 0; i < candidate_count; i++)
{
top = i;
}

if (candidates[top].votes > (voter_count / 2))
{
printf("Winner: %s", candidates[top].name);
return true;
}
else
return false;

}

// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
// go through all candidates and find out who has least votes
// 1) take 1st candidate and store it's number of votes
// 2) compare 1st candidate with further candidates to find min

int first = 0; // flag which indicate first possible 'min' candidate
int min;

for (int i = 1; i < candidate_count; i++)
{
if (!first && !candidates[i].eliminated)
{
first = 1; // 1st min candidate found, so check flag
}

if (first && !candidates[i].eliminated && candidates[i].votes < min)
{
return min;
}
}

return 0;
}

// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
// find out is there candidates with the same vote number
// 1) go through candidates
// 2) if candidate eliminated or got 'min' votes - ignore it
// 3) if all 'active' candidates got same number of votes - tie
//    a) compare votes numbers with == between each other.
//    b) if even one isn't the same - false

int cmpvote = 0;

for (int i = 1; i < candidate_count; i++)
{
if (!cmpvote && !candidates[i].eliminated && (candidates[i].votes != min))
cmpvote = candidates[i].votes; // 1st valid candidate found

if (cmpvote && !(candidates[i].votes == cmpvote))
return false;
}
return true;
}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
// check which candidate got 'min' and eliminate him
for (int i = 1; i < candidate_count; i++)
{
candidates[i].eliminated = true;
}

return;
}```

The second solution from Igroglaz, corrected:

```#include <stdio.h>
#include <string.h>
#include <stdbool.h>
// I prefer to write code with vanilla C libraries,
// but this time I had to include cs50.h cause without it this program fails to compile for 'check50'
#include <cs50.h>

// Max voters and candidates
#define MAX_VOTERS 100
#define MAX_CANDIDATES 9

// preferences[i][j] is jth preference for voter i
int preferences[MAX_VOTERS][MAX_CANDIDATES]; // предпочтения

// Candidates have name, vote count, eliminated status
typedef struct
{
string name; // had to change it from 'char name' to 'string' to make 'check50' work...
bool eliminated;
}
candidate;

// Array of candidates
candidate candidates[MAX_CANDIDATES];

// Numbers of voters and candidates
int voter_count;
int candidate_count;

// Function prototypes
bool vote(int voter, int rank, char *name);
void tabulate(void);
bool print_winner(void);
int find_min(void);
bool is_tie(int min);
void eliminate(int min);

int main(int argc, char *argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: runoff [candidate ...]\n");
return 1;
}

// Populate array of candidates
candidate_count = argc - 1;
if (candidate_count > MAX_CANDIDATES)
{
printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
return 2;
}
for (int i = 0; i < candidate_count; i++)
{
strcpy (candidates[i].name, argv[i + 1]);
candidates[i].eliminated = false;
}

printf("Number of voters: ");
scanf("%d", &voter_count);
while (getchar() != '\n') ; // clear buffer

if (voter_count > MAX_VOTERS)
{
printf("Maximum number of voters is %i\n", MAX_VOTERS);
return 3;
}

for (int i = 0; i < voter_count; i++)
{

// Query for each rank
for (int j = 0; j < candidate_count; j++)
{

char name; // there user enters name of candidate when voting

printf("Rank %i: ", j + 1);
scanf("%s", name);

// Record vote, unless it's invalid
if (!vote(i, j, name))
{
printf("Invalid vote.\n");
return 4;
}
}

printf("\n");
}

// Keep holding runoffs until winner exists
while (true)
{
// Calculate votes given remaining candidates
tabulate();

// Check if election has been won
bool won = print_winner();
if (won)
{
break;
}

// Eliminate last-place candidates
int min = find_min();
bool tie = is_tie(min);

// If tie, everyone wins
if (tie)
{
for (int i = 0; i < candidate_count; i++)
{
if (!candidates[i].eliminated)
{
printf("%s\n", candidates[i].name);
}
}
break;
}

// Eliminate anyone with minimum number of votes
eliminate(min);

// Reset vote counts back to zero
for (int i = 0; i < candidate_count; i++)
{
}
}

return 0;
}

// Record preference if vote is valid
bool vote(int voter, int rank, char *name)
{
for (int i = 0; i < candidate_count; i++)
{
if (!strcmp(name, candidates[i].name))
{
preferences[voter][rank] = i;
//debug printf("%c - voter:%d rank:%d pref:%d\n", *name, voter, rank, preferences[voter][rank]);
return true;
}

}

return false;
}

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
// go through all voters and check whom they give highest preference
// 1) take voter
// 2) find this voter 'rank 0' preffered candidate
// 3) increase votes number of this candidate

for (int voter = 0; voter < voter_count; voter++)
{
// 'flag' - to show that next valid candidate after elimineted one is found
// and it's time to break cycle
for (int r = 0, flag = 0; r < candidate_count; r++) // cycle through ranks
{
for (int c = 0; c < candidate_count; c++) // cycle through possible candidates
{
if (preferences[voter][r] == c && !candidates[c].eliminated)
{
flag = 1;
break;
}
}
if (flag == 1)
{
break;
}
}
}
}

// Print the winner of the election, if there is one
bool print_winner(void)
{
// 1) go through candidates array and check who got highest vote
// 2) if there is a candidate who > 1/2 votes - he wins

int top_votes = 0; // biggest number of votes for particular candidate at election
int c; // id of candidate with biggest number of votes

for (int i = 0; i < candidate_count; i++)
{
{
c = i;
}
}

if (candidates[c].votes > (voter_count / 2))
{
printf("%s\n", candidates[c].name);
return true;
}
else
return false;
}

// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
// go through all candidates and find out who has least votes
// 1) take 1st candidate and store it's number of votes
// 2) compare 1st candidate with further candidates to find min

int first = 0; // flag which indicate first possible 'min' candidate
int min = 0;

for (int i = 0; i < candidate_count; i++)
{
if (!first && !candidates[i].eliminated)
{
first = 1; // 1st min candidate found, so check flag
}

if (first && !candidates[i].eliminated && candidates[i].votes < min)
{
}
}

return min;
}

// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
// find out is there candidates with the same vote number
// 1) go through candidates
// 2) if candidate eliminated - ignore it
// 3) if all 'active' candidates got same number of votes - tie
//    a) compare votes numbers with == between each other.
//    b) if even one isn't the same - false

int cmpvote = 0; // flag; to find 1st valid candidate for comparing

for (int i = 0; i < candidate_count; i++)
{
if (!candidates[i].eliminated)
{
if (!cmpvote)
cmpvote = candidates[i].votes; // 1st valid candidate for comparison is found
else
{
return false;
}
}
}
return true;
}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
// check which candidate got 'min' and eliminate him
for (int i = 0; i < candidate_count; i++)
{
candidates[i].eliminated = true;
}

return;
}```

Solution (by Shtukentsia)

For me, this task turned out to be one of the most difficult at the moment. I spent several hours trying to figure out exactly what needs to be done. I did not think to watch the video with explanations, and as a result, I spent the whole day delving into the essence of the task. The diagrams on paper helped me with this:  If I had watched the video, I could have saved a few hours of torment. As soon as I roughly understood the task, the second problem arose: to understand the individual functions. I managed to make the first `bool vote(int voter, int rank, string name)` function more or less quickly.

But with the second I was at a dead end. I spent the whole day trying to come up with an algorithm for `void tabulate(void)`, and ended up writing a terribly long code with a lot of nested loops. I think that this is a completely suboptimal solution, but it didn’t work out differently.

Another important point about the option when the vote turns out to be a “draw”, if you enter two candidates A and B, and vote first A B, and then B A, then my program gave a strange error floating point exception (core dumped), this is due to arithmetic except when dividing by zero, or if there is a signed integer overflow.

While I was debugging the program, it turned out that the problem was in the void eliminate(int min) function, which should remove the candidates with the fewest votes. After all, if we have two candidates A and B, and two people vote for them (the first: A B; the second B A), then it turns out that at the first step everyone has 1 vote, and 1 is the least number of votes.

The result is the following program code:

```#include <cs50.h>
#include <stdio.h>
#include <string.h>

// Max voters and candidates
#define MAX_VOTERS 100
#define MAX_CANDIDATES 9

// preferences[i][j] is jth preference for voter i
int preferences[MAX_VOTERS][MAX_CANDIDATES];

// Candidates have name, vote count, eliminated status
typedef struct
{
string name;
bool eliminated;
}
candidate;

// Array of candidates
candidate candidates[MAX_CANDIDATES];

// Numbers of voters and candidates
int voter_count;
int candidate_count;

// Function prototypes
bool vote(int voter, int rank, string name);
void tabulate(void);
bool print_winner(void);
int find_min(void);
bool is_tie(int min);
void eliminate(int min);

int main(int argc, string argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: runoff [candidate ...]\n");
return 1;
}

// Populate array of candidates
candidate_count = argc - 1;
if (candidate_count > MAX_CANDIDATES)
{
printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
return 2;
}
for (int i = 0; i < candidate_count; i++)
{
candidates[i].name = argv[i + 1];
candidates[i].eliminated = false;
}

voter_count = get_int("Number of voters: ");
if (voter_count > MAX_VOTERS)
{
printf("Maximum number of voters is %i\n", MAX_VOTERS);
return 3;
}

for (int i = 0; i < voter_count; i++)
{

// Query for each rank
for (int j = 0; j < candidate_count; j++)
{
string name = get_string("Rank %i: ", j + 1);

// Record vote, unless it's invalid
if (!vote(i, j, name))
{
printf("Invalid vote.\n");
return 4;
}
}
printf("\n");
}

// Keep holding runoffs until winner exists
while (true)
{
// Calculate votes given remaining candidates
tabulate();

// Check if election has been won
bool won = print_winner();
if (won)
{
break;
}

// Eliminate last-place candidates
int min = find_min();
bool tie = is_tie(min);

// If tie, everyone wins
if (tie)
{
for (int i = 0; i < candidate_count; i++)
{
if (!candidates[i].eliminated)
{
printf("%s\n", candidates[i].name);
}
}
printf("Tie!\n");
break;
}

// Eliminate anyone with minimum number of votes
eliminate(min);

// Reset vote counts back to zero
for (int i = 0; i < candidate_count; i++)
{
}
}
return 0;
}

// Record preference if vote is valid
bool vote(int voter, int rank, string name)
{
for (int i = 0; i < candidate_count; i++)
{
if (!strcmp(name,candidates[i].name))
{
preferences[voter][rank]=i;
return true;
}
}
return false;
}

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
for (int j=0; j<candidate_count; j++)
{
for (int i=0; i<candidate_count; i++)
{
for (int z=0; z<voter_count; z++)
{
int prefer_name_index = preferences[z][j];
if (!candidates[prefer_name_index].eliminated)
{
}
if (candidates[prefer_name_index].eliminated)
{
for (int runoff_j = 1; runoff_j<candidate_count;runoff_j++)
{
prefer_name_index = preferences[z][runoff_j];
if (!candidates[prefer_name_index].eliminated)
{
}
}
}
}
break;
}
break;
}
return;
}

// Print the winner of the election, if there is one
bool print_winner(void)
{
string winner;

for (int i = 0; i < candidate_count; i++)
{
{
printf("Winner: %s\n", candidates[i].name);
return true;
}
}
return false;
}

// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
int find_min = voter_count;
for (int i = 0; i<candidate_count; i++)
{
{
}
}
return find_min;
}

// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
int still_in = 1;
int still_in_cand[candidate_count];

for (int i = 0; i<candidate_count; i++)
{
if(!candidates[i].eliminated)
{
still_in_cand[i]=i;
still_in+=1;
}
}

for (int j = 0; j<still_in; j++)
{
if(still_in_cand[j]!=tie_vote)
return false;
}
return true;
}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
for (int i=0; i<candidate_count; i++)
{
candidates[i].eliminated=true;
}
return;
}```

We test the program when voting is a draw: I thought the program worked well until I tested it with 5 voters: The result is strange.

Therefore, I decided to check the code through a debugger, and found a lot of bugs. It turned out that two functions did not work correctly for me – the calculation of the votes of the current round and the check for a draw. I had to rewrite them completely. Here’s what I got:

```#include <cs50.h>
#include <stdio.h>
#include <string.h>

// Max voters and candidates
#define MAX_VOTERS 100
#define MAX_CANDIDATES 9

// preferences[i][j] is jth preference for voter i
int preferences[MAX_VOTERS][MAX_CANDIDATES];

// Candidates have name, vote count, eliminated status
typedef struct
{
string name;
bool eliminated;
}
candidate;

// Array of candidates
candidate candidates[MAX_CANDIDATES];

// Numbers of voters and candidates
int voter_count;
int candidate_count;

// Function prototypes
bool vote(int voter, int rank, string name);
void tabulate(void);
bool print_winner(void);
int find_min(void);
bool is_tie(int min);
void eliminate(int min);

int main(int argc, string argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: runoff [candidate ...]\n");
return 1;
}

// Populate array of candidates
candidate_count = argc - 1;
if (candidate_count > MAX_CANDIDATES)
{
printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
return 2;
}
for (int i = 0; i < candidate_count; i++)
{
candidates[i].name = argv[i + 1];
candidates[i].eliminated = false;
}

voter_count = get_int("Number of voters: ");
if (voter_count > MAX_VOTERS)
{
printf("Maximum number of voters is %i\n", MAX_VOTERS);
return 3;
}

for (int i = 0; i < voter_count; i++)
{

// Query for each rank
for (int j = 0; j < candidate_count; j++)
{
string name = get_string("Rank %i: ", j + 1);

// Record vote, unless it's invalid
if (!vote(i, j, name))
{
printf("Invalid vote.\n");
return 4;
}
}
printf("\n");
}

// Keep holding runoffs until winner exists
while (true)
{
// Calculate votes given remaining candidates
tabulate();

// Check if election has been won
bool won = print_winner();
if (won)
{
break;
}

// Eliminate last-place candidates
int min = find_min();
bool tie = is_tie(min);

// If tie, everyone wins
if (tie)
{
for (int i = 0; i < candidate_count; i++)
{
if (!candidates[i].eliminated)
{
printf("%s\n", candidates[i].name);
}
}
printf("Tie!\n");
break;
}

// Eliminate anyone with minimum number of votes
eliminate(min);

// Reset vote counts back to zero
for (int i = 0; i < candidate_count; i++)
{
}
}
return 0;
}

// Record preference if vote is valid
bool vote(int voter, int rank, string name)
{
for (int i = 0; i < candidate_count; i++)
{
if (!strcmp(name, candidates[i].name))
{
preferences[voter][rank] = i;
return true;
}
}
return false;
}

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
for (int i = 0; i < voter_count; i++)
{
for (int j = 0; j < candidate_count; j++)
{
int id = preferences[i][j];

if (!candidates[id].eliminated)
{
break;
}

else
{
for (int z = 1; z < candidate_count; z++)
{
int id_elim = preferences[i][z];

if (!candidates[id_elim].eliminated)
{
break;
}
}
break;
}
}
}

return;
}

// Print the winner of the election, if there is one
bool print_winner(void)
{
float half_votes = voter_count * 0.5;
string winner;

for (int i = 0; i < candidate_count; i++)
{
{
printf("Winner: %s\n", candidates[i].name);
return true;
}
}
return false;
}

// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
int find_min = voter_count;
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].votes < find_min && !candidates[i].eliminated)
{
}
}
return find_min;
}

// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
for (int i = 0; i < candidate_count; i++)
{
if (candidates[i].votes != min && !candidates[i].eliminated)
{
return false;
}
}
return true;
}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
for (int i = 0; i < candidate_count; i++)
{
{
candidates[i].eliminated = true;
}
}
return;
}```

Testing the program: Hooray, now everything works correctly!

This assignment turned out to be very difficult for my level, so I figured it out for several days, maybe even about a week. Many times I wanted to quit everything, because there was a feeling that such a task was too tough for me. The bear was very supportive and believed that everything would work out. And we continued to move forward step by step. And now that the program has started working, I am very happy and proud of us that we are so good!

The task was designed in a very unusual and interesting way, so you learn a lot while completing them. It is important to do everything yourself, and not be guided by the ideas of other people. Bear and I looked at each other’s code only after our programs were fully operational. And it turned out that each of us has a completely unique approach to writing code. Do not look at other people’s ready-made solutions, this will not help you learn how to program. If something does not work out for you, do not despair and give yourself more time.

Everything will work out!

This entry was posted in C language (en). Bookmark the permalink.