问题描述
我最近开始从事我的代码的清洁/物流工作,如果你能给我一些关于如何改进某些事情的技巧,我将不胜感激。
这是一个简单的控制台 Hangman 游戏我做了:
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <time.h>
#include <vector>
using std::cout; using std::cin;
using std::endl; using std::string;
using std::vector; using std::ifstream;
void Clear_Screen(){
cout << string(1000, 'n');
}
vector<string> Add_Lines(unsigned short int usiLength){
vector<string> vecsLines_Vector;
for(unsigned short int i = 0; i < usiLength; i++){
vecsLines_Vector.push_back("_");
}
return vecsLines_Vector;
}
string Output_Guesses(vector<string> vecsVector, unsigned short int usiLimit){
string sOutput;
for(unsigned short int usiI = 0; usiI < usiLimit; usiI++){
sOutput += vecsVector[usiI] + " ";
}
return sOutput;
}
int main(){
while(true){
cout << "t" << " __ __ _______ __ _ _______ __ __ _______ __ _ " << endl
<< "t" << "| | | || _ || | | || || |_| || _ || | | |" << endl
<< "t" << "| |_| || |_| || |_| || ___|| || |_| || |_| |" << endl
<< "t" << "| || || || | __ | || || |" << endl
<< "t" << "| || || _ || || || || || _ |" << endl
<< "t" << "| _ || _ || | | || |_| || ||_|| || _ || | | |" << endl
<< "t" << "|__| |__||__| |__||_| |__||_______||_| |_||__| |__||_| |__|" << endl << endl
<< "tttt" << "---------------" << endl
<< "tttt" << "| 1. New Game |" << endl
<< "tttt" << "| 2. Exit |" << endl
<< "tttt" << "---------------" << endl
<< "tttt" << "Selected : ";
// Prevents from entering an invalid value
unsigned short int usiMenu_Select = 0;
do{
cin >> usiMenu_Select;
}while(usiMenu_Select < 1 || usiMenu_Select > 2);
// Quit Game
if(usiMenu_Select == 2){
return 0;
}
Clear_Screen();
// Grab all the words from the dictionary and store them into a vector
vector<string> vecsWords;
unsigned int uiLength = 0;
ifstream Dictionary;
Dictionary.open("dictionary.txt");
if(Dictionary.is_open()){
while(Dictionary.good()){
string sWord;
getline(Dictionary, sWord);
vecsWords.push_back(sWord);
uiLength++;
}
}
Dictionary.close();
// Pick a random word from the dictionary vector
srand (time(NULL));
const unsigned int kuiRandom_Number = rand() % uiLength;
const string ksWord_Selected = vecsWords[kuiRandom_Number];
// Creates guessing lines
const unsigned short int kusiWord_Length = ksWord_Selected.length();
const unsigned short int kusiTries = 6;
vector<string> vecsGuesses = Add_Lines(kusiWord_Length);
vector<string> vecsWrongs = Add_Lines(kusiTries);
// Guessing game loop
unsigned short int usiIncorrect = 0;
unsigned short int usiCorrect = 0;
while(usiIncorrect < kusiTries){
cout << "ttt" << "Correct : " << Output_Guesses(vecsGuesses, kusiWord_Length) << endl << endl <<
"ttt" << " Wrong : " << Output_Guesses(vecsWrongs, kusiTries) << endl << endl <<
"ttt" << " Guess : ";
// Prevents from entering more than 1 character
string cGuess;
do{
cin >> cGuess;
}while(cGuess.length() > 1);
// Checks if letter guessed is part of the word
bool bValid = false;
for(unsigned short int i = 0; i <= kusiWord_Length; i++){
const string ksValid_Character = ksWord_Selected.substr(i,1);
if(cGuess == ksValid_Character){
vecsGuesses[i] = cGuess;
usiCorrect++;
bValid = true;
}
}
// Checks if answer is valid
if(!bValid){
vecsWrongs[usiIncorrect] = cGuess;
usiIncorrect++;
}
// Output end game message
if(usiCorrect == kusiWord_Length){
cout << "ttt" << " _ _ _ _ " << endl
<< "ttt" << "| | | |_|___ ___ ___ ___ " << endl
<< "ttt" << "| | | | | | | -_| _|" << endl
<< "ttt" << "|_____|_|_|_|_|_|___|_| " << endl
<< "ttt" << "Enter any keys to continue..";
cin >> cGuess;
usiIncorrect = kusiTries;
}else if(usiIncorrect == kusiTries){
cout << "ttt" << " __ " << endl
<< "ttt" << "| | ___ ___ ___ ___ " << endl
<< "ttt" << "| |__| . |_ -| -_| _|" << endl
<< "ttt" << "|_____|___|___|___|_| " << endl
<< "ttt" << "Enter any keys to continue..";
cin >> cGuess;
usiIncorrect = kusiTries;
}
Clear_Screen();
}
}
}
最佳解决思路
-
您可以按字母顺序或按组分组您的 STL
#include
(在不同的答案中为 @Loki Astari 提供信用):// This is Class.cpp
#include "Class.h"
#include "OtherMyClassIdependon1.h"
#include "OtherMyClassIdependon1.h"// Now C++ header files // Lots of people order these alphabetically
#include <string> // Personally I group them
// All the containers together.
// All the streams together.
// etc. Each group alphabetical.// Now C header files // Lots of people order these alphabetically
#include <unistd.h>// Now System header files
#include <ICantThinkOfAnything> -
使用
<ctime>
代替<time.h>
。后者是 C 库,位于namespace std
内。 -
那些
using
在顶部看起来很粘。我只是把std::
放在需要的地方。为了可维护性和 name-clashing 的避免,这也将有助于将 STL 实体与其他实体分开。 -
如果大写字母用于类型,函数应以小写字母开头。
使用 camelCase:
void oneFunction() {}
使用 snake_case:
void another_function() {}
-
Output_Guesses()
不改变容器,所以通过vecsVector
const&
:std::string Output_Guesses(std::vector<std::string> const& vecsVector, /* ... */) {} // ^^^^^^
-
将
std::srand()
移动到main()
的顶部,以实现可维护性,并将其改写为:// use nullptr instead if you're using C++11 std::srand(static_cast<unsigned int>(std::time(NULL)));
没有这个,
std::srand()
每次返回相同的随机数。如果您的编译器发出警告,则只需要转换,因此请确保它们已启用。另外,如果你有 C++ 11,用
nullptr
替换NULL
。 -
当处理 STL 容器 (如
std::vector
和std::string
),use their iterators instead of indices whenever possible 。一次发生在Add_Lines()
。 -
对于您的”winner” 和”loser” 输出,您正在刷新每行的缓冲区,这是
std::endl
正在做的。要做一个换行符,不要这样做,添加n
在引号内的任何地方。您仍然可以将std::endl
放在程序的最后一行或需要刷新的地方。 -
while (true)
没问题另一个选项是for (;;)
,它也直到停止 (通常使用break
) 进行无限循环。 -
我会考虑使用
typedef
为您的 frequently-used 类型,如const unsigned short int
。例如:如果所有字长都使用相同的类型,则将其命名为WordLength
。这是大写的,因为它是一个 user-defined 类型。关于字长,喜欢std::string::size_type
。如果您使用的超级 freakishly-large 字符串超出了unsigned short int
的大小,该怎么办?使用这种类型将保证您可以使用这么长的字,而无需更改所有这些类型。 -
常数可以放在全球范围内:
const unsigned short int kusiTries = 6
这是可以的,因为它不是
const
的全局变量,不能在程序的其他地方修改。此外,请勿使用 strongly-typed 语言 (如 C++) 中的匈牙利符号。只需一个简单的
tries
即可。 -
使用所有的”magic numbers”,您都可以在
main()
中使用EXIT_SUCCESS
和EXIT_FAILURE
。对于”magic numbers” 本身,更喜欢常数 (如上所述) 。 -
为了正确输入
std::string
,最好选择std::getline()
:getline(std::cin, cGuess);
-
对于输入验证,如菜单,我建议 exception-handling(
try
/catch
) 。这将阻止程序从 bugging-up 输入不良,并允许恢复。 -
最后 (也许最重要的是):使用更多的功能。您可能仍然保持程序简单,而不使用类,没关系。但是,当您将所有内容都填入
main()
(或任何一种功能) 时,可以显著降低程序的整体可维护性。只要在main()
中保持”welcome” 的内容,菜单和游戏状态 (继续播放) 。其他一切都应该根据目的分为功能。创建一个只显示简单的消息的函数也不实用,除非它发生需要参数。此外,如果您重复使用相同的代码块 (例如显示数组),请将其放入单独的函数中。
参考文献
注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。