【问题标题】:Is there a way to run a statement when a condition in a loop is met at least once?当循环中的条件至少满足一次时,有没有办法运行语句?
【发布时间】:2019-12-23 01:23:19
【问题描述】:

我目前在空闲时间做游戏,目前正在开发一个刽子手游戏。但是,我偶然发现了一个问题,我认为如果有一种方法可以运行语句,如果循环内的条件至少满足一次,并且如果条件没有满足一次,它会做另一件事。有可能吗?有没有人有任何想法? 我很欣赏任何建议。

我试着做这样的事情:

for (){
if (string [i] == letter that the player inputed){
// replace underscores with the letter guessed
// and also turn a bool statement true
}
else {
 // turn the bool statement false
  }
}
if (!bool variable){
 // print that the letter guessed was not in the answer
 // and substract one from the number of guesses available
}

但是我注意到它不起作用,因为循环将运行并且如果它检查的最后一个字母不在答案中,则布尔值将变为假,因此打印该字母不在答案中并减去一个从比分。 (这也是我第一次在这里发帖,我不知道我是否应该这样写代码,所以如果我做得不对,请事先道歉) `

【问题讨论】:

  • 是的,这是可能的。什么语言?当前的代码是什么样的?
  • 我用来创建游戏的语言是c++
  • 我用我尝试做的伪代码更新了帖子,但我注意到它不起作用,因为循环将运行并且如果它检查的最后一个字母不在答案中,布尔值将变为假,从而打印出该字母不在答案中并从分数中减去一个。 (这也是我第一次在这里发帖,我不知道我是否应该这样写代码,所以如果我做得不对,我先道歉)
  • 如果我从您写的内容中理解正确,您可以在 for 循环范围之外声明一个 bool 变量并将初始值设置为 false,然后在 if 条件下将其翻转为 true。这对你有用吗?
  • 是的,但问题是如果字符串的最后一个字符与输入的字母不同,即使前面的任何一个为真,布尔值也会变为假。然后,当字母不在答案中时运行语句。

标签: c++ loops


【解决方案1】:

你应该从不同的角度来解决这个问题:

for( ... ) {
    if( your condition is met ) {
        do_whatever_you_have_to();

        break;   // <<--- exit the loop, so it's done only once
    }
}

【讨论】:

  • 但是如果答案中有多个该字母的实例怎么办?它只会替换其中一个下划线并最终确定语句。
  • @Influence_r 你曾经问过如何做某事。现在你在问每次满足条件时如何做某事——如果这是你想要的,创建一个布尔标志并保持更新,然后在循环完成后基于它做一些事情(正如你在问题)是一种有效的方法。
  • @Influence_r 查看我的答案,您必须跟踪比较字符的该字符串的所有索引位置并将它们保存到向量或数组中......我在我的回答但没有提及;它被留在那里作为一个隐藏的挑战让你拿起,但我的回答确实有助于解决这个问题!
【解决方案2】:

如果比较失败,您不必关闭标志 guessed

string s;
bool guessed = false;
char inputted_letter; // comes from somewhere
for (size_t i = 0; i < s.size(); ++i) {
  if (s[i] == inputted_letter) {
    // replace underscores with the letter guessed
    guessed = true;
  }
}

if (!guessed) {
  // print that the letter guessed was not in the answer
  // and substract one from the number of guesses available
}

【讨论】:

  • 是的,但问题是如果字符串的最后一个字符与输入的字母不同,即使前面的任何一个为真,布尔值也会变为假。然后,当字母不在答案中时运行语句。
  • 在我提供的示例中,只有一个可能的标志更改 - 在if 条件为正的情况下,这正是您所期望的。
【解决方案3】:

您不必在循环中设置false

bool has_found = false;

for (auto& c : word_to_guess)
{
    if (input_letter == c) {
        // replace _ by current letter...
        has_found = true;
    }
}
if (!has_found){
    // print that the letter guessed was not in the answer
    // and substract one from the number of guesses available
}

但我建议您的循环一次只做一件事:

bool contains(const std::string& word_to_guess, char input_letter)
{
    return std::any_of(word_to_guess.begin(),
                       word_to_guess.end(),
                       [&](char c){ return input_letter == c; })
    /*
    for (auto& c : word_to_guess)
    {
        if (input_letter == c) {
            return true;
        }
    }
    return false;
    */
}


if (contains(word_to_guess, input_letter)
{
    // show current letter...
    for (std::size_t i = 0; i != hangman_word.size(); ++i) {
        if (word_to_guess[i] == input_letter) {
            hangman_word[i] = word_to_guess[i];
        }
    }
} else {
    // print that the letter guessed was not in the answer
    // and substract one from the number of guesses available
}

【讨论】:

    【解决方案4】:

    你能按你的要求做吗?可能,但是您说您正在用 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 语句,因为只有几个游戏状态和转换需要担心关于。此实现还演示了所涉及的算法,并展示了它们如何相互连接。我希望这有助于让您更好地了解设计过程。

    在制作具有不同状态且有点复杂的游戏时,您应该首先制作状态表,并在编写任何代码之前列出其所有转换。然后你应该列出你的开始、继续、获胜、失败和退出状态或案例。然后,您需要根据他们所需的条件制定如何从一种状态转换到另一种状态。从长远来看,这将对您有所帮助!

    一旦您正确布置了游戏状态及其转换,您就可以开始为这些状态创建所需的函数并开始将它们连接在一起。在那之后,您将编写函数的内部或它们将要做什么的实现。

    最后,在完成之后,您需要进行一些调试以及单元和案例测试,如果一切看起来都还不错,那么可以安全地改进您当前的算法或选择更好的算法以达到峰值或最高效表现。

    【讨论】:

      猜你喜欢
      • 2018-11-04
      • 2012-05-23
      • 2019-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-04
      • 1970-01-01
      相关资源
      最近更新 更多