Skip to main content
Code Review

Return to Question

Remove TIA- say "thanks" by voting - refer to https://codereview.stackexchange.com/help/someone-answers
Source Link

iI have recently created a Hangman Game in C++ and I would like your feedback on any improvements that can be made to this Hangman game I have written especialy in class organization. Here is the code : https://github.com/ydepledt/Hangman. (for now code function only for french words because iI haven't yet implemented words from english)

Thanks in advance !

i have recently created a Hangman Game in C++ and I would like your feedback on any improvements that can be made to this Hangman game I have written especialy in class organization. Here is the code : https://github.com/ydepledt/Hangman. (for now code function only for french words because i haven't yet implemented words from english)

Thanks in advance !

I have recently created a Hangman Game in C++ and I would like your feedback on any improvements that can be made to this Hangman game I have written especialy in class organization. Here is the code : https://github.com/ydepledt/Hangman. (for now code function only for french words because I haven't yet implemented words from english)

Source Link

C++ Hangman Game, class organization

i have recently created a Hangman Game in C++ and I would like your feedback on any improvements that can be made to this Hangman game I have written especialy in class organization. Here is the code : https://github.com/ydepledt/Hangman. (for now code function only for french words because i haven't yet implemented words from english)

Thanks in advance !

File.hpp

#ifndef FILE_HPP
#define FILE_HPP
#include <iostream>
#include <string>
#include "Settings.hpp"
#include "utils.hpp"
class File {
 public:
 File() = delete;
 File(LANGUAGE language, std::string const & filePath, int nbOfLetters = 5);
 File(Settings settings);
 int nbOfWordsInFile ();
 std::string generateNameFile ();
 std::string generateLanguageFile();
 std::string generateFilePath ();
 std::string getWordFromFile(int position);
 std::string getRandomWord();
 private:
 LANGUAGE m_language;
 int m_nbOfLetters;
 std::string m_filePath;
};
#endif

Game.hpp

#ifndef GAME_HPP
#define GAME_HPP
#include <iostream>
#include <vector>
#include <algorithm>
#include "Word.hpp"
#include "Settings.hpp"
#include "File.hpp"
#include "utils.hpp"
class Game {
 public:
 Game() = delete;
 Game(DIFFICULTY const difficulty, LANGUAGE const language, int const numberOfLetters);
 void initialisation();
 void step();
 bool finished();
 static void askUserSettings(DIFFICULTY & difficulty, LANGUAGE & language, int & numberOfLetters);
 static bool askPlayAgain();
 private:
 int m_count;
 Settings m_settings;
 File m_file;
 Word m_word;
 int m_limit;
};
#endif

Settings.hpp

#ifndef SETTINGS_HPP
#define SETTINGS_HPP
#include <iostream>
#include "utils.hpp"
class Settings {
 public:
 Settings() = delete;
 Settings(DIFFICULTY difficulty, LANGUAGE language = FRENCH, int numberOfLetters = 5);
 DIFFICULTY getDifficulty() {return m_difficulty;};
 LANGUAGE getLanguage() {return m_language;};
 int getNbOfLetters() {return m_numberOfLetters;};
 private:
 DIFFICULTY m_difficulty;
 LANGUAGE m_language;
 int m_numberOfLetters;
};
#endif

utils.hpp

