/** * @file * @brief C implementation of [Hangman Game](https://en.wikipedia.org/wiki/Hangman_(game)) * @details * Simple, readable version of hangman. * Changed graphic to duck instead of traditional stick figure (same number of guesses). * @author [AtlantaEmrys2002](https://github.com/AtlantaEmrys2002) */ #include /// for main() - tolower() #include /// for main(), new_word(), new_guess(), won() - I/O operations #include /// for all functions - exit(), rand() and file functions #include /// for main() - for string operations strlen, strchr, strcpy #include /// for new_game() - used with srand() for declaring new game instance /* * @brief game_instance structure that holds current state of game */ struct game_instance{ char current_word[30]; ///< word to be guessed by player char hidden[30]; ///< hidden version of word that is displayed to player int size; ///< size of word int incorrect; ///< number of incorrect guesses char guesses[25]; ///< previous guesses int guesses_size; ///< size of guesses array }; // function prototypes struct game_instance new_game(void); // creates a new game int new_guess(char, const char guesses[], int size); // checks if player has already played letter int in_word(char, const char word[], unsigned int size); // checks if letter is in word void picture(int score); // outputs image of duck (instead of hang man) void won(const char word[], int score); // checks if player has won or lost /** * @brief Main Function * @returns 0 on exit */ int main() { struct game_instance game = new_game(); // new game created char guess; // current letter guessed by player // main loop - asks player for guesses while ((strchr(game.hidden, '_') != NULL) && game.incorrect <= 12) { do { printf("\n****************************\n"); printf("Your word: "); for (int i = 0; i < game.size; i++) { printf("%c ", game.hidden[i]); } if (game.guesses_size > 0) { printf("\nSo far, you have guessed: "); for (int i = 0; i < game.guesses_size; i++) { printf("%c ", game.guesses[i]); } } printf("\nYou have %d guesses left.", (12 - game.incorrect)); printf("\nPlease enter a letter: "); scanf(" %c", &guess); guess = tolower(guess); } while (new_guess(guess, game.guesses, game.guesses_size) != -1); game.guesses[game.guesses_size] = guess; // adds new letter to guesses array game.guesses_size++; // updates size of guesses array if (in_word(guess, game.current_word, game.size) == 1) { printf("That letter is in the word!"); for (int i = 0; i < game.size; i++) { if ((game.current_word[i]) == guess) { game.hidden[i] = guess; } } } else { printf("That letter is not in the word.\n"); (game.incorrect)++; } picture(game.incorrect); } won(game.current_word, game.incorrect); return 0; } /** * @brief checks if letter has been guessed before * @param new_guess letter that has been guessed by player * @param guesses array of player's previous guesses * @param size size of guesses[] array * @returns 1 if letter has been guessed before * @returns -1 if letter has not been guessed before */ int new_guess(char new_guess, const char guesses[], int size) { for (int j = 0; j < size; j++) { if (guesses[j] == new_guess) { printf("\nYou have already guessed that letter."); return 1; } } return -1; } /** * @brief checks if letter is in current word * @param letter letter guessed by player * @param word current word * @param size length of word * @returns 1 if letter is in word * @returns -1 if letter is not in word */ int in_word(char letter, const char word[], unsigned int size) { for (int i = 0; i < size; i++) { if ((word[i]) == letter) { return 1; } } return -1; } /** * @brief creates a new game - generates a random word and stores in global variable current_word * @returns current_game - a new game instance containing randomly selected word, its length and hidden version of word */ struct game_instance new_game() { char word[30]; // used throughout function FILE *fptr; fptr = fopen("games/words.txt", "r"); if (fptr == NULL){ fprintf(stderr, "File not found.\n"); exit(EXIT_FAILURE); } // counts number of words in file - assumes each word on new line int line_number = 0; while (fgets(word, 30, fptr) != NULL) { line_number++; } rewind(fptr); // generates random number int random_num; srand(time(NULL)); random_num = rand() % line_number; // selects randomly generated word int s = 0; while (s <= random_num){ fgets(word, 30, fptr); s++; } // formats string correctly if (strchr(word, '\n') != NULL){ word[strlen(word) - 1] = '\0'; } fclose(fptr); // creates new game instance struct game_instance current_game; strcpy(current_game.current_word, word); current_game.size = strlen(word); for (int i = 0; i < (strlen(word)); i++) { current_game.hidden[i] = '_'; } current_game.incorrect = 0; current_game.guesses_size = 0; return current_game; } /** * @brief checks if player has won or lost * @param word the word player has attempted to guess * @param score how many incorrect guesses player has made * @returns void */ void won(const char word[], int score) { if (score > 12) { printf("\nYou lost! The word was: %s.\n", word); } else { printf("\nYou won! You had %d guesses left.\n", (12 - score)); } } /* * @brief gradually draws duck as player gets letters incorrect * @param score how many incorrect guesses player has made * @returns void */ void picture(int score) { switch(score) { case 12: printf("\n _\n" " __( ' )> \n" " \\_ < _ ) "); break; case 11: printf("\n _\n" " __( ' )\n" " \\_ < _ ) "); break; case 10: printf("\n _\n" " __( )\n" " \\_ < _ ) "); break; case 9: printf("\n \n" " __( )\n" " \\_ < _ ) "); break; case 8: printf("\n \n" " __( \n" " \\_ < _ ) "); break; case 7: printf("\n \n" " __ \n" " \\_ < _ ) "); break; case 6: printf("\n \n" " _ \n" " \\_ < _ ) "); break; case 5: printf("\n \n" " _ \n" " _ < _ ) "); break; case 4: printf("\n \n" " \n" " _ < _ ) "); break; case 3: printf("\n \n" " \n" " < _ ) "); break; case 2: printf("\n \n" " \n" " _ ) "); break; case 1: printf("\n \n" " \n" " ) "); break; case 0: break; default: printf("\n _\n" " __( ' )> QUACK!\n" " \\_ < _ ) "); break; } }