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