#ifndef UTILS_HPP
#define UTILS_HPP
#include <iostream>
#include <limits>
#include "algorithm"
enum LANGUAGE {FRENCH, ENGLISH};
enum DIFFICULTY {EASY, MEDIUM, HARD, EXTREME};
void stringToLower (std::string & s);
int checkStringBool (std::string s);
int randomNumber (int sup, int inf = 0);
// void createAllFilesTXT (LANGUAGE language, std::string const & bigFilePath);
int transformDifficultyInLimit(DIFFICULTY difficulty);
std::istream& operator>>(std::istream& flow, DIFFICULTY & difficulty);
std::istream& operator>>(std::istream& flow, LANGUAGE & language);
template <typename T, typename Predicate, typename Predicate2>
void secureInput(T & var, Predicate predicate, Predicate2 predicate2)
{
 while (!(std::cin >> var) || !predicate(var) || !predicate2(var))
 {
 if (std::cin.eof())
 {
 throw std::runtime_error("Flow stopped !");
 }
 else if (std::cin.fail())
 {
 std::cout << "Incorrect input" << std::endl;
 std::cin.clear();
 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
 }
 else
 {
 std::cout << "Predicates non respected" << std::endl;
 }
 }
}
template <typename T, typename Predicate>
void secureInput(T & var, Predicate predicate)
{
 while (!(std::cin >> var) || !predicate(var))
 {
 if (std::cin.eof())
 {
 throw std::runtime_error("Flow stopped !");
 }
 else if (std::cin.fail())
 {
 std::cout << "Incorrect input" << std::endl;
 std::cin.clear();
 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
 }
 else
 {
 std::cout << "Predicates non respected" << std::endl;
 }
 }
}
template <typename T>
void secureInput(T & var)
{
 while (!(std::cin >> var))
 {
 if (std::cin.eof())
 {
 throw std::runtime_error("Flow stopped !");
 }
 else if (std::cin.fail())
 {
 std::cout << "Incorrect input" << std::endl;
 std::cin.clear();
 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
 }
 }
}
#endif

Word.hpp

#ifndef WORD_HPP
#define WORD_HPP
#include <iostream>
#include <vector>
#include <algorithm>
class Word {
 public:
 Word();
 Word(std::string const & word);
 int length() {return m_len;};
 bool letterInWord(char letter);
 bool wordFound() {return std::find(std::begin(m_mask), std::end(m_mask), false) == std::end(m_mask);};
 void printLetterFound();
 
 friend std::ostream& operator<<(std::ostream & flow, Word const & word);
 
 private:
 std::string m_word;
 int m_len;
 std::vector<bool> m_mask; 
};
#endif

main.cpp

#include <iostream>
#include <fstream>
#include <ctime>
#include "Game.hpp"
#include "Settings.hpp"
#include "File.hpp"
int main()
{
 srand(time(NULL));
 bool playAgain {false};
 
 do {
 DIFFICULTY difficulty;
 LANGUAGE language;
 int numberOfLetters;
 Game::askUserSettings(difficulty, language, numberOfLetters);
 Game game(difficulty, language, numberOfLetters);
 
 game.initialisation();
 do {
 game.step();
 } while(!game.finished());
 playAgain = Game::askPlayAgain();
 } while(playAgain);
 return 0;
}

Game.cpp

#include <iostream>
#include <cassert>
#include "Game.hpp"
Game::Game(DIFFICULTY const difficulty, LANGUAGE const language, int const numberOfLetters) : m_count(0), 
 m_settings(difficulty, language, numberOfLetters),
 m_file(m_settings),
 m_word(m_file.getRandomWord()),
 m_limit(transformDifficultyInLimit(difficulty)) {}
