-1

The code I write has to run a runoff election. I have finished all of the code already that I have to do. However, I get an issue in the tabulate function. I passed all the checks when I ran it through check50 for all my code except a specific error in the tabulate function.

Some questions I got asked for a reproducible example. I found an example someone used for code and it produces the error in my code as well. I inputted 4 candidates (a,b,c,d) and 5 voters with their preferences ranked. The winner of this election should be candidate b. However, my code outputs candidates a, b, and d as the winners.

Voter 1: d,c,b,a

Voter 2: a,b,c,d

Voter 3: a,b,c,d

Voter 4: b,c,a,d

Voter 5: b,c,a,d

The specific error I am getting is -

:( tabulate handles multiple rounds of preferences tabulate did not produce correct vote totals

I've looked through multiple solutions people have had on here but couldn't find anything related to the code I wrote.

This is my 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;
    int votes;
    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].votes = 0;
        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;
    }

    // Keep querying for votes
    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);
                }
            }
            break;
        }

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

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

// Record preference if vote is valid
bool vote(int voter, int rank, string name)
{
    // TODO
    //loop over all candidates, and within each voter saves their preferences for each candidate
    for (int i = 0; i < candidate_count; i++)
    {
        if (strcmp(candidates[i].name, name) == 0)
        {
            preferences[voter][rank] = i;
            return true;
        }
    }
    return false;
}

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
    // TODO

    //loop over all voters
    for (int i = 0; i < voter_count; i++)
    {
        //check voter(i's) 1st preference(0th index), if voter(i) 1st preference isn't eliminated, update vote for candidate 1(0th index)
        //if voter(i) 1st preference is eliminated, check voter(i) 2nd preference
        //if 2nd preference isn't eliminated, update vote for candidate corresponding to 2nd candidate, voter[i][j+1]
        for (int j = 0; j < candidate_count; j++)
        {
            if (candidates[preferences[i][j]].eliminated == false)
            {
                candidates[preferences[i][j]].votes++;
            }
            else if(candidates[preferences[i][j]].eliminated == true)
            {
                candidates[preferences[i][j+1]].votes++;
            }
            break;
        }
    }
    return;
}

// Print the winner of the election, if there is one
bool print_winner(void)
{
    // TODO
    //define variable highest_vote to zero
    int majority_vote = voter_count/2;

    //loop over every candidate, if their vote count is higher than highest_vote, set highest_vote equal to the highest candidates vote count
    for (int i = 0; i < candidate_count; i++)
    {
        if (candidates[i].votes > majority_vote)
        {
            printf("%s\n", candidates[i].name);
            return true;
        }
    }
    return false;
}

// Return the minimum number of votes any remaining candidate has
int find_min(void)
{
    // TODO
    int min_votes = voter_count;

    //loop through all candidates to find lowest vote count
    for (int i = 0; i < candidate_count; i++)
    {
        if (candidates[i].eliminated == false && candidates[i].votes < min_votes)
        {
            min_votes = candidates[i].votes;
        }
    }
    return min_votes;
}

// Return true if the election is tied between all candidates, false otherwise
bool is_tie(int min)
{
    // TODO
    bool tie_check = false;

    //loop through all candidates, if candidates are tied, variable will be true
    for (int i = 0; i < candidate_count; i++)
    {
        if (candidates[i].eliminated == false && candidates[i].votes == min)
        {
            tie_check = true;
        }
        else if (candidates[i].eliminated == false && candidates[i].votes != min)
        {
            tie_check = false;
        }
    }

    if (tie_check == true)
    {
        return true;
    }
    else
    {
        return false;
    }
}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
    // TODO
    for (int i = 0; i < candidate_count; i++)
    {
        if (candidates[i].eliminated == false && min == candidates[i].votes)
        {
            candidates[i].eliminated = true;
        }
    }
    return;
}
Meliodas
  • 63
  • 7
  • Welcome to Stack Overflow. Please read [the help pages](http://stackoverflow.com/help), take the SO [tour], read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). Then please learn how to create a [mre], with emphasis on the *minimal* part. – Some programmer dude Oct 29 '22 at 23:38
  • Have you tried running your code line-by-line in a debugger while monitoring the values of all variables, in order to determine in which line your program stops behaving as intended? If you did not try this, then you may want to read this: [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/q/25385173/12149471) You may also want to read this: [How to debug small programs?](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Andreas Wenzel Oct 29 '22 at 23:42
  • 1
    Note that CS50 has its own debugger, which is called [debug50](https://video.cs50.io/v_luodP_mfE?screen=J0ND72qsI9U&start=1688&end=2012). – Andreas Wenzel Oct 29 '22 at 23:42
  • I just used the debug50 and found when I finish with my tabulate function, it goes to bool won and if(won), it breaks and exits the program. So from what I could tell, it did go through the for loop multiple times then exited the program. I still don't know how the problem is gonna be fixed with this information though. – Meliodas Oct 29 '22 at 23:56
  • To answer some programmer dude, I don't exactly know how to produce an example of an input because the check50 executable thing didn't tell me what they used as an input to produce the error. I'd have to guess and check but I wouldn't know if I'm on the right path. – Meliodas Oct 30 '22 at 00:11
  • That "hiding" is one of the things that make CS50 almost as bad as all these so-called "competition" and "judge" sites in my mind. To "properly" learn programming one need to be able to learn debugging as well. And to be able to debug issues, one needs to know the input. And while I haven't tested the debug50 debugger, being able to debug a program locally in ones own environment is a big plus. – Some programmer dude Oct 30 '22 at 10:12
  • When I used debug50, it only shows me values of i and j in the for loop for tabulate function. It doesn't show me the updates to the votes count for each candidate. – Meliodas Oct 30 '22 at 20:32

1 Answers1

2

I just found out what was wrong with my code. It lied in

            else if (candidates[preferences[i][j]].eliminated == true)
            {
                candidates[preferences[i][j + 1]].votes++;
            }
            break;

This was causing my code to incorrectly update the vote count for the candidates. My thought process for writing this code was if the candidate for that rank was eliminated, then add a vote to the next preference or rank which was the candidates[preferences[i][j+1]].votes++; line. This caused a misalignment in my votes count so here is my solution. It was also unnecessary because I could have just added a break; into the if statement.

I deleted the entire code that was wrong above. Then I simply added a break; into the if statement. The correct tabulate function now looks like this as a completed version.

// Tabulate votes for non-eliminated candidates
void tabulate(void)
{
    //loop over all voters
    for (int i = 0; i < voter_count; i++)
    {
        //loop over all candidates
        //check voter(i's) 1st preference(0th index), if voter(i) 1st preference isn't eliminated, update vote for candidate 1(0th index)
        //break out of the loop so you don't update votes for all preferences for each voter
        for (int j = 0; j < candidate_count; j++)
        {
            if (candidates[preferences[i][j]].eliminated == false)
            {
                candidates[preferences[i][j]].votes++;
                break;
            }
        }
    }
    return;
}
Meliodas
  • 63
  • 7