Hello, here's a simple game I coded up for someone I'm helping. Maybe some of you can find it useful:
// A simple text-based game of hangman
// This code is public domain. You can do what you want with it.
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
class Game {
const std::string& word;
std::string letters_picked;
std::string remaining_letters;
int chances_left;
bool won_;
public:
Game(const std::string& word, int chances):
word(word), chances_left(chances), won_(false) {
for(int i=0; i<word.length(); i++) {
char letter = word;
// Don't put duplicates in
if(remaining_letters.find(letter, 0) == std::string::npos) {
remaining_letters.push_back(letter);
}
}
}
// Print out the word with correct guesses filled in and everything else as blanks
void print_word() {
for(int i=0; i<word.length(); i++) {
char letter = word;
if(letters_picked.find(letter, 0) != std::string::npos) {
std::cout << letter << " ";
} else {
std::cout << "_ ";
}
}
}
// Print each letter the user has picked, separated by spaces
void print_letters_picked() {
for(int i=0; i<letters_picked.length(); i++) {
std::cout << letters_picked << " ";
}
}
// Update the game with the given input
void try_letter(char letter) {
letters_picked.push_back(letter);
// If the letter is correct, remove that letter from the remaining_letters
// and check to see if they won.
int pos = word.find(letter, 0);
if(pos != std::string::npos) {
remaining_letters.erase(0, pos);
if(remaining_letters.length() == 0) {
won_ = true;
}
} else {
chances_left--;
}
}
bool done() {
return chances_left == 0 || won_;
}
bool won() {
return won_;
}
int get_chances() {
return chances_left;
}
};
const char* word_list[] = {
"pickles",
"soup",
"Curtis",
"duck",
"salad",
"onions",
"building",
"tower",
"tomato",
"plate",
"planet",
"universe",
"multiverse",
"hyperbola",
"displacement",
"differential",
"hypertension",
"at",
"the",
"those",
"people",
"eat",
"severed",
"heads"
};
const int word_list_length = sizeof(word_list) / sizeof(word_list[0]);
// Print out the given word with spaces between each letter
void print_word(const std::string& word) {
for(int i=0; i<word.length(); i++) {
std::cout << word << " ";
}
}
// Return whether we should start a new game or not
bool ask_continue() {
while(true) {
std::cout << std::endl << "Continue playing (y/n)? ";
char response;
std::cin >> response;
if(response == 'y' || response == 'Y') {
return true;
} else if(response == 'n' || response == 'N') {
return false;
}
}
}
// Play a single round of a game of hang man
void play(Game& game) {
std::system("cls");
game.print_word();
std::cout << " ";
game.print_letters_picked();
std::cout << std::endl << std::endl << "Body parts left: " << game.get_chances();
std::cout << std::endl << std::endl << "Letter: ";
char letter;
std::cin >> letter;
game.try_letter(letter);
}
int main() {
std::srand( std::time(0) );
bool continue_playing = false;
do {
// Pick a random word to use
std::string word = word_list[ rand() % word_list_length ];
Game game(word, 6);
while(!game.done()) {
play(game);
}
std::system("cls");
print_word(word);
std::cout
<< std::endl << std::endl
<< (game.won() ? "Congratulations! You won." : "Congratulations! You lost.")
<< std::endl;
continue_playing = ask_continue();
} while(continue_playing);
return 0;
}
edit:
STLified version courtesy of Zahlman: (Thanks!)
// A simple text-based game of hangman
// This code is public domain. You can do what you want with it.
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <iterator>
// Print out the given word with spaces between each letter
void print_word(std::ostream& where, const std::string& word) {
std::copy(word.begin(), word.end(),
std::ostream_iterator<char>(where, " "));
}
class Game {
const std::string word;
std::string progress;
std::string picked;
int chances;
bool won() const {
return progress == word;
}
public:
Game(const std::string& word, int chances):
word(word), progress(word.length(), '_'),
picked(), chances(chances) {}
friend std::ostream& operator<<(std::ostream& os, const Game& g) {
if (g.done()) {
print_word(os, g.word);
os << "\n\n"
<< (g.won() ? "Congratulations! You won." :
"Congratulations! You lost.")
<< "\n";
} else {
print_word(os, g.progress);
os << " ";
print_word(os, g.picked);
os << "\n\nBody parts left: " << g.chances << "\n";
}
return os;
}
void guess(char letter) {
bool found = false;
picked.push_back(letter);
// This can be done with standard library algorithms, but
// requires more setup work than it's really worth, and won't
// really generate any good reusable components.
int len = progress.length();
for (int i = 0; i < len; ++i) {
if (word == letter) {
found = true;
progress = letter;
}
}
if (!found) { --chances; }
}
bool done() const {
return chances == 0 || won();
}
};
// TODO: Read words in from a file.
const char* word_list[] = {
"pickles",
"soup",
"Curtis",
"duck",
"salad",
"onions",
"building",
"tower",
"tomato",
"plate",
"planet",
"universe",
"multiverse",
"hyperbola",
"displacement",
"differential",
"hypertension",
"at",
"the",
"those",
"people",
"eat",
"severed",
"heads"
};
const int word_list_length = sizeof(word_list) / sizeof(word_list[0]);
char get_letter() {
std::string response;
std::getline(std::cin, response);
return (response + '\n')[0];
}
// Return whether we should start a new game or not
bool ask_continue() {
while (true) {
std::cout << "Continue playing (y/n)? ";
switch (get_letter()) {
case 'y': case 'Y': return true;
case 'n': case 'N': return false;
}
}
}
int main() {
std::srand(std::time(0));
do {
Game game(word_list[std::rand() % word_list_length], 6);
while (true) {
std::system("cls");
std::cout << game;
if (game.done()) { break; }
std::cout << "\nLetter: ";
game.guess(get_letter());
}
} while (ask_continue());
}
Very important:
This example was meant to show the basic structure of a game. It uses a
very minimal subset of the
STL so as not to obfuscate this purpose. Do not code like this! Here's what I left out for this reason:
- std::set should be used instead of a std::string for letters_picked and remaining_letters in Game.
- All of the routines that use cout should accept a std::ostream as a parameter instead.
- Iterators should always be used instead of indexes for STL containers.
- The for loops to print out elements should be replaced with std::copy and ostream_iterators. Outputting the blanks for unknown letters should be done using std::replace--it might be handy to just do this in try_letter (Normally it makes more sense to just keep track of the incomplete word using an array with "holes" in it, but C++ makes it rather hard to do it the proper way).
(If someone wants to create a "proper" version with the above changes implemented, I'll gladly update this post and place it under the simplified version.)
Some notes on the program design:
- I intentionally used a crippled form of MVC for the Game class. The model and view were fused together for simplicity; I left the controller (play) out because doing so didn't add much complexity.
- That being said, all of the print member functions should not normally be in the main class, unless removing them would add a large amount of complexity and if the flexibility gains aren't needed.
- Each function was designed so that each statement would be at the same level of abstraction. Originally I had play embedded in main, which is an example of what not to do. (The Game constructor currently violates this, which can be remedied by using a std::set) If you want to know more about this, I recommend you buy Code Complete, an excellent book on programming style and code design.
Finally, I'd like to add that, unless you have a very good reason to C++, you should consider learning how to program using a different language. If you're already pretty far along in C++, I still suggest you only write a few games in C++, just to get a hang of the language, then switch to something more powerful (and simpler), such as
python. If you know a decent amount of C++ already, I recommend
Dive Into Python. Otherwise, take a look at
How to Think Like a Computer Scientist. As always, google is your friend :)
Hope this helps,
Curtis
edits:
- Ugh, I hate 8 space tabs.
- Linkified MVC.
[Edited by - cw on June 11, 2007 11:54:04 AM]