void Game::askUserSettings(DIFFICULTY & difficulty, LANGUAGE & language, int & numberOfLetters)
{
 std::cout << "----------------------Welcome to the hangman game----------------------" << std::endl;
 std::cout << "Choose your difficulty : ";
 secureInput(difficulty, [](DIFFICULTY diff) -> bool {return diff == EASY || diff == MEDIUM || diff == HARD || diff == EXTREME;});
 std::cout << std::endl;
 std::cout << "Choose your language (for words) : ";
 secureInput(language, [](LANGUAGE lang) -> bool {return lang == FRENCH || lang == ENGLISH;});
 std::cout << std::endl;
 
 std::cout << "Choose word's number of letters : ";
 secureInput(numberOfLetters, [](int nbOfLet) -> bool {return nbOfLet >= 2 && nbOfLet <= 15;});
 std::cout << std::endl;
}
bool Game::askPlayAgain()
{
 std::string answer {""}; //easier to test predicate over string rather than bool
 bool playAgain {false};
 auto predicate = [](std::string answer) -> bool 
 { 
 return (checkStringBool(answer) == 1 || checkStringBool(answer) == 0); 
 };
 std::cout << "Do you want to play again : ";
 secureInput(answer, predicate);
 std::cout << std::endl << std::endl;
 playAgain = (checkStringBool(answer) == 1); //if string is a form of true (like true, yes, y...) then bool is true
 return playAgain;
}
void Game::initialisation()
{
 if (m_limit != -1) std::cout << "You have " << m_limit << " propositions to find the hidden word" << std::endl;
 std::cout << "Good Luck !!!" << std::endl << std::endl;
}
void Game::step()
{
 char letter {};
 std::cout << "The hidden word is : ";
 m_word.printLetterFound();
 std::cout << std::endl;
 std::string proposition = m_limit-m_count < 2 ? "proposition" : "propositions"; //to write singular or plural
 if (m_limit > 0) std::cout << m_limit-m_count << " remaining " << proposition << std::endl;
 std::cout << "Give a letter : ";
 secureInput(letter, isalpha);
 std::cout << std::endl;
 if (!m_word.letterInWord((char) toupper(letter))) ++m_count;
 std::cout << std::endl << std::endl;
}
bool Game::finished()
{
 if (m_count == m_limit)
 {
 std::cout << "You've lost, the word was : " << m_word << std::endl << std::endl;
 return true;
 }
 if (m_word.wordFound())
 {
 std::string proposition = m_count < 2 ? "proposition" : "propositions"; //to write singular or plural
 std::cout << "You've won, with " << m_count << " wrong " << proposition << ", the word was : " << m_word << std::endl << std::endl;
 return true;
 }
 return false;
}

File.cpp

#include <iostream>
#include <cstdlib>
#include <fstream>
#include <cassert>
#include "File.hpp"
File::File(LANGUAGE language, std::string const & filePath, int nbOfLetters) : m_language(language), 
 m_nbOfLetters(nbOfLetters), 
 m_filePath(filePath) 
{
 assert((m_language == FRENCH || m_language == ENGLISH) && "Language not correct");
}
File::File(Settings settings) : m_language(settings.getLanguage()), 
 m_nbOfLetters(settings.getNbOfLetters()),
 m_filePath(this->generateFilePath()) {}
int File::nbOfWordsInFile()
{
 int count {0};
 std::string word {""};
 std::ifstream file {m_filePath};
 if (file) 
 { 
 while(std::getline(file, word)) 
 { 
 ++count;
 }
 file.close();
 }
 else std::cout << "File doesn't exist !" << std::endl;
 return count;
}
std::string File::generateNameFile()
{
 return std::to_string(m_nbOfLetters) + "_letters.txt";
}
std::string File::generateLanguageFile()
{
 switch (m_language)
 {
 case FRENCH:
 return "french/";
 break;
 case ENGLISH:
 return "english/";
 break;
 default:
 return "french/";
 break;
 }
}
std::string File::generateFilePath()
{
 return "../../../words/" + this->generateLanguageFile() + this->generateNameFile();
}
std::string File::getWordFromFile(int position)
{
 assert(m_filePath != "" && "File path can't be \"\"");
 std::string word {""};
 std::ifstream file {m_filePath};
 if (file) 
 { 
 for (int i = 0; i < position; ++i)
 {
 std::getline(file, word);
 }
 file.close();
 }
 else std::cout << "File doesn't exist !" << std::endl;
 return word;
}
std::string File::getRandomWord()
{
 int randNumber {};
 std::string randomWord {};
 randNumber = randomNumber(this->nbOfWordsInFile());
 randomWord = this->getWordFromFile(randNumber);
 return randomWord;
}

Word.cpp

#include <iostream>
#include <cstdlib>
#include <fstream>
#include <cassert>
#include "File.hpp"
File::File(LANGUAGE language, std::string const & filePath, int nbOfLetters) : m_language(language), 
 m_nbOfLetters(nbOfLetters), 
 m_filePath(filePath) 
{
 assert((m_language == FRENCH || m_language == ENGLISH) && "Language not correct");
}
File::File(Settings settings) : m_language(settings.getLanguage()), 
 m_nbOfLetters(settings.getNbOfLetters()),
 m_filePath(this->generateFilePath()) {}
int File::nbOfWordsInFile()
{
 int count {0};
 std::string word {""};
 std::ifstream file {m_filePath};
 if (file) 
 { 
 while(std::getline(file, word)) 
 { 
 ++count;
 }
 file.close();
 }
 else std::cout << "File doesn't exist !" << std::endl;
 return count;
}
std::string File::generateNameFile()
{
 return std::to_string(m_nbOfLetters) + "_letters.txt";
}
std::string File::generateLanguageFile()
{
 switch (m_language)
 {
 case FRENCH:
 return "french/";
 break;
 case ENGLISH:
 return "english/";
 break;
 default:
 return "french/";
 break;
 }
}
std::string File::generateFilePath()
{
 return "../../../words/" + this->generateLanguageFile() + this->generateNameFile();
}
std::string File::getWordFromFile(int position)
{
 assert(m_filePath != "" && "File path can't be \"\"");
 std::string word {""};
 std::ifstream file {m_filePath};
 if (file) 
 { 
 for (int i = 0; i < position; ++i)
 {
 std::getline(file, word);
 }
 file.close();
 }
 else std::cout << "File doesn't exist !" << std::endl;
 return word;
}
std::string File::getRandomWord()
{
 int randNumber {};
 std::string randomWord {};
 randNumber = randomNumber(this->nbOfWordsInFile());
 randomWord = this->getWordFromFile(randNumber);
 return randomWord;
}

utils.cpp

#include <iostream>
#include <cstdlib>
#include <fstream>
#include <string>
#include "utils.hpp"
void stringToLower(std::string & s)
{
 std::transform(std::begin(s), std::end(s), std::begin(s), [](char letter) -> char {return std::tolower(letter);});
}
int checkStringBool (std::string s)
{ 
 stringToLower(s);
 if (s == "true" || s == "yes" || s == "y" || s == "1") return 1; 
 else if (s == "false" || s == "no" || s == "n" || s == "0") return 0;
 else return -1; //incorrect form of string
}
std::istream& operator>>(std::istream& flow, DIFFICULTY & difficulty)
{
 std::string diff;
 flow >> diff;
 stringToLower(diff);
 if (diff == "easy")
 difficulty = EASY;
 else if (diff == "medium")
 difficulty = MEDIUM;
 else if (diff == "hard")
 difficulty = HARD;
 else if (diff == "extreme")
 difficulty = EXTREME;
 return flow;
}
std::istream& operator>>(std::istream& flow, LANGUAGE & language)
{
 std::string lang;
 flow >> lang;
 stringToLower(lang);
 if (lang == "french" || lang == "fr")
 language = FRENCH;
 else if (lang == "english" || lang == "en")
 language = ENGLISH;
 
 return flow;
}
int randomNumber(int sup, int inf)
{
 return rand() % sup + inf;
}
int transformDifficultyInLimit(DIFFICULTY difficulty)
{
 switch (difficulty)
 {
 case EASY:
 return 20;
 break;
 case MEDIUM:
 return 15;
 break;
 case HARD:
 return 10;
 break;
 case EXTREME:
 return 5;
 break;
 default:
 return 15;
 }
}
//Useful to create files with words with different nb of letters from a big file that contains all words
// void createAllFilesTXT(LANGUAGE language, std::string const & bigFilePath)
// {
// std::string word {""};
// std::ifstream bigFile {bigFilePath};
// if (bigFile) 
// { 
// while(std::getline(bigFile, word)) 
// { 
// std::string nameFile = generateFilePath(language, word.size());
// std::ofstream file {nameFile, std::ios::app};
// file << word << '\n';
// }
// bigFile.close();
// }
// else std::cout << "File doesn't exist !" << std::endl;
// }

CmakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(hangman)
set(SOURCES src/main.cpp src/Word.cpp src/Game.cpp src/Settings.cpp src/File.cpp src/utils.cpp)
add_executable(main ${SOURCES})
set_target_properties(main PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
target_include_directories(main PRIVATE include)
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /