C++로 지뢰찾기를 만들었는데, 하다가 계속 프로그램이 중지되요 ㅠㅠ
아래는 지뢰찾기에 해당하는 소스코드인데요.
하다가 계속 프로그램이 중단됩니다. ㅠㅠ
예를 들어 seed에 1을 넣고 Choice에 0을 넣으면 프로그램이 중단됩니다 ㅠㅠ
뭐가 문제인가요?
#include
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::setw;
enum say_Yes_or_No { Yes, No };
enum result_of_Games { Success, Failure, Quit };
void initialize_Grid(char Grid[10][10]);
void get_seedValue(int&);
void dispose_the_Mines(int Cell[10][10]);
say_Yes_or_No Do_this_Number_exist(int randomNumber, int Cell[10][10]);
void locate_Mines(int Cell[10][10]);
void How_many_mines_are_in_Neighborhood(int Cell[10][10]);
void initialize_realCell(char realCell[10][10], int tempCell[10][10]);
void fill_in_the_Grid(char Cell[10][10], char Grid[10][10], int Choice);
void Game_set(char realCell[10][10], int tempCell[10][10], char Grid[10][10]);
result_of_Games Find_out_All_Mines(char realCell[10][10], char Grid[10][10], int Choice);
void How_many_minefree_cells_is_it(char Grid[10][10], int& counting);
say_Yes_or_No Does_the_Grid_Have_Mine(char Grid[10][10]);
int main()
{
//Declare the Cells, Grids and Seed.
int tempCell[10][10] = {0};
char realCell[10][10] = {'\0'};
char Grid[10][10] = {'\0'};
int seed;
get_seedValue(seed);
srand(seed);
Game_set(realCell, tempCell, Grid);
int Choice=0;
Find_out_All_Mines(realCell, Grid, Choice);
return 0;
}
result_of_Games Find_out_All_Mines(char realCell[10][10], char Grid[10][10], int Choice)
{
int how_much=0;
do {
int counting = 0;
How_many_minefree_cells_is_it(Grid, counting);
counting -= 20;
cout << endl
<< "0 1 2 3 4 5 6 7 8 9" << endl;
cout << "--------------------" << endl;
for (int i = 0; i<10; i++)
for (int j = 0; j < 10; j++)
{
cout << Grid[i][j] << setw(2);
if (j == 9)
cout << "|" << setw(2) << i << endl;
}
cout << endl
<< "Choose one of " << counting << " mine-free cells: ";
cin >> Choice;
if (Choice >= 0 && Choice <= 99)
fill_in_the_Grid(realCell, Grid, Choice);
else
{
if (how_much > 0 && Choice == 'Q')
return Quit;
while ((Choice < 0 || Choice > 99)||(Grid[Choice/10][Choice%10] != '#'))
{
if (cin.fail())
{
cin.clear();
cin.ignore(2147483647, '\n');
}
cout << "Choose again(Enter Q if you want to quit.): ";
cin >> Choice;
}
}
how_much++;
fill_in_the_Grid(realCell, Grid, Choice);
if (counting == 0)
{
cout << endl
<< "0 1 2 3 4 5 6 7 8 9" << endl;
cout << "--------------------" << endl;
for (int i = 0; i<10; i++)
for (int j = 0; j < 10; j++)
{
cout << Grid[i][j] << setw(2);
if (j == 9)
cout << "|" << setw(2) << i << endl;
}
cout << endl;
cout << "Congratulations! You cleared the game.";
return Success;
}
if (Does_the_Grid_Have_Mine(Grid) == Yes)
{
cout << endl
<< "0 1 2 3 4 5 6 7 8 9" << endl;
cout << "--------------------" << endl;
for (int i = 0; i<10; i++)
for (int j = 0; j < 10; j++)
{
cout << Grid[i][j] << setw(2);
if (j == 9)
cout << "|" << setw(2) << i << endl;
}
cout << endl;
cout << "BOOM! Game Over." << endl;
return Failure;
}
} while (1);
}
void How_many_minefree_cells_is_it(char Grid[10][10], int& counting)
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
if (Grid[i][j] == '#')
counting++;
}
say_Yes_or_No Does_the_Grid_Have_Mine(char Grid[10][10])
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
if (Grid[i][j] == 'B')
return Yes;
return No;
}
//****************Fill all Grid with "#"****************//
void initialize_Grid(char Grid[10][10])
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
Grid[i][j] = '#';
}
//******************************************************//
void get_seedValue(int& seed)
{
do {
if (cin.fail())
{
cin.clear();
cin.ignore(214748364, '\n');
}
cout << "Please specify the game seed number: ";
cin >> seed;
} while (seed < 0);
}
//****************It's all about initializing Cell.****************//
void dispose_the_Mines(int Cell[10][10])
{
int randomNumber;
int i = 0;
while (i < 20)
{
randomNumber = rand() % 100;
if (Do_this_Number_exist(randomNumber, Cell) == Yes)
continue;
else if (Do_this_Number_exist(randomNumber, Cell) == No)
{
Cell[randomNumber / 10][randomNumber % 10] = randomNumber;
i++;
}
}
}
say_Yes_or_No Do_this_Number_exist(int randomNumber, int Cell[10][10])
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
if (Cell[i][j] == randomNumber)
return Yes;
return No;
}
void locate_Mines(int Cell[10][10])
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (Cell[i][j] == 0)
;
else
Cell[i][j] = 66;
}
}
}
void How_many_mines_are_in_Neighborhood(int Cell[10][10])
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (Cell[i][j] == 66)
;
else
{
if (Cell[i - 1][j - 1] == 66)
Cell[i][j]++;
if (Cell[i - 1][j] == 66)
Cell[i][j]++;
if (Cell[i - 1][j + 1] == 66)
Cell[i][j]++;
if (Cell[i][j - 1] == 66)
Cell[i][j]++;
if (Cell[i][j + 1] == 66)
Cell[i][j]++;
if (Cell[i + 1][j - 1] == 66)
Cell[i][j]++;
if (Cell[i + 1][j] == 66)
Cell[i][j]++;
if (Cell[i + 1][j + 1] == 66)
Cell[i][j]++;
}
}
}
}
void initialize_realCell(char realCell[10][10], int tempCell[10][10])
{
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
if (tempCell[i][j] == 0)
realCell[i][j] = '.';
else if (tempCell[i][j] == 66)
realCell[i][j] = 'B';
else
realCell[i][j] = (tempCell[i][j] + 48);
}
}
//*****************************************************************//
void fill_in_the_Grid(char Cell[10][10], char Grid[10][10], int Choice)
{
if (Choice >= 0 && Choice <= 99)
switch (Cell[Choice / 10][Choice % 10])
{
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case 'B':
Grid[Choice / 10][Choice % 10] = Cell[Choice / 10][Choice % 10]; break;
case '.':
Grid[Choice / 10][Choice % 10] = Cell[Choice / 10][Choice % 10];
fill_in_the_Grid(Cell, Grid, Choice - 10);
fill_in_the_Grid(Cell, Grid, Choice - 1);
fill_in_the_Grid(Cell, Grid, Choice + 1);
fill_in_the_Grid(Cell, Grid, Choice + 10);
break;
default:
break;
}
else
;
}
void Game_set(char realCell[10][10], int tempCell[10][10], char Grid[10][10])
{
dispose_the_Mines(tempCell);
locate_Mines(tempCell);
How_many_mines_are_in_Neighborhood(tempCell);
initialize_realCell(realCell, tempCell);
initialize_Grid(Grid);
}
답변하기에 앞서, 0. 300여 줄 코드를
답변하기에 앞서,
0. 300여 줄 코드를 디버그해달라고 부탁하는 글 치고 질문글(https://archive.is/vfy2m)이 너무 성의없다는 생각 안 드십니까.
include 헤더는 다 날라갔고, 소스 코드 indentation도 하나도 안 되어 있고.
kldp 초심자라서 <code> 사용법은 모르실 수 있다고 해도 http://codepad.org/, http://ideone.com/ 등 다른 방법도 있는데, 질문 올리고 나서 한 번 다시 읽어보시지도 않으신 것 같네요.
남의 코드 읽고 리뷰해드리는건 꽤 귀찮은 일입니다. 깔끔하게 복붙하거나 다운받아서 바로 컴파일할 수 있게 올려놓아도 해 줄 사람 한 명이 있을까 말까에요.
1. 원래 대등한 프로그래머끼리는 아무래도 좋은 코딩 스타일 같은 건 웬만하면 서로 터치 안하는데 말이죠
지금은 제가 조언 드리는 입장이니까 실례하겠습니다.
더 많은 사람들에게 익숙한
bool
대신enum say_Yes_or_No;
같은 걸 쓰시는 이유는 무엇인가요.Yes
나No
를 조건문에 직접 명시해서 혼동을 줄여보려는 의도라던가 등 무슨 이유가 있으셨겠지만 별로 좋은 방식은 아니라고 생각됩니다.특히
enum
형 상수는 정수형 상수로 암시적 변환되기 때문에,Yes
가 0으로 암시적 변환되어서 비직관적이게도false
로 해석될 수 있다는 점을 보면 더욱 그렇죠.정 그런 용도로 쓰고 싶으시면 아예 별도의
class
로 정의하거나, C++11의enum class
를 알아보세요.혼자 만들어서 혼자 읽을 프로그램 코드라면 아무래도 상관없는데 이렇게 독창적인 스타일을 만들어서 남들더러 읽으라고 하면 의욕이 확 깎입니다.
2. 질문글이 너무 성의없어서 솔직히 그냥 훑어보기만 하려고 했는데 벌써 문제 있는 부분이 세 군데는 보이네요.
못 찾았다면 모르되 찾았으면 알려드리는 것이 바람직하겠죠.
1)
How_many_mines_are_in_Neighborhood
함수의 루프 안에 있는 배열 참조는 배열 인덱스 범위를 벗어납니다.예컨대
Cell[i - 1][j - 1]
는i=0, j=0
일 때 어디를 가리키게 될까요?물론 대개의 아키텍처&컴파일러 조합으로 질문자님이 프로그램을 실행했을 때 이 부분에서 프로그램이 눈에 띄는 에러를 발생시키지는 않을 겁니다.
하지만 표준에 따르면 배열의 범위를 벗어나는 배열 참조는 미정의 동작을 유발합니다. 거기까지 안 가더라도, 만에 하나 운이 없어서 배열 범위 밖에서 지뢰(66)가 발견되면 어쩌실 건데요?
덧, 미정의 동작보다는 덜 무섭지만 그래도 무서운 로직 에러 하나: 예컨대
i=3, j=0
이면Cell[i - 1][j - 1]
는 어딜 가리킬까요. 이게 지뢰찾기 프로그램의 올바른 로직인가요?2)
fill_in_the_Grid
함수는 DFS를 재귀로 구현하고 있는데 무한 재귀호출의 여지가 있습니다.프로그램이 죽는 이유가 아마 여기 있을 거 같네요. 한 번 방문한 셀은 다시 방문하지 않도록 막아야 하는데 그런 조건이 없습니다.
그러면, 예컨대 인접한 두 셀이 둘 다
'.'
일 경우(10×10 보드인데다 지뢰는 20개뿐이니 이런 경우가 꽤 있을겁니다.) 그 사이를 왔다갔다하면서 계속 재귀를 돌게 됩니다. 그러다가 스택 오버플로우로 죽는거죠.3)
fill_in_the_Grid
의 재귀호출 DFS 탐색 코드에도 로직 에러가 있군요.예컨대 행의 맨 왼쪽 셀이
'.'
이면 그 윗 행의 가장 오른쪽 셀까지도 열리는 구조인데요.(fill_in_the_Grid(Cell, Grid, Choice - 1);
) 제가 아는 지뢰찾기 규칙과 다릅니다. 벽에 부딛치면 거기서 멈춰야죠.이상입니다. 대체적으로 배열 인덱스 관련해서 실수가 있는 거 같은데 이 부분은 확실히 주의를 기울여야 할 만한 이유가 있는 부분입니다. 질문자님이 초심자라면 더더욱 그렇죠.
제 생각엔 더 찾아보면 버그가 더 있을 것 같은데, 질문글이 깔끔하게 정리되고 나면 한 번 생각해 보도록 하죠. -_-;
헐...
추천드립니다!!!
댓글 달기