簡體   English   中英

循環、功能和組織方面的問題

[英]trouble with loops, functions, and organization´

我想先說這不是當前的家庭作業; 但這是我輟學前三年前的作業。 我正在自學,並且正在重新審視一項舊作業。 我不是要整個程序,我只是在尋找幫助構建游戲初始骨架的幫助。

更多信息:玩家 1 將輸入單詞(任何長度/我一直在使用“測試”)供玩家 2 猜測。 玩家 2 將有 5 個字母猜測和 5 個單詞猜測。 如果玩家 2 進入“測試”,它應該能夠忽略上/下之間的大小寫(不使用 toupper / tolower)如果:玩家 2 輸入超過 1 個字母進行猜測:“aa”讓他們再次猜測,直到他們只猜測1個字母“a”。

我面臨的問題是:我不知道把所有東西放在哪里,我覺得我在混淆或弄亂功能,每次我試圖組織它,它只會變得更糟。 我已經重新啟動了幾次,我只是無法將其全部布置好。

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main()
{
    string word, wordguess, lower, dashes;
    string letterguess;
    int i = 0;
    bool GameOver = false, Validletterguess = true, Validwordguess = true;    


    cout << "Player 1, enter a word for Player 2 to guess: " << endl;
    getline(cin, word);
    cout << endl;
    cout << "Player 2, you have 5 letter guesses, and 5 word guesses." << endl;
    cout << "Guess your first letter: " << endl; 

    while (GameOver == false) // Start of Game. Setup for Round 1 letter guess and word guess.
    {           
      
        while (letterguess.length() != 1) // 1 letter only. loop until they enter 1 letter
        {
            cout << endl << "Type a single letter and press <enter>: ";
            cin >> letterguess;   // enter letter guess          

            for (int i = 0; i < letterguess.length(); i++) //ignore case of letter guess
            {
                if (letterguess.at(i) >= 'A' && letterguess.at(i) <= 'Z') 
                {
                    lower += letterguess.at(i) + 32; 

                }
                else
                {
                    lower += letterguess.at(i); 
                }  

            }  
            if (letterguess.at(i) == word.at(i) && Validletterguess == true) //if Player2 guesses a correct letter, replace the dash with letter and display location: ex. If "T" then "You guessed the 1st and 4th letter"
            {
                cout << "You guessed the first letter right!" << endl; // figure out how to display dashes? 
                dashes.at(i) = letterguess.at(i);
                cout << "Enter your first word guess: " << endl;
                cin >> wordguess;
            }
            else
                cout << "Wrong letter! Enter your first word guess: " << endl;
                cin >> wordguess;      

            if (wordguess == word & Validwordguess = true)
            {
                cout << "You guessed the word correctly in 1 try! " << endl;
                Gameover = true;

            }

        }
      

    }   
       
        

        
        
    }

C++ 中有幾件事可以幫助您。 很高興看到您已經在使用std::stringstd::getline來處理用戶輸入。 問題似乎是您在組織游戲邏輯以使其流動以及設置可以幫助您的結構方面陷入困境。

我實際上做了 go 並寫了一個游戲只是為了踢球。 希望我可以提供其中的一些內容並對其進行描述,以便您可以分塊消化,並且您可以看到如何一次構建一個程序。

因此,讓我們首先為實際運行游戲的 function 創建一個存根。 您可以從main調用它。 它通過將游戲與其他設置和關閉內容分開來簡化游戲的實際運行。 這也意味着您可以在以后連續運行多個游戲,而無需修改游戲循環。

enum GameResult {
    None,
    Win,
    Loss,
};

GameResult PlayHangman(const std::string& target, int wrongLetterLimit, int wrongWordLimit)
{
    return None;
}

為了說明這一點,這是我最終為調用這個游戲而寫的完整的main內容。 盡管它是一次性的,但您可以看到它很有用。 在這種情況下,我選擇從命令行讀取游戲設置:

void ExitSyntaxMessage(int code = -1)
{
    std::cerr << "Syntax: hangman <word> [guesses [wordGuesses]]\n";
    exit(code);
}

int main(int argc, char** argv)
{
    // Get game settings
    std::string target;
    int letterGuesses = 5;
    int wordGuesses = 5;

    try {
        if (argc < 2) throw std::runtime_error("Not enough arguments");
        target = argv[1];
        if (argc > 2) letterGuesses = std::stoi(argv[2]);
        if (argc > 3) wordGuesses = std::stoi(argv[3]);
    }
    catch(...)
    {
        ExitSyntaxMessage();
    }

    // Play game
    GameResult result = PlayHangman(target, letterGuesses, wordGuesses);

    // Deliver result and exit
    switch(result)
    {
    case Win:
        std::cout << "Congratulations!\n";
        return 0;
    case Loss:
        std::cout << "Better luck next time!\n";
        return 1;
    default:
        std::cout << "Game stopped.\n";
        return -2;
    }
}

所以,現在有一個簡單的框架供您的游戲運行。代碼不多,但您可以立即開始測試,然后再繼續充實游戲本身。

在這一點上,我應該提到該程序將需要的一些標頭。 有些已經被要求了。 我們將要做的事情需要其他人。

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include <set>

開始游戲... 幫助 function 將字符串轉換為小寫字母總是很方便。 我們一定會利用它。 請注意,這使用了 lambda function。 如果您沒有現代 C++ 編譯器(支持 C++11),則可以使用普通的 function 指針代替。

std::string Lowercase(const std::string& s)
{
    std::string lc(s);
    std::transform(lc.begin(), lc.end(), lc.begin(),
        [](char c)->char{ return std::tolower(c); });
    return lc;
}

現在是時候擴展PlayHangman存根了。 它仍然是一個存根,但我們可以設置一些我們需要的東西,並在繼續之前對它們進行測試。

GameResult PlayHangman(const std::string& target, int wrongLetterLimit, int wrongWordLimit)
{
    GameResult result = None;

    // Create lowercase target and add its characters to set of remaining letters
    std::string lcTarget = Lowercase(target);
    std::set<char> lettersRemaining(lcTarget.begin(), lcTarget.end());
    std::set<std::string> guesses;

    // Set up game parameters
    int letterGuessesRemaining = wrongLetterLimit;
    int wordGuessesRemaining = wrongWordLimit;

    // Sanity-test to ensure game is winnable
    if (wordGuessesRemaining == 0 && letterGuessesRemaining < lettersRemaining.size())
    {
        std::cout << "Game is not winnable...\n";
        return None;
    }

    // Game loop until stream error or game finishes
    bool done = false;
    while (!done)
    {
        done = true;  // The loop is a stub for now
    }

    //// ^^^ for now, just use this bit to test the game setup stuff.
    //// Make sure that your lowercase bits are working and the set of
    //// remaining letters works.  You can add some output code to debug
    //// their values and run tests from the command line to verify.

    return result;
}

這將是單個游戲的主要結構。 所以讓我們談談它。 再次注意我仍然沒有詳細說明。 在這一點上,我已經考慮過我應該如何在邏輯上運行游戲。

現在,我應該說,實際上,大多數人不會像這樣從外到內以線性方式編寫代碼。這更像是一個有機過程,但我確實注意將內容分成邏輯位,然后重新洗牌/像我一樣整理東西 go。 我也盡量不要一次做太多。

通過我介紹的方式,您會看到,我鼓勵您開發一個可靠的平台來編寫您的游戲邏輯。 當您編寫該邏輯時,您應該能夠相信其他一切都已經工作,因為您已經對其進行了測試。

上面發生的一些事情是:

  • 目標字符串被復制到其自身的小寫版本中。 這將用於測試單詞猜測。 還有其他方法可以忽略大小寫來測試字符串,但這只是一種簡單的方法。
  • 因為我們已經構建了該字符串,所以我們還可以使用它來構建一個std::set ,該集合恰好包含該字符串中每個唯一字符中的一個。 這是一個單行,從字符串的迭代器構造集合。 非常整潔!
  • 我們還有一組稱為guesses的字符串——這將跟蹤所有的猜測(包括正確/不正確),這樣您就不會因意外重復您已經猜到的內容而受到懲罰。
  • 有一個健全性檢查,它是循環內最終游戲測試的副本。 老實說,這是我最后添加的內容之一,但我把它放在這里是因為它是游戲前設置的一部分,除了存根循環之外,這是整個“游戲”序列。

檢查點:游戲骨架完成

至此,您可能已經看到足夠的 go 關閉並完成游戲。 上面介紹了一些重要的概念。 特別是,將剩余字母存儲為std::set的想法可能只是讓所有內容都點擊到位的技巧。

從這里開始閱讀將完成該程序。 您是否想這樣做,或者停止閱讀並先自己嘗試一下,這取決於您。


讓我們開始充實一些游戲循環。 首先,您可能想要處理顯示當前游戲 state 並請求輸入。 這發生在兩個步驟中。 第一部分通過隱藏尚未猜到的字符來構建一個字符串,然后將其輸出。 第二部分是一個輸入驗證循環,它丟棄空行,忽略重復猜測並處理流尾。

