0

I'm having trouble with getting Runoff to complete Check50. All checks are passing except the two below for the print_winner function


:( print_winner prints name when someone has a majority

Cause print_winner did not print winner of election

:( print_winner returns true when someone has a majority

Cause print_winner did not print winner and then return true


I can't figure out what the issue is as my manual testing shows the correct candidate as winner. I think there must be some edge case where it's not correctly identifying the winner however I can't think of it

eg:

./runoff Alice Bob Charlie
Number of voters: 5
Rank 1: Alice
Rank 2: Charlie
Rank 3: Bob

Rank 1: Alice
Rank 2: Charlie
Rank 3: Bob

Rank 1: Bob
Rank 2: Charlie
Rank 3: Alice

Rank 1: Bob
Rank 2: Charlie
Rank 3: Alice

Rank 1: Charlie
Rank 2: Alice
Rank 3: Bob

Alice

My code is below, if anyone could point me in the right direction regards to a mistake in my logic or a test case I should try to find the bug(s) I'd really appreciate it. Also - Is there any way to see more information on what check50 is trying to help with debugging? Thanks

#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;

//int min;
int votecount;
string currentcandidate;
double winningtally;
string winrar;


// 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);

//argc determines the number of arguments requested from the user
//argv stands for argument values
int main(int argc, string argv[])
{
    // Check for invalid usage
    //if less than 2 arguments are made on the command line print message and exit
    //argc == arg count
    if (argc < 2)
    {
        printf("Usage: runoff [candidate ...]\n");
        //exit program
        //return 1 in the main function means that the program does not execute successfully and there is some error
        return 1;
    }

    // Populate array of candidates
    // each candidate is an argument word. 1 is subtracted to take out the ./command word
    candidate_count = argc - 1;

    //if too many candidates (arguments) are entered by the user return an error message
    if (candidate_count > MAX_CANDIDATES)
    {
        printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
        //return error code 2
        return 2;
    }
    //create the appropriate number of candidates based on candidate_count (taken from arguments)
    for (int i = 0; i < candidate_count; i++)
    {
        // first argument is name 1. then loop i to pickup other names too
        candidates[i].name = argv[i + 1];
        //set votes default to 0
        candidates[i].votes = 0;
        //set all candidates as not eliminated to start
        candidates[i].eliminated = false;
    }

//ask user for number of voters
    voter_count = get_int("Number of voters: ");
//if more voters set than max number (100 set using #define at the top) then print an error message to user
    if (voter_count > MAX_VOTERS)
    {
        printf("Maximum number of voters is %i\n", MAX_VOTERS);
        //return error code 3 - to differentiate different errors when debugging.
        //any value returned other than 0 means something was wrong,
        //so set different numbers throughout to differentiate which one went wrong
        return 3;
    }

    // Keep querying for votes
    //loop asking for vote until voter count is reached
    for (int i = 0; i < voter_count; i++)
    {

        // Query for each rank
        //loop asking for each voters rank until they have voted for each candidate
        for (int j = 0; j < candidate_count; j++)
        {
            string name = get_string("Rank %i: ", j + 1);

            // Record vote, unless it's invalid

            //if they input a name that doesnt match a candidate then inform the user the vote was wasted/not counted
            //if !vote means if vote returns false. if vote() would mean if vote returns true.
            //seems like this type of if statement still runs the vote process
            if (!vote(i, j, name))
            {
                printf("Invalid vote.\n");
                //return error code 4
                return 4;
            }
        }

        printf("\n");
    }

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

        // Check if election has been won
        //create bool variable - and set it whether print winner returned true or false when ran
        bool won = print_winner();
        //if won = true then 'break' the loop
        if (won)
        {
            break;
        }

        // Eliminate last-place candidates
        //create min integer which holds the value of the minimum number of votes any candidate has in this round
        int min = find_min();
        //create bool variable and set it to whether true or false
        //which is determined by whether the is_tie function
        //says there is a tie or not using the min value of minimum votes
        //(ie - if all remaining candidates have the min number of votes, set to true)
        bool tie = is_tie(min);

        // If tie, everyone wins
        //if tie = true then print all remaining candidate names
        if (tie)
        {
            //loop through all candidates
            for (int i = 0; i < candidate_count; i++)
            {
                //if a candidate.eliminated is false, print their name
                if (!candidates[i].eliminated)
                {
                    printf("Tied winner: %s\n", candidates[i].name);
                }
            }
            //break loop
            break;
        }

        // Eliminate anyone with minimum number of votes
        //run the eliminate function at the end of the round to take out any candidates that have the
        //min number of votes
        eliminate(min);

        // Reset vote counts back to zero
        //loops through eacg candidate to set their votes to 0 for next round of counting
        for (int i = 0; i < candidate_count; i++)
        {
            candidates[i].votes = 0;
        }
        //TBR!!!
        printf("Recount, votes reset to 0\n");
    }
    // return 0 means application executed successfully
    return 0;
}

// Record preference if vote is valid
//vote is either true or false (return true or false to main function where vote function was called)
bool vote(int voter, int rank, string name)
{
    // TODO

    //#### FROM PLURALITY
    int votetrueorfalse = 0;
    for (int i = 0; i < candidate_count; i++)
    {

        currentcandidate = candidates[i].name;
        if (strcmp(name, currentcandidate) == 0)
        {
            preferences[voter][rank] = i;
            votetrueorfalse = 1;
        }

    }
    //END PLURALITY
    if (votetrueorfalse == 0)
    {
        return false;
    }
    else
    {
        return true;
    }
}

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

    //preferences[voter][vote rank]  e.g. voter 2, vote rank 3 is second candidate => preferences[1][2] = 1


    // TODO
    //check first preference, if not eliminated then add vote for that candidate
    //if first preference is eliminated check 2nd preference and loop

    //check first candidate - eliminated true or false.

    //loop through each voter
    for (int i = 0; i < voter_count; i++)
    {
        //create an int to hold voters preference of candidate
        int chosencandidate;
        int k = 0;
        chosencandidate = preferences[i][k];

        while (candidates[chosencandidate].eliminated == true)
        {
            k++;
            chosencandidate = preferences[i][k];
        }
        //set the int to the value of candidate (candidate 1 / candidate 2 / candidate 3)

        //if that candidate hasnt been eliminated, increase that candidates vote count by 1
        candidates[chosencandidate].votes++;
        //TBR
        printf("vote for %s, tally is %i\n", candidates[chosencandidate].name, candidates[chosencandidate].votes);

    }
    return;
}

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

    int winner = 0;

    //check first candidate number of votes. if greater than half the votes - then print name and return true
    //check next candidate number of votes.
    //loop
    //winningtally = (candidate_count / 2);
    //PROBLEM!!
    winningtally = (voter_count / 2.0);
    for (int i = 0; i < candidate_count; i++)
    {
        if (candidates[i].votes > winningtally)
        {
            //printf("Winner is: %s", candidates[i].name);
            //printf("%s\n", candidates[i].name);
            winrar = candidates[i].name;
            winner = 1;
            //return true;
            //break;
        }
        else
        {
            i++;
        }

    }

    if (winner == 0)
    {
        return false;
    }
    else
    {
        printf("%s\n", winrar);
        return true;
    }

}

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

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

    }

    int candidatesleft = 0;
    for (int i = 0; i < candidate_count; i++)
    {
        if (candidates[i].eliminated == false)
        {
            candidatesleft++;
        }

    }


    //if (candidatesleft == candidate_count)
    if (candidatesleft == tiecounter)
    {
        return true;
    }
    else
    {
        return false;
    }
    // TODO

}

// Eliminate the candidate (or candidates) in last place
void eliminate(int min)
{
    // TODO
    for (int i = 0; i < candidate_count; i++)
    {
        if (candidates[i].votes == min)
        {
            candidates[i].eliminated = true;
            printf("Candidate %s has been eliminated\n", candidates[i].name);
        }
    }
    return;
}

1 Answer 1

1

If the second, or any odd-indexed candidate, is the winner print_winner will not detect that. Why?

The i for loop in vote only processes the even indexes because i is incremented twice: once in the loop itself and once in the else clause. Incrementing the for variable within the loop is very often a bug waiting to happen.

If you run the Usage Election but switch Bob and Alice for each voter (therefore Bob should win), print_winner will not produce a result and will return false.

I don't know of a way to get more insight into check50's tests for this particular problem.

1
  • Eureka!! Thanks so much. I've been trying to use FOR loops more as I had a bad habit of lazily using WHILE loops all the time - and my bad habits seem to have bitten me here haha. Such a simple oversight, you've really helped. Thank you Commented Apr 11, 2021 at 9:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .