यहां प्रोग्रामिंग के लिए नौसिखिया। आज मैंने c++ में rng का अभ्यास करने के लिए एक रॉक, पेपर, कैंची गेम बनाने की कोशिश की। मैंने पहले ही कई बार कोड की जाँच की है और सुनिश्चित किया है कि कोई त्रुटि नहीं है या कुछ भी ओवरलैप नहीं है, लेकिन कभी-कभी यह अभी भी गलत परिणाम प्रदर्शित करता है। क्या यह मेरे स्विच के उपयोग के कारण है? यहाँ कोड है:

#include <cstdlib>
#include <ctime>
#include <iostream>

using namespace std;

int main() {

    char answer;
    int rng;
    int cont = 0;

    do {
        srand((unsigned)time(0));
        rng = (rand() % 3) + 1;

        switch (rng) {
        case 1:
            cout << "r for rock, p for paper, s for scissors \n";
            cout << "please enter your hand: ";
            cin >> answer;
            switch (answer) {
            case 'r':
                cout << "The computer used rock! \n";
                cout << "It's a draw.\n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            case 'p':
                cout << "The computer used paper! \n";
                cout << "You lose. \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            case 's':
                cout << "The computer used scissors! \n";
                cout << "You win! \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            }break;
        case 2:
            cout << "r for rock, p for paper, s for scissors \n";
            cout << "please enter your hand: ";
            cin >> answer;
            switch (answer) {
            case 'r':
                cout << "the computer used paper! \n";
                cout << "You lose. \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            case 'p':
                cout << "the computer used paper! \n";
                cout << "It's a draw. \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            case 's':
                cout << "the computer used paper! \n";
                cout << "You win! \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            }break;
        case 3:
            cout << "r for rock, p for paper, s for scissors \n";
            cout << "please enter your hand: ";
            cin >> answer;
            switch (answer) {
            case 'r':
                cout << "the computer used scissors! \n";
                cout << "You win! \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            case 'p':
                cout << "the computer used scissors! \n";
                cout << "You lose. \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            case 's':
                cout << "the computer used scissors! \n";
                cout << "It's a draw. \n";
                cout << "enter 1 to continue: ";
                cin >> cont;
                break;
            }break;
        }
    } while (cont == 1);
}
c++
-2
ChinoP 24 सितंबर 2021, 15:56

2 जवाब

सबसे बढ़िया उत्तर

तथ्य यह है कि आपके switch में प्रत्येक मामले में बहुत अधिक दोहराव था, एक लाल झंडा उठाया जाना चाहिए था। DRY (डोंट रिपीट योरसेल्फ) नामक एक सिद्धांत है जो हमें बेहतर कोड लिखने की अनुमति देता है। ऐसा इसलिए है क्योंकि हर दोहराव एक अतिरिक्त जगह है जहां आपको तर्क बदलने की जरूरत है, अगर इसे बदलने की जरूरत है। आखिरकार आप एक को याद करेंगे, और अलग-अलग परिदृश्य अलग-अलग व्यवहार करेंगे। यह आपके कोड के आकार को भी बढ़ाता है और इसे कम पठनीय बनाता है।

तो हम खुद को न दोहराने के बारे में कैसे जाते हैं? हमें दोहराए जाने वाले हिस्से को देखने की जरूरत है, और यह पता लगाने की जरूरत है कि क्या हमें इसे एक लूप में, एक फंक्शन में रखना है, या बस इसे अपने मामले में बाहर निकालना है।

यहां आपका कोड है, जिसमें दोहराव निकाला गया है। सबसे पहले, उपयोगकर्ता इनपुट पर चर्चा करते हैं। आपको इसे हमेशा प्राप्त करने की आवश्यकता होती है, और यह इस बात पर निर्भर नहीं है कि कंप्यूटर ने क्या चुना है। उपयोगकर्ता की पसंद और कंप्यूटर की पसंद परस्पर अनन्य हैं।

एक और हिस्सा जो निकाला गया है वह जारी रखने के लिए कह रहा है। फिर, इसका कंप्यूटर द्वारा किए गए चयन से कोई लेना-देना नहीं है। आपने नौ बार जारी रखने के लिए कहते हुए कोड टाइप किया है। यह सही नहीं लग सकता था।

#include <cstdlib>
#include <ctime>
#include <iostream>

using namespace std;

int main() {
  srand((unsigned)time(0));  // CHANGED: Only seed ONCE
  char answer;
  int rng;
  int cont = 0;

  do {
    // CHANGED: Get user's input up front
    cout << "r for rock, p for paper, s for scissors \nplease enter your hand: ";
    cin >> answer;

    rng = (rand() % 3) + 1;

    switch (rng) {
      case 1:
        switch (answer) {
          case 'r':
            cout << "The computer used rock! \n";
            cout << "It's a draw.\n";
            break;
          case 'p':
            cout << "The computer used paper! \n";
            cout << "You lose. \n";
            break;
          case 's':
            cout << "The computer used scissors! \n";
            cout << "You win! \n";
            break;
        }
        break;
      case 2:
        switch (answer) {
          case 'r':
            cout << "the computer used paper! \n";
            cout << "You lose. \n";
            break;
          case 'p':
            cout << "the computer used paper! \n";
            cout << "It's a draw. \n";
            break;
          case 's':
            cout << "the computer used paper! \n";
            cout << "You win! \n";
            break;
        }
        break;
      case 3:
        switch (answer) {
          case 'r':
            cout << "the computer used scissors! \n";
            cout << "You win! \n";
            break;
          case 'p':
            cout << "the computer used scissors! \n";
            cout << "You lose. \n";
            break;
          case 's':
            cout << "the computer used scissors! \n";
            cout << "It's a draw. \n";
            break;
        }
        break;
    }

    cout << "enter 1 to continue: ";
    cin >> cont;
  } while (cont == 1);
}

हम पहले से ही देख सकते हैं कि स्विच केस बहुत साफ दिखते हैं। आप जिस तर्क त्रुटि का वर्णन कर रहे हैं, वह अभी भी मौजूद है। लेकिन मैं आलस्य को स्वीकार करने जा रहा हूं कि मैं प्रत्येक व्यक्तिगत परिणाम की जांच क्यों नहीं कर रहा हूं। हम नेस्टेड स्विच से बेहतर कर सकते हैं।

आइए परिणामों पर विचार करें: खिलाड़ी जीतता है, हारता है या ड्रॉ करता है। ड्रा की जांच करना सबसे आसान है। क्या खिलाड़ी की पसंद कंप्यूटर से मेल खाती थी? ये रहा आपका [खाली] do लूप।

  do {
    // CHANGED: Get user's input up front
    // TODO: Naive input validation
    cout << "r for rock, p for paper, s for scissors \nyour choice: ";
    cin >> answer;

    rng = (rand() % 3) + 1;

    // 1: rock, 2: paper, 3: scissors
    int playerNum;
    switch (answer) {
      case ('r'):
        playerNum = 1;
        break;
      case ('p'):
        playerNum = 2;
        break;
      case ('s'):
        playerNum = 3;
        break;
      default:
        std::cerr << "Shouldn't be here\n";
    }

    if (playerNum == rng) {
      std::cout << "It's a tie!\n";
    }

    cout << "enter 1 to continue: ";
    cin >> cont;
  } while (cont == 1);

हम देख सकते हैं कि यह अभी भी थोड़ा गड़बड़ है क्योंकि कंप्यूटर एक int चुनता है, और आप इनपुट को char के रूप में लेते हैं। इसके लिए हमारी ओर से कुछ रूपांतरण की आवश्यकता है। हम केवल प्रकारों का मिलान करके रूपांतरण की आवश्यकता को पूरी तरह से समाप्त कर सकते हैं। मुझे उपयोगकर्ता के चरित्र में प्रवेश करने का विचार पसंद है (और मुझे लगता है कि यह कॉपी/पास्ता/सबमिट को रोकने में मदद करता है), इसलिए मैं कंप्यूटर को एक चरित्र चुनने जा रहा हूं। चूंकि मैं छू रहा हूं कि कंप्यूटर कैसे अपना चयन करता है, मैं भी srand()/rand() को खोदने जा रहा हूं और <random> का उपयोग कर रहा हूं, जो यादृच्छिक मान प्राप्त करने का C++ तरीका है।

यहाँ अब तक का नया कोड है:

#include <array>
#include <iostream>
#include <random>

using namespace std;

int main() {
  // CPU setup
  constexpr std::array<char, 3> choices{'r', 'p', 's'};
  std::mt19937 prng(std::random_device{}());
  std::uniform_int_distribution<int> rps(0, 2);

  char answer;
  char cpuChoice;  // CHANGED: rng was a bad name
  int cont = 0;

  do {
    // CHANGED: Get user's input up front
    // TODO: Naive input validation
    cout << "r for rock, p for paper, s for scissors \nyour choice: ";
    cin >> answer;

    cpuChoice = choices[rps(prng)];

    if (answer == cpuChoice) {
      std::cout << "It's a tie.\n";
    }

    cout << "enter 1 to continue: ";
    cin >> cont;
  } while (cont == 1);
}

अब हम सीधे कंप्यूटर के चयन के साथ उपयोगकर्ता इनपुट की तुलना कर सकते हैं, और एक ड्रा का पता लगा सकते हैं। आइए एक जीत का पता लगाने पर गौर करें।

केवल तीन परिदृश्य हैं जिसके परिणामस्वरूप एक खिलाड़ी जीतता है।

player 'r' chooses and cpu chooses 's'
player 'p' chooses and cpu chooses 'r'
player 's' chooses and cpu chooses 'p'

तो अगर हम टाई नहीं करते हैं, तो देखते हैं कि हम जीतते हैं या नहीं:

    if (answer == cpuChoice) {
      std::cout << "It's a tie.\n";
    } else if ((answer == 'r' && cpuChoice == 's') ||
               (answer == 'p' && cpuChoice == 'r') ||
               (answer == 's' && cpuChoice == 'p')) {
      std::cout << "You win!\n";
    }

आइए पीछे हटें और इसकी तुलना अपने नेस्टेड स्विच से करें। ड्रा की जांच करने के लिए कोड के तीन अलग-अलग टुकड़ों को देखना और यह सुनिश्चित करना आवश्यक है कि तीनों सही थे। मैं एक टुकड़े की जांच करता हूं। जीत के साथ ही। जब डिबगिंग की बात आती है, तो इसका पालन करना और जांचना बहुत आसान होता है।

अब घाटे से कैसे निपटा जाए। ठीक है, यदि आप टाई नहीं करते हैं, और आप नहीं जीते हैं, तो आप हार गए। यह जांचने की कोई आवश्यकता नहीं है कि हमने किया था, हम जानते हैं कि यदि हमने इसे इतना आगे बढ़ाया तो हमने किया।

    if (answer == cpuChoice) {
      std::cout << "It's a tie.\n";
    } else if ((answer == 'r' && cpuChoice == 's') ||
               (answer == 'p' && cpuChoice == 'r') ||
               (answer == 's' && cpuChoice == 'p')) {
      std::cout << "You win!\n";
    } else {
      std::cout << "You lose :(\n";
    }

और कोड का यह छोटा सा ब्लॉक खेल के परिणाम को निर्धारित करता है। यदि आप यह घोषित करना चाहते हैं कि कंप्यूटर ने क्या चुना है, तो इसे इस ब्लॉक के पहले और बाहर करें।

और भी बहुत सी छोटी-छोटी चीजें हैं जिन्हें बदला जा सकता है। using namespace std; खराब अभ्यास है। मैं उस लाइन के साथ इंट्रो पढ़ाता था क्योंकि मुझे लगता था कि यह छात्रों को "आसान" कर रहा था। जब अगले सेमेस्टर में लाइन को खत्म करने का समय आया तो कुछ छात्र इसे जाने नहीं दे सकते थे। इसलिए इसे जल्दी खोना सीखिए।

आप उपयोगकर्ता इनपुट को भी मान्य नहीं करते हैं और न ही आप खराब इनपुट के लिए जिम्मेदार हैं। मैं किसी जटिल चीज के बारे में बात नहीं कर रहा हूं, बस यह सुनिश्चित करना है कि एक अच्छा मूल्य दर्ज किया गया है। यह अपने स्वयं के कार्य में सबसे अच्छा होगा।

ये मेरा:

template <typename T, typename Container>
void ask_user_with_selection(const std::string& prompt, T& inputVar,
                             const Container& validOptions) {
  bool inputIsInvalid = true;
  do {
    std::cout << prompt;
    std::cin >> inputVar;
    if (std::find(validOptions.begin(), validOptions.end(), inputVar) ==
        validOptions.end()) {
      std::cout << "Please enter a valid choice.\n";
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
      std::cin.clear();
    } else {
      inputIsInvalid = false;
    }
  } while (inputIsInvalid);
}

जब मैं सी ++ 20 अवधारणाओं को सीखने के लिए तैयार हो जाता हूं, तो यह फ़ंक्शन थोड़ा बदल जाएगा।

1
sweenish 24 सितंबर 2021, 19:47

आपके पास कुछ चीजें हैं जो अजीब हैं।

सबसे पहले, इसका कारण आपको अपने परिणामों की परवाह नहीं है -- केस 1 बनाम केस 2 और 3 के लिए आपके द्वारा किए गए कोड की तुलना करें। आप देखेंगे कि वे अलग हैं। मेरा मानना ​​​​है कि केस 1 वह है जो टूटा हुआ है।

इसके बाद, आप अपना कोड सरल बना सकते हैं। मैं आपको एक मध्यवर्ती चरण दिखाने जा रहा हूँ। यह नहीं है कि मैं अपना अंतिम संस्करण कैसे करूंगा, लेकिन यह इसके प्रति एक विकास है। मेरे महत्वपूर्ण बदलाव आगे और पीछे हैं। मैंने आंतरिक स्विच स्टेटमेंट को भी सरल बनाया।

    // This code is the same for all 3 cases, so pull it out
    // and only do it once.
    cout << "r for rock, p for paper, s for scissors \n";
    cout << "please enter your hand: ";
    cin >> answer;

    switch (rng) {
    case 1:
        cout << "The computer used rock! \n";
        switch (answer) {
        case 'r':
            cout << "It's a draw.\n";
            break;
        case 'p':
            cout << "You lose. \n";
            break;
        case 's':
            cout << "You win! \n";
            break;
        }
        break;
    case 2:
        cout << "the computer used paper! \n";
        switch (answer) {
        case 'r':
            cout << "You lose. \n";
            break;
        case 'p':
            cout << "It's a draw. \n";
            break;
        case 's':
            cout << "You win! \n";
            break;
        }break;
    case 3:
        cout << "the computer used scissors! \n";
        switch (answer) {
        case 'r':
            cout << "You win! \n";
            break;
        case 'p':
            cout << "You lose. \n";
            break;
        case 's':
            cout << "It's a draw. \n";
            break;
        }break;
    }

    // This code is also the same. Pull it out.
    cout << "enter 1 to continue: ";
    cin >> cont;

अब, यह वह नहीं है जो मैं करूँगा और अभी भी आपके मूल ढांचे पर टिका रहूँगा। मैं परिणामों के साथ एक छोटा सा कोड लिखूंगा और फिर आपके नेस्टेड स्विच स्टेटमेंट के एक टन से छुटकारा पाऊंगा, लेकिन यह जितना मैं फिर से लिखना चाहता हूं उससे कहीं अधिक है।

0
Joseph Larson 24 सितंबर 2021, 18:52