請注意,輸入被轉換為小寫。 這只是簡化了事情。 特別是在檢查重復猜測時。

    while (!done)
    {
        // Create prompt from original string with a dash for each hidden character
        std::string prompt(target);
        for(char& c : prompt)
        {
            if (lettersRemaining.count(std::tolower(c)) != 0) c = '-';
        }
        std::cout << prompt << "\n";

        // Get input
        std::string input;
        for (bool validInput = false; !validInput && !done; )
        {
            std::cout << "> " << std::flush;
            if (!std::getline(std::cin, input))
            {
                done = true;
            }
            else if (!input.empty())
            {
                input = Lowercase(input);
                validInput = guesses.insert(input).second;
                if (!validInput)
                {
                    std::cout << "You already guessed that!\n";
                }
            }
        }
        if (done)
            continue;

        // TODO: Process guess, update game state, and check end-game conditions
   }

再一次,我們擴展了實現,現在有一些東西要測試。 因此,請確保一切都按照您想要的方式編譯和工作。 顯然游戲現在將永遠運行,但這很好——你可以終止該過程。

當您感到高興時,請繼續執行實際邏輯。 這是我們開始將所有已經設置好的東西放在一起的地方。

由於我們的輸入循環,我們現在知道輸入現在是一個新的猜測,包括 1 個字母或一個單詞。 因此,我首先針對字母猜測或單詞猜測進行分支。 你應該開始在這里看到一個模式,對吧? 再一次,我寫了一段空白的代碼來做某事,然后開始實際填充它......

        // Check the guessed letter or word
        bool correctGuess = false;
        if (input.size() == 1)
        {
            if (letterGuessesRemaining == 0)
            {
                std::cout << "No more letter guesses remain.\n";
            }
            else
            {
                // Test the guessed letter
            }
        }
        else
        {
            if (wordGuessesRemaining == 0)
            {
                std::cout << "No more word guesses remain.\n";
            }
            else
            {
                // Test the guessed word
            }
        }

所以,字母測試......回想一下,我們已經構建了lettersRemaining集並對其進行了測試。 這些是提示中唯一被破折號遮擋的內容。 因此,確定他們是否猜到了一個變得微不足道。 如果它在集合中,他們猜對了,你將它從集合中移除。 否則,他們會燒毀他們的猜測之一。

因為輸入已經是小寫的,我們可以使用字母逐字搜索存儲在集合中的值(也是小寫的)。

                // Test the guessed letter
                char letter = input[0];
                if (lettersRemaining.count(letter) != 0)
                {
                    correctGuess = true;
                    lettersRemaining.erase(letter);
                }
                else
                {
                    std::cout << "Nope!\n";
                    --letterGuessesRemaining;
                }

單詞測試更容易。 回想一下,我們已經存儲了目標單詞的小寫版本,並且輸入也被轉換為小寫。 所以我們只是比較。 您看到所有這些小寫業務實際上如何使生活變得不那么復雜了嗎?

                // Test the guessed word
                if (input == lcTarget)
                {
                    correctGuess = true;
                    lettersRemaining.clear();  //<-- we can use this to test for a win
                }
                else
                {
                    std::cout << "Nope!\n";
                    --wordGuessesRemaining;
                }

我們幾乎完成了。 剩下要做的就是檢查游戲是否應該因輸贏而停止。 這是游戲循環的最后一部分。

因為處理正確單詞猜測的代碼也是禮貌的並且清除了lettersRemaining集,所以我們可以將其用作獲勝條件的測試,無論是否猜測了字母或單詞。

您還將再次看到游戲失敗條件的那種邏輯。 回想一下,在我們檢查是否有可能獲勝的主循環之前。

        // If guessed incorrectly, show remaining attempts
        if (!correctGuess)
        {
            std::cout << "\nAttempts remaining: "
                << letterGuessesRemaining << " letters, "
                << wordGuessesRemaining << " words.\n";
        }            

        // Check if game is complete
        if (lettersRemaining.empty())
        {
            std::cout << target << "\n";
            result = Win;
            done = true;
        }
        else if (wordGuessesRemaining == 0 && letterGuessesRemaining < lettersRemaining.size())
        {
            std::cout << target << "\n";
            result = Loss;
            done = true;
        }

我希望這對您有所幫助,您已經能夠跟進,並且您了解故障和解釋。 這通常是我處理編程的方式。 我喜歡構建我可以依賴的代碼片段,而不是迷失在一些細節中而忽略更基本的事情。

此處使用的某些技術、語言特性或標准庫的某些部分可能是您以前沒有遇到過的。 很好——你可以用它來在線學習、實驗和研究。 在瀏覽器中保留https://cppreference.com書簽。

如果不出意外,我希望這能讓您深入了解將任務分解為您現在關心的小部分,以及您以后可以擔心的其他內容。 以這種方式迭代地構建程序使您能夠定期測試代碼,並增加發現愚蠢錯誤的機會,這些錯誤可能會在以后妨礙您。 看到初學者一鍵編寫一個完整的程序,運行它,然后因為它不“工作”而嚇壞了,這是很常見的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM