你能按你的要求做吗?可能,但是您说您正在用 C++ 制作游戏 Hangman,我认为您正在使用错误的方法来解决这个问题,因此选择或实现了错误的算法。您正在尝试从后端遍历两个长度可能不同的字符串,如果没有正确完成可能会导致问题,往往很难跟踪,特别是如果它们的比较确定循环条件、退出或返回语句。
我已经实现了我的“Hangman”版本,尽管格式不是最漂亮的,关卡字典也不是从大量随机单词中生成的。我在代码的 cmets 中表达了这一点,这些代码通常会从文本文件中读取并保存到这些结构中。为简单起见,我直接在代码中用随机词初始化它们。
看看我做了什么:
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <random>
class Game;
int main() {
using namespace util;
try {
Game game("Hangman");
game.start();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
class Game {
private:
std::string title_;
bool is_running_{ false };
std::string answer_;
std::string guessed_;
std::map<unsigned, std::vector<std::string>> dictionary_; // unsigned represents difficulty level of word
unsigned choosen_difficulty_;
std::string guessed_characters_{"\n"};
public:
Game(const std::string& title) : title_{ title }, choosen_difficulty_{ 0 } {
initialize();
start_over();
}
void start() {
is_running_ = true;
// the player has as many attempts as twice the length of hidden answer's word length.
int number_tries = answer_.size() * 2;
while (is_running_ || number_tries > 0) {
displayGuessedWord();
displayGuessedCharacters();
// ask the player to guess a character;
char guess;
// get a character and make sure it is a valid alphabet character
do {
std::cout << "Guess a character ";
std::cin >> guess;
// Note: I'm using ascii characters in this case
// but for demonstration purposes only!
if ((guess < 'a' && guess > 'z') ||
(guess < 'A' && guess > 'Z')) {
std::cout << "invalid entry ";
}
} while ( (guess < 'a' && guess > 'z') ||
(guess < 'A' && guess > 'Z') );
// test character and update guessed word and number of tries.
test_character(guess);
update_guessed_characters(guess);
number_tries--;
// failed condition
if (number_tries <= 0 && guessed_ != answer_) {
std::cout << "\nGame Over!\n";
is_running_ = try_again(number_tries);
// winning condition
} else if (number_tries > 0 && guessed_ == answer_) {
std::cout << "\nCongratulations!\n";
is_running_ = try_again(number_tries);
}
if (!is_running_) break;
}
}
private:
void displayGuessedWord() {
std::cout << '\n' << guessed_ << '\n';
}
void displayGuessedCharacters() {
std::cout << guessed_characters_ << '\n';
}
void initialize() {
// Normally this would be read in from a file upon game initialization
// but for demonstration purpose, I'll generate a few small vectors of strings
// and associate them to their difficulty level
// levels are based on 3 factors, the length of the word, the repetitive occurance
// of common characters, and the amount of less commonly used characters.
std::vector<std::string> level_1{ "ate", "cat", "dog", "coat", "coal", "does" };
std::vector<std::string> level_2{ "able", "believe", "balloon", "better", "funny", "happy" };
std::vector<std::string> level_3{ "ability", "carpenter", "dogmatic", "hilarious", "generosity", "hostility" };
// ... etc. I'll use just these here for simplicty where each vector has six entries, however,
// with random number generators, this can be done generically for any size
// or number of elements in each of the different vectors.
// create generate the map:
dictionary_[1] = level_1;
dictionary_[2] = level_2;
dictionary_[3] = level_3;
}
std::string getWordFromDictionary(unsigned difficulty, std::map<unsigned, std::vector<std::string>>& dict) {
auto level = dict[difficulty]; // extract the vector based on difficulty level
auto size = level.size(); // get the size of that vector
std::random_device dev; // create a random device
std::mt19937 rng(dev()); // create a pseudo random generator
// create a uniform int distribution type with the range from 0 to size-1
std::uniform_int_distribution<std::mt19937::result_type> dist(0, size - 1);
return level[dist(rng)]; // return a random string from this level.
}
void start_over() {
system("cls"); // Note: I'm running visual studio on Windows!
std::cout << "Welcome to " << title_ << '\n';
// We can use a random generator to pick a word from the given difficulty
// but first we need to get user input for the chosen level.
do {
std::cout << "Choose your difficulty [1-3]\n";
std::cin >> choosen_difficulty_;
if (choosen_difficulty_ < 1 || choosen_difficulty_ > 3) {
std::cout << "Invalid entry:\n";
}
} while (choosen_difficulty_ < 1 || choosen_difficulty_ > 3);
answer_ = getWordFromDictionary(choosen_difficulty_, dictionary_);
// clear and resize guessed word to be that of answer_ and add bunch of hypens.
guessed_.clear();
guessed_.resize(answer_.size(), '-');
// also reset the guessed_characters
guessed_characters_ = std::string("\n");
}
bool try_again(int& tries) {
std::cout << "Would you like to try again?\n";
char c;
std::cin >> c;
if (c == 'y' || c == 'Y') {
start_over();
// don't forget to update this so that the loop can repeat
tries = answer_.size() * 2;
return true;
}
else {
std::cout << "Thank you for playing " << title_ << '\n';
return false;
}
}
void test_character(const char c) {
// here is where you would use the standard library for taking the character
// passed into this function, updating the guessed_characters
// get all indexes
std::vector<unsigned> locations;
for (unsigned i = 0; i < answer_.size(); i++)
if (answer_[i] == c)
locations.push_back(i);
// now update the guessed word
if ( locations.size() > 0 )
for (size_t n = 0; n < locations.size(); n++)
guessed_[locations[n]] = c;
}
void update_guessed_characters(const char c) {
guessed_characters_.insert(0, &c); // just push to the front
}
};
如果你注意到我如何构建上面的游戏类;我将 while 和 do-while 循环与 for 循环和 if 语句以及单个布尔标志结合使用来确定游戏的状态。游戏状态也是由猜字和猜字的更新决定的。然后我将其与答案进行比较。根据某些条件,循环将继续寻找用户的输入或退出。
我不保证此代码 100% 没有错误,因为我没有进行任何严格的测试或检查极端情况或特殊情况,但代码运行没有错误,并且我已经测试了所有主要的游戏状态案例。它似乎工作正常。
我知道如果我选择使用一些标准库函数来处理字符串,可能会有很多改进和简化,但我想说明设计或思考过程中涉及的各个步骤具有状态及其转换的游戏。我也可以将游戏类声明放在它自己的头文件中,并将其实现放在一个 cpp 文件中,但我将它保留为一个单独的类,显示在 main.cpp 中,以便于复制、粘贴和编译。
在这个特定的游戏中,我没有使用 switch 和 case 语句,我只是坚持使用一些 while 和 do-while 循环、一些 for 循环和 if 语句,因为只有几个游戏状态和转换需要担心关于。此实现还演示了所涉及的算法,并展示了它们如何相互连接。我希望这有助于让您更好地了解设计过程。
在制作具有不同状态且有点复杂的游戏时,您应该首先制作状态表,并在编写任何代码之前列出其所有转换。然后你应该列出你的开始、继续、获胜、失败和退出状态或案例。然后,您需要根据他们所需的条件制定如何从一种状态转换到另一种状态。从长远来看,这将对您有所帮助!
一旦您正确布置了游戏状态及其转换,您就可以开始为这些状态创建所需的函数并开始将它们连接在一起。在那之后,您将编写函数的内部或它们将要做什么的实现。
最后,在完成之后,您需要进行一些调试以及单元和案例测试,如果一切看起来都还不错,那么可以安全地改进您当前的算法或选择更好的算法以达到峰值或最高效表现。