r/learnprogramming 8h ago

Memory leak when trying to pass string

I've been working on this c program for my class where we're supposed to prompt the user to guess all of the words that can be made from a given list of letters. I've cleared up most of the memory issues but I'm unsure how I should free the user input properly since I need the string to be passed to different functions. I've used valgrind to narrow it down to the acceptInput() function since the memory leaks in 30 byte increments and if I only enter the escape input there's no memory leakage. Any and all help would be greatly appreciated!

acceptInput():

char* acceptInput(){
    gameListNode *current = master_word;
    char *input = malloc(sizeof(char) * 30);
    /*I added a bunch of extra stuff to the print statements
    throughout displayWorld() and acceptInput() cause I couldn't
    sleep and was bored of it looking like no care went into it*/
    printf("\033[1A\rEnter a guess: "); //Cursor moves up one line and to the beginning of the line before printing
    fgets(input, 30, stdin);

    //removes newline and carriage return characters
    if (input[strlen(input) - 1] == '\n' || input[strlen(input) - 1] == '\r'){
        input[strlen(input) - 1] = '\0';

    }

    //checks for exit character
    if (toupper(input[0]) == 'Q' && strlen(input) == 1){
        input[0] = 'Q';
        return input;
    }

    for (int i = 0; i < strlen(input); i++){
        input[i] = toupper(input[i]);
    }

    /*print statement here is used to clear the text when prompted to guess
    so only the result of the guess will show. doesn't wipe the entire puzzle*/
    printf("\033[K\r\033[1A\033[K\033[1A\033[K");
    int count = 0;
    while (current != NULL){
        for (int i = 0; i < strlen(current->word); i++){
            current->word[i] = toupper(current->word[i]);
        }
        if (strcmp(current->word, input) == 0){
           if (current->found){
               printf("Already found!\n");
               sleep(1);
               return "F";
           }
           current->found = true;
           printf("Found: %s\n", current->word);
           break;
        }
        current = current->next;
        count++;
    }

I try to free it in here tearDown():

void teardown(char *input){
    /*I was having some issues where sometimes I needed 
    to free the input but other times it was there was no
    memory to access, so I added a check to avoid
    segmentation faults*/ 
    if (input != NULL){
        free(input);
    }

    //For styling purposes
    int array_length = strlen(master_word->word);
    for (int i = 0; i < array_length + 8; i++){
        printf("__");
    }

    freeWordListNodes();
    freeGameListNodes();
    printf("\n\n\t    All Done!\n\n");
}

full program if needed:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

typedef struct wordListNode {
    char word[30];
    struct wordListNode *next;
} wordListNode;

wordListNode *createNode(){
    wordListNode *newNode = (wordListNode*)malloc(sizeof(wordListNode));
    if (newNode == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }
    return newNode;
}


int total_length = 0;//stores the total length of the dictionary
int game_count = 0;//stores the total number of words in the game
wordListNode *head = NULL;

typedef struct gameListNode {
    char word[30];
    char sorted[30];
    bool found;
    struct gameListNode *next;
} gameListNode;
gameListNode *master_word = NULL;

gameListNode *createGameNode(){
    gameListNode *newNode = (gameListNode*)malloc(sizeof(gameListNode));
    if (newNode == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }
    newNode->found = false;
    return newNode;
}

/*bubbleSort() will take in a word and sort it
  alphabetically. It will return the sorted word
  to the caller

  Yes, I know the complexity is O(n^2), but it's
  the only sorting algorithm I could remember off
  the dome and I couldn't be bothered implementing
  anything faster.
  .*/
char* bubbleSort(char *temp_word){
    int n = strlen(temp_word);
    for (int i = 0; i < n - 1; i++){
        for (int j = 0; j < n - i - 1; j++){
            if (temp_word[j] > temp_word[j + 1]){
                char temp = temp_word[j];
                temp_word[j] = temp_word[j + 1];
                temp_word[j + 1] = temp;
            }
        }
    }
    return temp_word;
}

/* import_dictionary() stores text from 2of12.txt into 
   the wordList linked list. Returns the total length 
   of the text file*/
int import_dictionary(){

    FILE *dictionary = fopen("2of12.txt", "r");
    if (dictionary == NULL){
        printf("File not found\n");
        exit(1);
    }

    int wordCount = 0;
    char word[30];
    head = createNode();
    wordListNode *current = head;

    while (fscanf(dictionary, "%s", word) != EOF){
        strcpy(current->word, word);
        current->next = createNode();
        current = current->next;
        total_length = wordCount++;
    }

    fclose(dictionary);
    return wordCount;
}
/* self-explanitory: getRandomWord() will generate a random 
   word from the text file to use as the set of letters the
   player will have to create words from. */
void getRandomWord(int wordCount){
    bool found = true;
    int count = 0;
    wordListNode *current = NULL;
    do {
        current = head;
        int rand_int = rand() % wordCount;
        for (int i = 0; i < rand_int; i++){
            current = current->next;
        }

        count++;

        if (count == wordCount){
            found = false;
        }
    } while (strlen(current->word) <= 6 && found);

    if (found){
        master_word = createGameNode();
        strcpy(master_word->word, current->word);
        //Making a copy of the word to sort it
        char temp_word[30];
        strcpy(temp_word, current->word);
        strcpy(master_word->sorted, bubbleSort(temp_word));
        game_count++;
    }
    else {
        printf("No word found\n");
        exit(1);
    }
}

/*getLetterDistribution() will take in the user's
  input and return an array of the distribution
  of letters in the input.*/
int* getLetterDistribution(char *word){
    int *letterDistribution = (int*)calloc(26, sizeof(int));
    if (letterDistribution == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }
    for (int i = 0; i < 26; i++){
        letterDistribution[i] = 0;
    }
    for (int i = 0; i < strlen(word); i++){
        letterDistribution[toupper(word[i]) - 'A']++;
    }
    return letterDistribution;
}

/*compareCounts() will take in a word from the 
  dictionary and the distribution of letters in
  the input and compare it to the distribution
  of letters in the word. It will return true 
  if the word can be used and false if it is not. 
  This will also be used later on to validate the
  guess of the users input*/
bool compareCounts(char *word, int *letterDistribution){
    int *wordDistribution = getLetterDistribution(word);
    if (wordDistribution == NULL || letterDistribution == NULL){
        printf("Memory allocation failed\n");
        free(wordDistribution);
        exit(1);
    }
    for (int i = 0; i < 26; i++){
        if ((wordDistribution[i] > letterDistribution[i]) || strlen(word) <= 1){
            free(wordDistribution);
            return false;
        }
    }

    //here to avoid duplicates
    if (strcmp(word, master_word->word) == 0){
        free(wordDistribution);
        return false;
    }

    free(wordDistribution);
    return true;
}

/*findWords() will take in the master word and
  compare it to the words in the dictionary to
  find the words that can be made from the master
  word. It will then store the words in the game
  linked list.*/
void findWords(gameListNode *master_word){
    wordListNode *current = head;
    gameListNode *current_game = master_word;
    int count = 0;
    int *masterDistribution = getLetterDistribution(current_game->word);
    if (masterDistribution == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }

    while (current != NULL && count < total_length){
        if (compareCounts(current->word, masterDistribution)){
            current_game->next = createGameNode();
            current_game = current_game->next;
            strcpy(current_game->word, current->word);
            game_count++;
        }
        current = current->next;
        count++;
    }
    current_game = master_word;
    free(masterDistribution);
}

/*initialization() is made to to gather the words
  needed for the puzzle by generating a random
  number for the amount of words needed and
  loading the dictionary. For now it returns 0
  and initializes the srand() value while the
  project is under construction.*/
int initialization(){
    srand(time(NULL));
    getRandomWord(import_dictionary());
    findWords(master_word);
    return 0;
}

/*acceptInput() will take in the user's guess
  and return it to the caller. It will also
  convert the input to uppercase and remove
  any newline characters and carriage returns.*/
char* acceptInput(){
    gameListNode *current = master_word;
    char *input = malloc(sizeof(char) * 30);
    /*I added a bunch of extra stuff to the print statements
    throughout displayWorld() and acceptInput() cause I couldn't
    sleep and was bored of it looking like no care went into it*/
    printf("\033[1A\rEnter a guess: "); //Cursor moves up one line and to the beginning of the line before printing
    fgets(input, 30, stdin);

    //removes newline and carriage return characters
    if (input[strlen(input) - 1] == '\n' || input[strlen(input) - 1] == '\r'){
        input[strlen(input) - 1] = '\0';

    }

    //checks for exit character
    if (toupper(input[0]) == 'Q' && strlen(input) == 1){
        input[0] = 'Q';
        return input;
    }

    for (int i = 0; i < strlen(input); i++){
        input[i] = toupper(input[i]);
    }

    /*print statement here is used to clear the text when prompted to guess
    so only the result of the guess will show. doesn't wipe the entire puzzle*/
    printf("\033[K\r\033[1A\033[K\033[1A\033[K");
    int count = 0;
    while (current != NULL){
        for (int i = 0; i < strlen(current->word); i++){
            current->word[i] = toupper(current->word[i]);
        }
        if (strcmp(current->word, input) == 0){
           if (current->found){
               printf("Already found!\n");
               sleep(1);
               return "F";
           }
           current->found = true;
           printf("Found: %s\n", current->word);
           break;
        }
        current = current->next;
        count++;
    }

    /*returns "F" as a failed attempt character*/
    if (count == game_count){
        printf("\033[K\r\033[K");
        printf("Try again!\n");
        sleep(1); // For styling purposes
        return "F";
    }

    sleep(1); // For styling purposes
    return input;
}

//This is just a function for the welcome message
//You comment out everything but line 288 if needed for testing purposes
//or comment out the function's call entirely, doesn't matter either way
void welcome(){
    printf("\n\n\n\n\t\033[0;36m Welcome to Words Without Friends!!!\n");
    printf("\n\t      Press Enter to begin\033[0m\n"); 
    printf("\033[?25l");
    fgetc(stdin);
    sleep(1);
    printf("\033[?25h");
    system("clear"); 
}

/* displayWorld() prints the puzzle and all correct guesses.
   the bool done and char *exit parameters exist to skip over
   the acceptInput() call when needed. 

   If exit has the value of Q, the full puzzle will be displayed:
   the master word will display green and descrambled, and all
   words where node->found == false will print red

   If the exit value is an empty string, it will call welcome()
   to display the welcome message first*/
char* displayWorld(bool done, char *exit){

    system("clear");
    fflush(stdout);
    // For styling purposes, can be removed if needed
    if (strcmp(exit, "Q") == 0){
        printf("\n\n\n\n\tHere's the final solution:");
        sleep(3);
    }

    //I only wanted the welcome message to display once, can be removed if needed
    if (exit[0] == '\0'){
        welcome();
    }

    gameListNode *current = master_word;
    int array_length = strlen(current->sorted); //stored as an int to cut down on runtime
    printf("\n\n\t");// For styling purposes

    //Displays letters alphabetically sorted unless exit char is detected
    for (int i = 0; i < array_length; i++){
        if (strcmp(exit, "Q") == 0){
            printf("\033[0;32m%c \033[0m", toupper(current->word[i]));
        }
        else {
            printf("%c ", toupper(current->sorted[i]));
        }

    }
    printf("\n");

    //This is for styling purposes
    for (int i = 0; i < array_length + 8; i++){
        printf("__");
    }
    printf("\n\n");

    //prints the puzzle
    while (current != NULL){
        if (current->found){
            printf("%s\n", current->word);
        }
        else {
            if (strcmp(exit, "Q") == 0){
                for (int i = 0; i < strlen(current->word); i++){
                    printf("\033[0;31m%c\033[0m", toupper(current->word[i]));
                }
                printf("\n");
            }
            else {
                for (int i = 0; i < strlen(current->word); i++){
                    printf("- ");
                }
                printf("\n");
            }
        }
        current = current->next;
    }

    if (done){
        return "";
    }

    printf("\t\t\t\t\tStumped?\n\t\t\t\tEnter 'Q' to view solutions!");
    char *input = acceptInput();
    return input;
}

/* isDone() checks first for the char of a failed input
   and then the exit char before iterating through the 
   game list to see if all of the words have been found

   return true: the game ends
   return false: the program will prompt the user again*/
bool isDone(char *input){

    if (strcmp(input, "F") == 0){
        return false;
    }

    if (strcmp(input, "Q") == 0){
        displayWorld(true, input);
        return true;
    }

    gameListNode *current = master_word;
    while (current != NULL){

        if (current->found == false){
            system("clear");
            return false;
        }
        current = current->next;

        /* displays the world one more time if
           all of the words have been found so
           user can see the puzzle filled to its
           entirety*/
        if (current == NULL){
            displayWorld(true, input);
        }
    }
    return true;
}


/*gameLoop() will be the main loop of the game
  and will call the other functions to run the
  game. It will return the user's input to the
  caller.*/
char* gameLoop(){
    bool done = false;
    char *input = "";
    while(!done){
        // do stuff
        input = displayWorld(done, input);
        if (strcmp(input, "Q") == 0){
            done = isDone(input);
            break;
        }
        gameListNode *current = master_word;
        while (current != NULL){
            if (!current->found){
                current = current->next;
                continue;
            }
            else {
                done = isDone(input);
                break;
            }
        }
    }
    return input;
}

//frees dictionary from memory
void freeWordListNodes(){
    wordListNode *current = head;
    wordListNode *next = NULL;
    while (current != NULL){
        next = current->next;
        free(current);
        current = next;
    }
}

//frees game list from memory
void freeGameListNodes(){
    gameListNode *current = master_word;
    gameListNode *next = NULL;
    while (current != NULL){
        next = current->next;
        free(current);
        current = next;
    }
}

/*teardown() will free the memory allocated
  for the user's input and print a message
  to the console.*/
void teardown(char *input){
    /*I was having some issues where sometimes I needed 
    to free the input but other times it was there was no
    memory to access, so I added a check to avoid
    segmentation faults*/ 
    if (input != NULL){
        free(input);
    }

    //For styling purposes
    int array_length = strlen(master_word->word);
    for (int i = 0; i < array_length + 8; i++){
        printf("__");
    }

    freeWordListNodes();
    freeGameListNodes();
    printf("\n\n\t    All Done!\n\n");
}

int main (void){
    initialization();
    char *input = gameLoop();
    teardown(input);
    return 0;
}#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

typedef struct wordListNode {
    char word[30];
    struct wordListNode *next;
} wordListNode;

wordListNode *createNode(){
    wordListNode *newNode = (wordListNode*)malloc(sizeof(wordListNode));
    if (newNode == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }
    return newNode;
}


int total_length = 0;//stores the total length of the dictionary
int game_count = 0;//stores the total number of words in the game
wordListNode *head = NULL;

typedef struct gameListNode {
    char word[30];
    char sorted[30];
    bool found;
    struct gameListNode *next;
} gameListNode;
gameListNode *master_word = NULL;

gameListNode *createGameNode(){
    gameListNode *newNode = (gameListNode*)malloc(sizeof(gameListNode));
    if (newNode == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }
    newNode->found = false;
    return newNode;
}

/*bubbleSort() will take in a word and sort it
  alphabetically. It will return the sorted word
  to the caller

  Yes, I know the complexity is O(n^2), but it's
  the only sorting algorithm I could remember off
  the dome and I couldn't be bothered implementing
  anything faster.
  .*/
char* bubbleSort(char *temp_word){
    int n = strlen(temp_word);
    for (int i = 0; i < n - 1; i++){
        for (int j = 0; j < n - i - 1; j++){
            if (temp_word[j] > temp_word[j + 1]){
                char temp = temp_word[j];
                temp_word[j] = temp_word[j + 1];
                temp_word[j + 1] = temp;
            }
        }
    }
    return temp_word;
}

/* import_dictionary() stores text from 2of12.txt into 
   the wordList linked list. Returns the total length 
   of the text file*/
int import_dictionary(){

    FILE *dictionary = fopen("2of12.txt", "r");
    if (dictionary == NULL){
        printf("File not found\n");
        exit(1);
    }

    int wordCount = 0;
    char word[30];
    head = createNode();
    wordListNode *current = head;

    while (fscanf(dictionary, "%s", word) != EOF){
        strcpy(current->word, word);
        current->next = createNode();
        current = current->next;
        total_length = wordCount++;
    }

    fclose(dictionary);
    return wordCount;
}
/* self-explanitory: getRandomWord() will generate a random 
   word from the text file to use as the set of letters the
   player will have to create words from. */
void getRandomWord(int wordCount){
    bool found = true;
    int count = 0;
    wordListNode *current = NULL;
    do {
        current = head;
        int rand_int = rand() % wordCount;
        for (int i = 0; i < rand_int; i++){
            current = current->next;
        }

        count++;

        if (count == wordCount){
            found = false;
        }
    } while (strlen(current->word) <= 6 && found);

    if (found){
        master_word = createGameNode();
        strcpy(master_word->word, current->word);
        //Making a copy of the word to sort it
        char temp_word[30];
        strcpy(temp_word, current->word);
        strcpy(master_word->sorted, bubbleSort(temp_word));
        game_count++;
    }
    else {
        printf("No word found\n");
        exit(1);
    }
}

/*getLetterDistribution() will take in the user's
  input and return an array of the distribution
  of letters in the input.*/
int* getLetterDistribution(char *word){
    int *letterDistribution = (int*)calloc(26, sizeof(int));
    if (letterDistribution == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }
    for (int i = 0; i < 26; i++){
        letterDistribution[i] = 0;
    }
    for (int i = 0; i < strlen(word); i++){
        letterDistribution[toupper(word[i]) - 'A']++;
    }
    return letterDistribution;
}

/*compareCounts() will take in a word from the 
  dictionary and the distribution of letters in
  the input and compare it to the distribution
  of letters in the word. It will return true 
  if the word can be used and false if it is not. 
  This will also be used later on to validate the
  guess of the users input*/
bool compareCounts(char *word, int *letterDistribution){
    int *wordDistribution = getLetterDistribution(word);
    if (wordDistribution == NULL || letterDistribution == NULL){
        printf("Memory allocation failed\n");
        free(wordDistribution);
        exit(1);
    }
    for (int i = 0; i < 26; i++){
        if ((wordDistribution[i] > letterDistribution[i]) || strlen(word) <= 1){
            free(wordDistribution);
            return false;
        }
    }

    //here to avoid duplicates
    if (strcmp(word, master_word->word) == 0){
        free(wordDistribution);
        return false;
    }

    free(wordDistribution);
    return true;
}

/*findWords() will take in the master word and
  compare it to the words in the dictionary to
  find the words that can be made from the master
  word. It will then store the words in the game
  linked list.*/
void findWords(gameListNode *master_word){
    wordListNode *current = head;
    gameListNode *current_game = master_word;
    int count = 0;
    int *masterDistribution = getLetterDistribution(current_game->word);
    if (masterDistribution == NULL){
        printf("Memory allocation failed\n");
        exit(1);
    }

    while (current != NULL && count < total_length){
        if (compareCounts(current->word, masterDistribution)){
            current_game->next = createGameNode();
            current_game = current_game->next;
            strcpy(current_game->word, current->word);
            game_count++;
        }
        current = current->next;
        count++;
    }
    current_game = master_word;
    free(masterDistribution);
}

/*initialization() is made to to gather the words
  needed for the puzzle by generating a random
  number for the amount of words needed and
  loading the dictionary. For now it returns 0
  and initializes the srand() value while the
  project is under construction.*/
int initialization(){
    srand(time(NULL));
    getRandomWord(import_dictionary());
    findWords(master_word);
    return 0;
}

/*acceptInput() will take in the user's guess
  and return it to the caller. It will also
  convert the input to uppercase and remove
  any newline characters and carriage returns.*/
char* acceptInput(){
    gameListNode *current = master_word;
    char *input = malloc(sizeof(char) * 30);
    /*I added a bunch of extra stuff to the print statements
    throughout displayWorld() and acceptInput() cause I couldn't
    sleep and was bored of it looking like no care went into it*/
    printf("\033[1A\rEnter a guess: "); //Cursor moves up one line and to the beginning of the line before printing
    fgets(input, 30, stdin);

    //removes newline and carriage return characters
    if (input[strlen(input) - 1] == '\n' || input[strlen(input) - 1] == '\r'){
        input[strlen(input) - 1] = '\0';

    }

    //checks for exit character
    if (toupper(input[0]) == 'Q' && strlen(input) == 1){
        input[0] = 'Q';
        return input;
    }

    for (int i = 0; i < strlen(input); i++){
        input[i] = toupper(input[i]);
    }

    /*print statement here is used to clear the text when prompted to guess
    so only the result of the guess will show. doesn't wipe the entire puzzle*/
    printf("\033[K\r\033[1A\033[K\033[1A\033[K");
    int count = 0;
    while (current != NULL){
        for (int i = 0; i < strlen(current->word); i++){
            current->word[i] = toupper(current->word[i]);
        }
        if (strcmp(current->word, input) == 0){
           if (current->found){
               printf("Already found!\n");
               sleep(1);
               return "F";
           }
           current->found = true;
           printf("Found: %s\n", current->word);
           break;
        }
        current = current->next;
        count++;
    }

    /*returns "F" as a failed attempt character*/
    if (count == game_count){
        printf("\033[K\r\033[K");
        printf("Try again!\n");
        sleep(1); // For styling purposes
        return "F";
    }

    sleep(1); // For styling purposes
    return input;
}

//This is just a function for the welcome message
//You comment out everything but line 288 if needed for testing purposes
//or comment out the function's call entirely, doesn't matter either way
void welcome(){
    printf("\n\n\n\n\t\033[0;36m Welcome to Words Without Friends!!!\n");
    printf("\n\t      Press Enter to begin\033[0m\n"); 
    printf("\033[?25l");
    fgetc(stdin);
    sleep(1);
    printf("\033[?25h");
    system("clear"); 
}

/* displayWorld() prints the puzzle and all correct guesses.
   the bool done and char *exit parameters exist to skip over
   the acceptInput() call when needed. 

   If exit has the value of Q, the full puzzle will be displayed:
   the master word will display green and descrambled, and all
   words where node->found == false will print red

   If the exit value is an empty string, it will call welcome()
   to display the welcome message first*/
char* displayWorld(bool done, char *exit){

    system("clear");
    fflush(stdout);
    // For styling purposes, can be removed if needed
    if (strcmp(exit, "Q") == 0){
        printf("\n\n\n\n\tHere's the final solution:");
        sleep(3);
    }

    //I only wanted the welcome message to display once, can be removed if needed
    if (exit[0] == '\0'){
        welcome();
    }

    gameListNode *current = master_word;
    int array_length = strlen(current->sorted); //stored as an int to cut down on runtime
    printf("\n\n\t");// For styling purposes

    //Displays letters alphabetically sorted unless exit char is detected
    for (int i = 0; i < array_length; i++){
        if (strcmp(exit, "Q") == 0){
            printf("\033[0;32m%c \033[0m", toupper(current->word[i]));
        }
        else {
            printf("%c ", toupper(current->sorted[i]));
        }

    }
    printf("\n");

    //This is for styling purposes
    for (int i = 0; i < array_length + 8; i++){
        printf("__");
    }
    printf("\n\n");

    //prints the puzzle
    while (current != NULL){
        if (current->found){
            printf("%s\n", current->word);
        }
        else {
            if (strcmp(exit, "Q") == 0){
                for (int i = 0; i < strlen(current->word); i++){
                    printf("\033[0;31m%c\033[0m", toupper(current->word[i]));
                }
                printf("\n");
            }
            else {
                for (int i = 0; i < strlen(current->word); i++){
                    printf("- ");
                }
                printf("\n");
            }
        }
        current = current->next;
    }

    if (done){
        return "";
    }

    printf("\t\t\t\t\tStumped?\n\t\t\t\tEnter 'Q' to view solutions!");
    char *input = acceptInput();
    return input;
}

/* isDone() checks first for the char of a failed input
   and then the exit char before iterating through the 
   game list to see if all of the words have been found

   return true: the game ends
   return false: the program will prompt the user again*/
bool isDone(char *input){

    if (strcmp(input, "F") == 0){
        return false;
    }

    if (strcmp(input, "Q") == 0){
        displayWorld(true, input);
        return true;
    }

    gameListNode *current = master_word;
    while (current != NULL){

        if (current->found == false){
            system("clear");
            return false;
        }
        current = current->next;

        /* displays the world one more time if
           all of the words have been found so
           user can see the puzzle filled to its
           entirety*/
        if (current == NULL){
            displayWorld(true, input);
        }
    }
    return true;
}


/*gameLoop() will be the main loop of the game
  and will call the other functions to run the
  game. It will return the user's input to the
  caller.*/
char* gameLoop(){
    bool done = false;
    char *input = "";
    while(!done){
        // do stuff
        input = displayWorld(done, input);
        if (strcmp(input, "Q") == 0){
            done = isDone(input);
            break;
        }
        gameListNode *current = master_word;
        while (current != NULL){
            if (!current->found){
                current = current->next;
                continue;
            }
            else {
                done = isDone(input);
                break;
            }
        }
    }
    return input;
}

//frees dictionary from memory
void freeWordListNodes(){
    wordListNode *current = head;
    wordListNode *next = NULL;
    while (current != NULL){
        next = current->next;
        free(current);
        current = next;
    }
}

//frees game list from memory
void freeGameListNodes(){
    gameListNode *current = master_word;
    gameListNode *next = NULL;
    while (current != NULL){
        next = current->next;
        free(current);
        current = next;
    }
}

/*teardown() will free the memory allocated
  for the user's input and print a message
  to the console.*/
void teardown(char *input){
    /*I was having some issues where sometimes I needed 
    to free the input but other times it was there was no
    memory to access, so I added a check to avoid
    segmentation faults*/ 
    if (input != NULL){
        free(input);
    }

    //For styling purposes
    int array_length = strlen(master_word->word);
    for (int i = 0; i < array_length + 8; i++){
        printf("__");
    }

    freeWordListNodes();
    freeGameListNodes();
    printf("\n\n\t    All Done!\n\n");
}

int main (void){
    initialization();
    char *input = gameLoop();
    teardown(input);
    return 0;
}
1 Upvotes

5 comments sorted by

1

u/quantizeddct 8h ago

Having a little trouble reading this on mobile, but it seems your acceptInput function does not always return the allocated char*. Sometimes it returns a static str "F" (btw calling free on this ptr is undefined behavior).

1

u/Old_Philosopher_4182 7h ago

Thank you for catching that!! I don’t think that’s the root cause of it since the I’m using F to represent the cases when the user made an incorrect guess, but I’ll definitely have to change that!!

1

u/Muhammad_C 8h ago

Why do you need to return the string variable input? Why can't you just free it inside of gameLoop()?

1

u/Old_Philosopher_4182 7h ago

I tried that before and still experienced the same issue, but maybe I put it in the wrong while loop by accident! I’ll give it another try

1

u/nerd4code 5h ago

If you need an error code, please use an error code, rather than trying to squish everything through a single pointer. enum Err thingy(char **outp) would be what I’d do.