Reputation: 31
I am trying to write a simple Hangman game in C++. The game works mostly but sometimes when I debug it I get the exception std::length_error. Usually after the user inputs the choice. I've written the relevant code here due to Stack Overflow's saying that most of my post was mostly code. But I'll provide the rest if it's needed. The main code, variable and function declarations:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <ctime>
#include <stdlib.h>
using namespace std;
void game(int choice);
void check(char letter, string word);
bool check_win(string word);
char guessed[30];
int guessCount = 0, tries = 0;
int main()
{
int choice, sentinel;
cout << "Welcome to Phobez's Hangman game!" << endl;
cout << "=================================" << endl;
cout << "Guess a word which is represented by a row of dashes, representing each letter of the word." << endl;
cout << "Pick a Topic" << endl;
cout << "1. Animals\n2. Colours\n3. Days of the Week\n4. Months\n5. Body Parts\n6. Numbers" << endl;
cout << "Choice: ";
cin >> choice;
game(choice);
system("pause");
return 0;
}
Then the game() function which takes care of setting up and running most of the game:
void game(int choice) {
string word;
const int MAX_TRIES = 6;
char letter;
string animal[] = { "cat", "dog", "horse", "elephant", "duck", "turtle", "bird", "crab", "spider", "bear", "pig", "penguin", "mouse", "rabbit", "lion", "monkey", "bull"};
string colour[] = { "black", "white", "red", "yellow", "purple", "green", "blue" };
string day[] = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };
string month[] = { "january", "february", "may", "march", "april", "june", "july", "august", "september", "october", "november", "december" };
string bodPart[] = { "head", "shoulder", "knee", "forehead", "eyebrow", "ear", "eye", "nose", "chest", "mouth", "arm", "stomach", "hand", "leg", "foot", "toe" };
string nums[] = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "zero" };
srand(time(0));
switch (choice) {
case 1:
word = animal[(rand() % 17) - 1];
break;
case 2:
word = colour[(rand() % 7) - 1];
break;
case 3:
word = day[(rand() % 7) - 1];
break;
case 4:
word = month[(rand() % 12) - 1];
break;
case 5:
word = bodPart[(rand() % 16) - 1];
break;
case 6:
word = nums[(rand() % 10) - 1];
break;
default:
word = animal[(rand() % 18) - 1];
}
do {
cout << "Word: ";
for (int i = 0; i < strlen(word.c_str()); i++) {
bool isPrinted = 0;
for (int j = 0; j < strlen(word.c_str()); j++) {
if (word[i] == guessed[j]) {
cout << word[i];
isPrinted = 1;
break;
}
}
if (isPrinted) {
continue;
}
else {
cout << "_";
}
}
cout << endl;
cout << "Misses: " << tries << endl;
cout << "Guess: ";
cin >> letter;
check(letter, word);
if (check_win(word)) {
cout << word << endl;
cout << "You won!" << endl;
break;
}
else {
continue;
}
} while (tries < MAX_TRIES);
if (tries >= MAX_TRIES) {
cout << "You lost!" << endl;
}
}
I've tried searching Google and Stack Overflow for help on this question but I couldn't find one that could explain to me what's wrong with my code.
The error only pops up sometimes. Most of the time it works. I thought it was an error in my switch case (the random integer generator limits), but I tried changing them up a few times but there was no difference. Can anyone help please? That's the only error that I'm facing. Thanks in advance!
Upvotes: 0
Views: 6360
Reputation: 37513
At if (word[i] == guessed[j])
j
index might get out of bounds because it is capped by j < strlen(word.c_str());
instead of guessed
length. Also it would be worth to change guessed
type into ::std::string
. So loop condition need to be changed to something like
for
(
::std::string::size_type guessed_symbol_index = 0;
guessed.length() != guessed_symbol_index;
++guessed_symbol_index
)
...
if (word[i] == guessed[guessed_symbol_index]) {
All the switch cases are filled with lines that may cause out of bounds access, such as animal[(rand() % 17) - 1];
if rand
returns 17. Also you may accidentally set wrong array size (using magic numbers is bad). You should redefine those arrays as
::std::array<::std::string, 7> day = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" };
and then extract length properly:
day[static_cast<::std::size_t>(rand()) % day.size()];
Upvotes: 0
Reputation: 73376
With the debugger it goes faster. But here the deductive way:
std::length_error
is thrown when there are attempts to exceed implementation defined length limits for some object such as max_size
for string
or vector
. (Note that this is not an out of range issue, but an issue with the size that is required to store the object).
You don't use vector
and the only string you have is word
. The only place where word
could exceed the storage limit is when you make your assignments to it. But the limit is such large that this cannot be triggered by the small words in your arrays.
Consequently, this means that your array access doesn't access the elements you think it accesses. So there must be an issue with the subscripts.
And here we land on rand()
which returns a value between 0 and RAND_MAX
. With the modulo, you ensure that the upper limit is ok. But what if rand()
returns 0 ? You then access the element -1
, which is undefined behavior and the root cause of your unexpected crash.
How to solve it ?
Change your subscript expressions. For example in animal[]
, you have 17 elements, indexed from 0 to 16. So if you change your subscript to rand() % 17
you will get a number between 0 and 16 which is guaranteed to be within the expected bounds.
You could also use vectors instead of arrays, which could avoid the manual counting of elements in it:
<string> animal{ "cat", "dog", "horse", "elephant", "duck", "turtle", "bird", "crab", "spider", "bear", "pig", "penguin", "mouse", "rabbit", "lion", "monkey", "bull"};
...
word = animal[ rand()%animal.size() ]; // asuming that the vector is not empty :-)
P.S: strlen(word.c_str())
can be replaced with word.size()
Upvotes: 3