본 프로그램은 C언어의 2차원 배열을 이용하여 설계, 수정한 던전 탐색 게임입니다.
[전체 구조 메커니즘]
- 초기화 구역
게임이 시작되기 전, 이중 for문을 통해 전체 맵을 빈칸(.)으로 초기화한 뒤 외벽(#), 내벽(#), 보물(T), 몬스터(M), 워프 포탈(O)의 좌표 데이터를 배열에 미리 심어둡니다. - 게임 루프 (while)
gameOver 변수가 1이 되기 전까지 무한히 반복됩니다. - 렌더링 (system("cls"))
루프가 돌 때마다 화면을 깨끗이 지우고, 현재 배열 상태와 플레이어 좌표를 계산하여 화면에 실시간으로 출력합니다. - 입력 및 제어 (_getch())
사용자가 누른 키(W, A, S, D)를 버퍼 없이 즉시 받아와 플레이어의 '다음 좌표(nextX, nextY)'를 계산합니다. - 예외 처리 및 상태 검증
이동하려는 칸이 벽이면 이벤트를 차단하고, 몬스터면 게임 오버, 보물이면 점수를 추가하며, 포탈(O)일 경우 좌표를 강제로 특수 구역으로 전환시킵니다. - 좌표 업데이트
검증이 끝나면 플레이어의 현재 위치를 이동 경로인 *로 변경한 뒤 실제 좌표를 갱신합니다.
코드
// Week 9 Practice
// 2D Array Dungeon Game - 개선 버전 (발자국 및 포탈 전용 방 설계)
// 학번: 22620024_박재영
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#define ROWS 15
#define COLS 30
int main(void)
{
char map[ROWS][COLS];
char key;
int playerY = 1;
int playerX = 1;
int nextY;
int nextX;
int score = 0;
int turn = 0;
int gameOver = 0;
int i, j;
// ==========================================
// 1. 맵 초기화
// ==========================================
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLS; j++) {
map[i][j] = '.';
}
}
// ==========================================
// 2. 바깥 벽 만들기
// ==========================================
for (i = 0; i < ROWS; i++) {
map[i][0] = '#';
map[i][COLS - 1] = '#';
}
for (j = 0; j < COLS; j++) {
map[0][j] = '#';
map[ROWS - 1][j] = '#';
}
// ==========================================
// 3. 내부 벽 만들기 (좌측 상단을 아예 가두는 벽 설계)
// ==========================================
// 좌측 상단을 완전히 밀봉하는 가로벽과 세로벽
for (j = 1; j < 14; j++) {
map[5][j] = '#'; // 5행을 가로지르는 가로벽
}
for (i = 1; i < 6; i++) {
map[i][14] = '#'; // 14열을 가로지르는 세로벽
}
// 기존 나머지 구역 벽들도 조화롭게 배치
for (j = 14; j < 25; j++) {
map[8][j] = '#';
}
for (i = 6; i < 13; i++) {
map[i][6] = '#';
}
// ==========================================
// 4. 보물 배치
// ==========================================
// [핵심] 외부와 차단된 비밀의 방 내부에 보물 배치
map[2][3] = 'T';
map[3][2] = 'T';
// 나머지 일반 구역 보물들
map[3][18] = 'T';
map[11][3] = 'T';
map[12][25] = 'T';
// ==========================================
// 5. 몬스터 배치
// ==========================================
map[2][18] = 'M';
map[9][24] = 'M';
map[12][10] = 'M';
// ==========================================
// 5-2. 워프 포탈 배치
// ==========================================
map[2][12] = 'O'; // 1번 포탈: 격리된 비밀의 방 내부에 위치 (탈출용)
map[13][2] = 'O'; // 2번 포탈: 외부 아래쪽 구역에 위치 (진입용)
// ==========================================
// 6. 게임 시작
// ==========================================
while (gameOver == 0) {
system("cls");
printf("============================================================\n");
printf(" 2D ARRAY DUNGEON GAME (Isolated Secret Room Ver)\n");
printf("============================================================\n");
printf("이동: W A S D / 종료: Q\n");
printf("점수: %d 턴: %d 위치: (%d, %d)\n", score, turn, playerX, playerY);
printf("============================================================\n\n");
// ==========================================
// 7. 맵 출력
// ==========================================
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLS; j++) {
if (i == playerY && j == playerX) {
printf("P ");
}
else {
if (map[i][j] == '#') {
printf("# ");
}
else if (map[i][j] == '.') {
printf(". ");
}
else if (map[i][j] == '*') {
printf("* ");
}
else if (map[i][j] == 'O') {
printf("O ");
}
else if (map[i][j] == 'T') {
printf("T ");
}
else if (map[i][j] == 'M') {
printf("M ");
}
else {
printf("? ");
}
}
}
printf("\n");
}
printf("\n");
printf("설명: P=플레이어, #=벽, T=보물, M=몬스터, O=포탈, *=지나온 길, .=빈칸\n");
printf("키 입력: ");
key = _getch();
if (key == 'q' || key == 'Q') {
printf("\n게임을 종료합니다.\n");
break;
}
nextY = playerY;
nextX = playerX;
// ==========================================
// 8. 이동 방향 계산
// ==========================================
switch (key) {
case 'w':
case 'W':
nextY = playerY - 1;
break;
case 's':
case 'S':
nextY = playerY + 1;
break;
case 'a':
case 'A':
nextX = playerX - 1;
break;
case 'd':
case 'D':
nextX = playerX + 1;
break;
default:
printf("\n잘못된 키입니다.\n");
Sleep(500);
continue;
}
// ==========================================
// 9. 이동 가능 여부 확인
// ==========================================
if (map[nextY][nextX] == '#') {
Beep(300, 150);
printf("\n벽입니다. 이동할 수 없습니다.\n");
Sleep(500);
continue;
}
// ==========================================
// 10. 몬스터 만남
// ==========================================
if (map[nextY][nextX] == 'M') {
system("cls");
printf("============================================================\n");
printf("몬스터를 만났습니다!\n");
printf("게임 오버!\n");
printf("최종 점수: %d\n", score);
printf("총 턴 수: %d\n", turn);
printf("============================================================\n");
gameOver = 1;
break;
}
// ==========================================
// 11. 보물 획득
// ==========================================
if (map[nextY][nextX] == 'T') {
score += 10;
map[nextY][nextX] = '.';
Beep(1000, 80);
Beep(1200, 80);
}
// ==========================================
// 12. 실제 이동 전 발자국 남기기 & 포탈 워프 처리
// ==========================================
map[playerY][playerX] = '*';
if (map[nextY][nextX] == 'O') {
// 외부 포탈(13, 2)을 밟으면 완전히 막혀있는 비밀의 방 안쪽(2, 11)으로 워프
if (nextY == 13 && nextX == 2) {
nextY = 2;
nextX = 11;
}
// 비밀의 방 내부 포탈(2, 12)을 밟으면 외부 포탈 바로 옆 칸(13, 3)으로 안전하게 탈출
else if (nextY == 2 && nextX == 12) {
nextY = 13;
nextX = 3;
}
Beep(800, 150);
}
// ==========================================
// 12. 실제 이동
// ==========================================
playerY = nextY;
playerX = nextX;
turn++;
// ==========================================
// 13. 승리 조건
// ==========================================
if (score >= 50) {
system("cls");
printf("============================================================\n");
printf("모든 보물을 찾았습니다!\n");
printf("게임 클리어!\n");
printf("최종 점수: %d\n", score);
printf("총 턴 수: %d\n", turn);
printf("============================================================\n");
gameOver = 1;
break;
}
}
printf("\n프로그램을 종료합니다.\n");
return 0;
}
1. 프로그램 실행 화면 캡처 (최소 3장)



