Есть ли способ выполнить оператор, когда условие в цикле встречается хотя бы один раз? - PullRequest
2 голосов
/ 23 декабря 2019

В настоящее время я занимаюсь играми в свободное время и в настоящее время работаю над игрой в палач. Однако я наткнулся на проблему и думаю, что смог бы решить ее, если бы был способ выполнить оператор, если условие внутри цикла выполнено хотя бы один раз, и если условие не будет выполнено ни разу, это будетсделай другую вещь. Возможно ли это сделать? У кого-нибудь есть какие-либо идеи? Я ценю любые предложения.

Я пытался сделать что-то вроде этого:

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
}

Однако я заметил, что это не работает, потому что цикл будет работать, и если последняя проверенная букване в ответе, bool станет ложным, таким образом напечатав, что письмо не было в ответе и вычтя одно из счета. (Это также моя первая публикация здесь, и я не знаю, должен ли я написать код, поэтому заранее прошу прощения, если я делаю это неправильно) `

Ответы [ 4 ]

1 голос
/ 23 декабря 2019

Вам не нужно снимать флажок 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
}
1 голос
/ 23 декабря 2019

Вы должны подойти к этой проблеме под другим углом:

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

        break;   // <<--- exit the loop, so it's done only once
    }
}
0 голосов
/ 23 декабря 2019

Вам не нужно устанавливать 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
}
0 голосов
/ 23 декабря 2019

Можете ли вы сделать то, что вы спрашиваете;возможно, однако вы заявили, что создаете игру Hangman на C ++, и я думаю, что вы делаете это с неправильным подходом, поэтому выбираете или реализуете неправильные алгоритмы. Вы пытаетесь пройти через две строки с возможной различной длиной от серверной части, что, если это не сделано правильно, может привести к проблемам, будет трудно отследить, особенно если их сравнения определяют условия цикла, операторы выхода или возврата.

Я реализовал свою версию «Палача», но теперь форматирование не самое красивое и словари уровней не генерируются из большого количества случайных слов. Я выражаю это в комментариях к коду, которые обычно читаются из текстового файла и сохраняются в этих структурах. Для простоты я инициализировал их случайными словами прямо в коде.

Посмотрите, что я сделал:


#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, поскольку в игре только несколько состояний и состояний. переходы, чтобы беспокоиться о. Эта реализация также демонстрирует задействованные алгоритмы и показывает, как они связаны друг с другом. Я надеюсь, что это поможет вам лучше понять процесс проектирования.

При создании игры с различными состояниями, имеющей небольшую сложность, вы должны сначала создать свою таблицу состояний и перечислить все ее переходы, прежде чем даже писать какой-либо код. Затем вы должны указать свои стартовые, продолжающиеся, выигрышные, неудачные и выходящие состояния или дела. Затем вам необходимо составить план перехода из одного состояния в другое в соответствии с необходимыми условиями. Это поможет вам в долгосрочной перспективе!

Как только у вас будет правильно выстроено игровое состояние и его переходы, вы можете начать выполнять необходимые функции для этих состояний и начать соединять их вместе. После этого вы должны написать внутреннюю часть функций или их реализацию того, что они будут делать.

И, наконец, после того, как у вас возникнет недостаток, вы захотите провести некоторую отладку и модульное тестирование, и если все будет хорошо, тогда будет безопасно улучшить ваши текущие алгоритмы или выбрать лучшие для пикаили наиболее эффективное исполнение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...