카테고리 없음

[C언어] 2차원 배열을 활용한 던전 게임 개선 및 구현

qkr3853 2026. 5. 13. 16:12

본 프로그램은 C언어의 2차원 배열을 이용하여 설계, 수정한 던전 탐색 게임입니다.

 

[전체 구조 메커니즘]

  1. 초기화 구역
    게임이 시작되기 전, 이중 for문을 통해 전체 맵을 빈칸(.)으로 초기화한 뒤 외벽(#), 내벽(#), 보물(T), 몬스터(M), 워프 포탈(O)의 좌표 데이터를 배열에 미리 심어둡니다.
  2. 게임 루프 (while)
    gameOver 변수가 1이 되기 전까지 무한히 반복됩니다.
  3. 렌더링 (system("cls"))
    루프가 돌 때마다 화면을 깨끗이 지우고, 현재 배열 상태와 플레이어 좌표를 계산하여 화면에 실시간으로 출력합니다.
  4. 입력 및 제어 (_getch())
    사용자가 누른 키(W, A, S, D)를 버퍼 없이 즉시 받아와 플레이어의 '다음 좌표(nextX, nextY)'를 계산합니다.
  5. 예외 처리 및 상태 검증
    이동하려는 칸이 벽이면 이벤트를 차단하고, 몬스터면 게임 오버, 보물이면 점수를 추가하며, 포탈(O)일 경우 좌표를 강제로 특수 구역으로 전환시킵니다.
  6. 좌표 업데이트
    검증이 끝나면 플레이어의 현재 위치를 이동 경로인 *로 변경한 뒤 실제 좌표를 갱신합니다.

코드

// 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) 같은 게임의 기본 요소들을 미리 채워두는 저장소로 사용했습니다.
// 선언 부분
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)' 시스템 구현

  • 기존 코드의 아쉬운 점
    그냥 상하좌우로 벽만 피해 다니면서 걷는 방식이라 미로 찾기가 조금 단조롭고 지루한 느낌이 있었습니다.
  • 수정
    특정 칸에 올라가면 다른 좌표로 번쩍 순간이동하는 워프 포탈 기능을 직접 만들었습니다. 비밀의 방 안에서 시작해 포탈을 타고 바깥 미로로 탈출하고, 다시 포탈을 타면 비밀의 방으로 돌아올 수 있게 만들어서 게임이 훨씬 다채롭고 재밌어졌습니다.