2. 본인이 만든 맵 설명 (5줄 이상)
기존의 단순했던 내부 벽 배치를 고치고, 좌측 상단 구역을 벽으로 완전히 둘러싸 가로막은 '비밀의 방' 구조로 만들었습니다.
이 방은 사방이 벽(#)으로 꽁꽁 막혀있기 때문에, 플레이어가 (1, 1) 위치인 비밀의 방 안에서 게임을 시작하여 반드시 방 안의 포탈(O)을 밟아야만 바깥 미로로 순간이동해 탈출할 수 있도록 설계했습니다.
일단 탈출한 뒤에는 바깥 구석구석에 숨겨진 보물들을 찾아다녀야 합니다.
여기에 내가 지나간 자리는 발자국(*)이 남도록 수정하여, 미로를 탐색하면서 내가 움직였던 곳의 위치를 발자국을 통해 알 수 있게 되어 의미 없는 움직임을 최소화하며 최단 거리로 보물을 먹을 수 있게 재미있게 맵을 완성했습니다.
3. 배열을 어떻게 사용했는지 설명
- 맵의 기본 배경 저장 (2차원 배열 활용)
- 세로 15줄, 가로 30칸짜리 바둑판 모양의 공간을 만들기 위해 char map[15][30]이라는 2차원 배열을 만들었습니다.
이 배열 칸칸마다 벽(#), 빈칸(.), 보물(T), 몬스터(M), 포탈(O) 같은 게임의 기본 요소들을 미리 채워두는 저장소로 사용했습니다.
- 세로 15줄, 가로 30칸짜리 바둑판 모양의 공간을 만들기 위해 char map[15][30]이라는 2차원 배열을 만들었습니다.
// 선언 부분
char map[ROWS][COLS]; // ROWS=15, COLS=30으로 선언된 2차원 배열
// 데이터 배치 부분 (일부)
map[5][j] = '#'; // 내부 벽 생성
map[2][3] = 'T'; // 보물 배치
map[2][12] = 'O'; // 포탈 배치
- 플레이어 위치 출력 방식 (원본 데이터 보호)
플레이어(P)가 움직일 때마다 배열 안에 있는 데이터를 직접 지우고 새로 쓰면, 플레이어가 보물이나 포탈 위에 올라갔을 때 그 자리에 있던 원래 데이터가 지워져 버리는 문제가 생깁니다. 이를 막기 위해 맵 배열 데이터는 그대로 유지하고, 화면에 맵을 그려서 보여주는 순간에만 플레이어의 좌표를 확인하여 P를 겹쳐서 보여주는 방식을 사용했습니다.
// 7. 맵 실시간 렌더링 부분
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLS; j++) {
// 원본 배열 데이터를 덮어쓰지 않고, 출력 시점에 좌표만 확인해서 P를 얹어줌
if (i == playerY && j == playerX) {
printf("P ");
}
else {
if (map[i][j] == '#') printf("# ");
// ... (이하 생략)
}
}
}
- 지나간 길 실시간 변경
- 플레이어가 한 칸 움직여서 이동할 때마다, 바로 직전에 서 있던 배열 칸의 값을 원래 빈칸(.)에서 발자국 기호(*)로 실시간으로 바꾸어 저장하도록 배열을 활용했습니다.
// 12. 실제 이동 전 발자국 남기기 부분
// 플레이어가 실제로 다음 좌표로 이동하기 직전, 현재 밟고 있는 배열의 칸을 '*'로 교체
map[playerY][playerX] = '*';
playerY = nextY;
playerX = nextX;
turn++;
4. 기존 코드 대비 개선점 설명
1. 지나간 자리에 발자국(*) 남기기 기능 추가
- 기존 코드의 아쉬운 점
원래 코드는 플레이어가 움직여도 지나온 자리가 그냥 똑같은 빈칸(.)으로 남았습니다. 그래서 미로 안에서 내가 이미 가본 길인지, 아니면 새로 가야 하는 길인지 헷갈려서 턴만 낭비하는 경우가 많았습니다. - 수정
플레이어가 한 걸음 옮기기 직전에, 지금 서 있는 자리를 * 모양으로 바꾸는 코드를 넣었습니다. 내가 지나온 길이 실시간으로 맵에 표시되니까 미로를 헤매지 않고 훨씬 편하게 탐색할 수 있게 되었습니다.
2. 순간이동하는 '워프 포탈(O)' 시스템 구현
- 기존 코드의 아쉬운 점
그냥 상하좌우로 벽만 피해 다니면서 걷는 방식이라 미로 찾기가 조금 단조롭고 지루한 느낌이 있었습니다. - 수정
특정 칸에 올라가면 다른 좌표로 번쩍 순간이동하는 워프 포탈 기능을 직접 만들었습니다. 비밀의 방 안에서 시작해 포탈을 타고 바깥 미로로 탈출하고, 다시 포탈을 타면 비밀의 방으로 돌아올 수 있게 만들어서 게임이 훨씬 다채롭고 재밌어졌습니다.