c++ - 石头剪刀布有时会给出错误的结果
问题描述
新手在这里编程。今天尝试制作一个石头剪刀布游戏来练习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);
}
解决方案
你的每个案例都有很多重复的事实switch
应该引发一个危险信号。有一个叫做 DRY(Don't Repeat Yourself)的原则可以让我们写出更好的代码。这是因为每次重复都是一个额外的地方,如果需要更改,您必须更改逻辑。最终你会错过一个,不同的场景会有不同的表现。它还会增加代码的大小并降低其可读性。
那么我们如何不重复自己呢?我们需要查看重复的部分,并确定是否需要将其放入循环、函数中,或者在您的情况下将其拉出。
这是您的代码,去掉了重复部分。首先,让我们讨论一下用户输入。你总是需要得到它,它不依赖于计算机选择了什么。用户的选择和计算机的选择是相互排斥的。
被拉出的另一部分是要求继续。同样,它与计算机所做的选择无关。您输入了要求继续九次的代码。那感觉不对。
#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;
是不好的做法。我曾经用那条线来教 Intro,因为我认为它可以“缓和”学生的学习。当到了下学期放弃这条线的时候,一些学生无法放手。所以要学会早点失去它。
您也不会验证用户输入,也不会考虑错误输入。我不是在谈论任何复杂的事情,只是确保输入了一个好的值。最好在它自己的功能中完成。
这是我的:
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);
}
当我开始学习 C++20 概念时,这个函数会有所改变。
推荐阅读
- python - 使用python中的请求模块POST / PUT方法将大文件拆分为小文件并将其发送到github的最佳方法是什么?
- python - 在熊猫中将一列日期时间和字符串转换为句点
- java - 在列表中排列数据
- php - php curl:Rest API Post XML
- swift - 为什么响应总是 {"detail":"Unsupported media type \"text/plain\" in request."} 在 swift 中?
- android - 在 Android Studio 中启动新项目
- php - get_bloginfo() 不在页脚上回显 - Wordpress
- android - 不遵守 Google Play 开发者计划政策
- javascript - 添加了关键道具 || 警告:列表中的每个孩子都应该有一个唯一的“关键”道具
- rabbitmq - 1 个队列上的 RabbitMQ 2 侦听器做一项工作(第一个和第二个侦听器做它)