티스토리 뷰
학원에서 C 언어를 배우면서 숙제로 만들었던 미로찾기 소스입니다.
이 당시엔 전역변수를 쓰던 단계였어서 중요한 변수들은 전역변수를 쓰고 있습니다.
이거 이후에 지역변수를 쓰는 단계로 넘어갔던 것으로 기억
전체적인 설계는 제가 했지만 마지막 점검 때 코드를 지저분하게 쓴 부분은 선생님이 깔끔하게 손 봐주신 코드입니다.
(구현은 했지만 코드 진행방향에서 중복 코드가 많은 점 등 다소 깔끔하지 못하게 진행되는 부분들...)
각 함수마다 주석을 달아놓았습니다.
코드 분석에 도움이 되길 바라며...
실행화면입니다.
조건은
미로는 자유롭게 설계하되,
게임 속 오브젝트를 3개 이상 만들기였습니다.
오브젝트란 스위치를 누르면 문이 열린다던지 무기를 가지고 벽을 부수거나 적을 물리친다던지 하는 게임 속 요소들을 말합니다.
저는 포탈 입구에 올라서면 출구 포탈로 이동함
스위치를 누르면 문이 열림
적을 만났는데 무기를 가지고 있지 않으면 적 블록이 사라지지 않고 체력이 깎임
무기를 가지고 있으면 적을 없앨 수 있음
퀴즈를 틀리면 퀴즈 블록이 없어지지 않고 체력이 깎임
퀴즈를 맞힐때만 퀴즈 블록이 사라짐
하트를 먹으면 깎인 체력을 회복할 수 있도록 했습니다.
#include<stdio.h>
#include<conio.h>
#include<Windows.h>
//////////////////////////////////////////////////////
#define col GetStdHandle(STD_OUTPUT_HANDLE)
#define BLACK SetConsoleTextAttribute( col,0x0000 );
#define DARK_BLUE SetConsoleTextAttribute( col,0x0001 );
#define GREEN SetConsoleTextAttribute( col,0x0002 );
#define BLUE_GREEN SetConsoleTextAttribute( col,0x0003 );
#define BLOOD SetConsoleTextAttribute( col,0x0004 );
#define PUPPLE SetConsoleTextAttribute( col,0x0005 );
#define GOLD SetConsoleTextAttribute( col,0x0006 ); //색상 지정
#define ORIGINAL SetConsoleTextAttribute( col,0x0007 );
#define GRAY SetConsoleTextAttribute( col,0x0008 );
#define BLUE SetConsoleTextAttribute( col,0x0009 );
#define HIGH_GREEN SetConsoleTextAttribute( col,0x000a );
#define SKY_BLUE SetConsoleTextAttribute( col,0x000b );
#define RED SetConsoleTextAttribute( col,0x000c );
#define PLUM SetConsoleTextAttribute( col,0x000d );
#define YELLOW SetConsoleTextAttribute( col,0x000e );
//////////////////////////////////////////////////////
#define TRUE 1
#define FALSE 0
#define WALL 1
#define NULL 0
#define QUIZ_INDEX 0
#define ANSWER_INDEX 1
#define Y 0
#define X 1
#define CHARACTER 2
#define HEART_TOTAL 3
#define SWORD_TOTAL 4
#define EXIT 5
#define POTAL_MAX 4//포탈 갯수 //각종 표시사항 define
#define ENTRY_START 10
#define EXIT_START 20
#define QUIZ 30
#define QUIZ_MAX 2
#define MEET_QUIZ 2
#define HEAL 40
#define HEAL_MAX 2
#define ENEMY 50
#define MEET_ENEMY 3
#define SWORD 60
#define SWORD_MAX 2
#define DOOR_MAX 3
#define DOOR 70
#define SWITCH_MAX 3
#define SWITCH 80
#define LEFT 75
#define RIGHT 77
#define UP 72
#define DOWN 80
#define WIDTH 20
#define HEIGHT 21
int map[HEIGHT][WIDTH] = { //맵 2차원 배열, 벽 1, 빈공간 0, 캐릭터 3, 무기 4 등등... 자세한 건 define값에서 확인 가능
{ 3, 0, 4, 0, },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 2, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 60, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 81, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 20, 1 },
{ 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 72, 0, 1, 1, 0, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 1, 0, 0, 1, 61, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1 },
{ 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1 },
{ 1, 1, 70, 1, 0, 1, 1, 31, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1 },
{ 1, 40, 1, 1, 1, 0, 0, 1, 82, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 50, 1, 0, 1, 0, 0, 0, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
{ 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1 },
{ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 41, 1, 1, 1, 50, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 71, 0, 0, 1, 1, 1, 0, 0, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1 },
{ 1, 1, 0, 30, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 1, 1, 1, 80, 0, 0, 0, 1, 0, 10, 1, 1, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1 } };
int character[2];
int heartTotal[20];
int heartCounter = 3;
int swordTotal[20];
int swordCounter = 0;
int Exit[2];
int Entry_Potal[POTAL_MAX][2]; //맵 안의 요소들이 여러 개가 되었을 때 따로따로 관리하기 위해서 2차원 배열로 선언
int Exit_Potal[POTAL_MAX][2]; //각 요소들의 최대 갯수만큼 만들어준다.
int LastObjectIndex = NULL; //[MAX][2]로 만든 이유는 특수문자는 배열에서 두 칸을 차지하기 때문
int quiz[QUIZ_MAX][2] = { {0,1},{1,3} };
char quiz_string[QUIZ_MAX][256] = { "Q) 1 + 1 = ? \nA) ① 2 ② 3 ③ 4 ④ 5\n",
"Q) 2021년은 무슨해일까요?\nA) ① 병신년 ② 을사년\n ③ 신축년 ④ 임인년\n" };
int heal[HEAL_MAX][2];
int enemy[2];
int sword[SWORD_MAX][2];
int door[DOOR_MAX][2];
int Switch[SWITCH_MAX][2];
int getExit = 0;
void Init() //캐릭터가 이동할 때마다 맵 전체를 스캔하면 비효율적이니까 처음에 맵 구성요소들을 한 번 스캔해놓고 시작하는 용도로 만든 함수
{//먼저 콘솔창 크기를 설정함
int Width = (WIDTH * 2) + 1; //특수문자는 두 칸을 차지하기 때문에 콘솔창 가로 길이가 최소 2배 이상이 되어야 정상적으로 출력됨
int Height = HEIGHT + 4; //맵 바깥에 출력할 문구들의 줄 수 만큼 세로도 늘려주기
char buf[256];
sprintf(buf, "mode con: lines=%d cols=%d", Height, Width);
system(buf); //여기까지 콘솔창 크기 설정하는 부분
for (int y = 0; y < HEIGHT; y++) //이중반복문으로 맵 전체에 뭐가 있는지 확인하고 각 요소들의 2차원 배열에 좌표값을 지정해준다.
{
for (int x = 0; x < WIDTH; x++)
{
if (map[y][x] == CHARACTER)
{
character[X] = x;
character[Y] = y;
}
else if (map[y][x] == HEART_TOTAL)
{
heartTotal[X] = x;
heartTotal[Y] = y;
}
else if (map[y][x] == SWORD_TOTAL)
{
swordTotal[X] = x;
swordTotal[Y] = y;
}
else if (map[y][x] >= ENTRY_START && map[y][x] < ENTRY_START + POTAL_MAX)
{
Entry_Potal[map[y][x] - ENTRY_START][X] = x;
Entry_Potal[map[y][x] - ENTRY_START][Y] = y;
}
else if (map[y][x] >= EXIT_START && map[y][x] < EXIT_START + POTAL_MAX)
{
Exit_Potal[map[y][x] - EXIT_START][X] = x;
Exit_Potal[map[y][x] - EXIT_START][Y] = y;
}
else if (map[y][x] >= HEAL && map[y][x] < HEAL + HEAL_MAX)
{
heal[map[y][x] - HEAL][X] = x;
heal[map[y][x] - HEAL][Y] = y;
}
else if (map[y][x] >= SWORD && map[y][x] < SWORD + SWORD_MAX)
{
sword[map[y][x] - SWORD][X] = x;
sword[map[y][x] - SWORD][Y] = y;
}
else if (map[y][x] >= DOOR && map[y][x] < DOOR + DOOR_MAX)
{
door[map[y][x] - DOOR][X] = x;
door[map[y][x] - DOOR][Y] = y;
}
else if (map[y][x] >= SWITCH && map[y][x] < SWITCH + SWITCH_MAX)
{
Switch[map[y][x] - SWITCH][X] = x;
Switch[map[y][x] - SWITCH][Y] = y;
}
else if (map[y][x] == EXIT)
{
Exit[X] = x;
Exit[Y] = y;
}
}
}
}
void MapDraw() //맵 2차원 배열을 바탕으로 캐릭터를 포함한 맵을 그리는 함수
{
for (int y = 0; y < HEIGHT; y++)
{
for (int x = 0; x < WIDTH; x++)
{
if (map[y][x] == WALL)
printf("▩");
else if (map[y][x] == CHARACTER)
{
RED
printf("♧");
ORIGINAL
}
else if (map[y][x] == HEART_TOTAL)
{
ORIGINAL
printf("남은 체력 : ");
BLOOD
printf("%d", heartCounter);
ORIGINAL
}
else if (map[y][x] == SWORD_TOTAL)
{
ORIGINAL
printf("무기 갯수 : ");
SKY_BLUE
printf("%d", swordCounter);
ORIGINAL
}
else if (map[y][x] >= ENTRY_START && map[y][x] < ENTRY_START + POTAL_MAX)
{
BLUE
printf("◎");
ORIGINAL
}
else if (map[y][x] >= EXIT_START && map[y][x] < EXIT_START + POTAL_MAX)
{
YELLOW
printf("●");
ORIGINAL
}
else if (map[y][x] >= QUIZ && map[y][x] < QUIZ + QUIZ_MAX)
{
GOLD
printf("??");
ORIGINAL
}
else if (map[y][x] >= HEAL && map[y][x] < HEAL + HEAL_MAX)
{
BLOOD
printf("♥");
ORIGINAL
}
else if (map[y][x] == ENEMY)
{
RED
printf("Θ");
ORIGINAL
}
else if (map[y][x] >= SWORD && map[y][x] < SWORD + SWORD_MAX)
{
SKY_BLUE
printf("ψ");
ORIGINAL
}
else if (map[y][x] >= DOOR && map[y][x] < DOOR + DOOR_MAX)
{
GRAY
printf("¶");
ORIGINAL
}
else if (map[y][x] >= SWITCH && map[y][x] < SWITCH + SWITCH_MAX)
{
PUPPLE
printf("⊙");
ORIGINAL
}
else if (map[y][x] == EXIT)
{
GREEN
printf("▼");
ORIGINAL
}
else if (map[y][x] == NULL)
printf(" ");
}
printf("\n");
}
BLUE
printf("입구 : ◎ ");
YELLOW
printf("출구 : ● ");
GOLD
printf("퀴즈 : ??\n");
RED
printf("적 : Θ ");
SKY_BLUE
printf("무기 : ψ ");
GRAY
printf("문 : ¶\n");
PUPPLE
printf("스위치 : ⊙ ");
BLOOD
printf("체력회복 : ♥");
ORIGINAL
}
void MovePotal() //포탈 입구 위에 올라서면 캐릭터의 위치를 포탈 출구로 바꿔주는 함수
{
int index = map[character[Y]][character[X]];
if (index >= ENTRY_START && index < ENTRY_START + POTAL_MAX)
{
character[X] = Exit_Potal[index - ENTRY_START][X];
character[Y] = Exit_Potal[index - ENTRY_START][Y];
}
}
void getHeal() //캐릭터가 하트 좌표에 가면 체력이 회복되는 함수
{
int index = map[character[Y]][character[X]];
if (index >= HEAL && index < HEAL + HEAL_MAX)
{
map[heal[index - HEAL][Y]][heal[index - HEAL][X]] = NULL;
heartCounter++;
}
}
void removeQuiz(int trueOrFalse) //퀴즈를 풀면 맵에서 ?? 퀴즈 블록이 없어지는 함수
{
if (trueOrFalse == TRUE)
{
if (map[character[Y]][character[X] - 1] >= QUIZ && map[character[Y]][character[X] - 1] < QUIZ + QUIZ_MAX)
map[character[Y]][character[X] - 1] = NULL;
else if (map[character[Y]][character[X] + 1] >= QUIZ && map[character[Y]][character[X] + 1] < QUIZ + QUIZ_MAX)
map[character[Y]][character[X] + 1] = NULL;
else if (map[character[Y] - 1][character[X]] >= QUIZ && map[character[Y] - 1][character[X]] < QUIZ + QUIZ_MAX)
map[character[Y] - 1][character[X]] = NULL;
else if (map[character[Y] + 1][character[X]] >= QUIZ && map[character[Y] + 1][character[X]] < QUIZ + QUIZ_MAX)
map[character[Y] + 1][character[X]] = NULL;
}
}
void popUpQuiz() //캐릭터가 퀴즈 벽에 부딪혔을 때 퀴즈를 띄우는 함수
{
int index = map[character[Y]][character[X]] - QUIZ;
printf("퀴즈를 맞춰야 지나갈 수 있습니다.\n");
printf("%s", quiz_string[quiz[index][QUIZ_INDEX]]);
printf("정답을 입력해 주세요 : ");
int Answer;
scanf("%d", &Answer);
if (Answer == quiz[index][ANSWER_INDEX])
{
printf("정답입니다!\n");
map[character[Y]][character[X]] = NULL;
}
else
{
printf("틀렸습니다.\n");
printf("체력이 감소됩니다.\n");
heartCounter--;
}
system("pause");
}
void takeSword() //캐릭터가 무기 좌표에 가면 무기를 획득하는 함수
{
int index = map[character[Y]][character[X]];
if (index >= SWORD && index < SWORD + SWORD_MAX)
{
map[sword[index - SWORD][Y]][sword[index - SWORD][X]] = NULL;
swordCounter++;
}
}
void killEnemy(char ch) //캐릭터가 적 좌표에 갔을 때 하는 행동 함수
{
if (swordCounter <= 0) //무기가 없으면 체력 감소
heartCounter--;
else //무기가 있으면 무기를 하나 쓰고 적 블록 없애기
{
map[character[Y]][character[X]] = NULL;
swordCounter--;
}
}
void toggleSwitch() //캐릭터가 스위치 좌표에 가면 문 블록이 없어지고 통과가 가능해지는 함수
{
int index = map[character[Y]][character[X]];
if (index >= SWITCH && index < SWITCH + SWITCH_MAX)
map[door[index - SWITCH][Y]][door[index - SWITCH][X]] = NULL;
}
int moveCheck(int object) //캐릭터의 이동 가능 범위를 확인하는 함수. 참일때만 이동 가능
{
if (object == WALL)
return FALSE;
else if (object >= DOOR && object < DOOR + DOOR_MAX)
return FALSE;
else if (object >= QUIZ && object < QUIZ + QUIZ_MAX)
return MEET_QUIZ;
else if (object == ENEMY)
return MEET_ENEMY;
else
return TRUE;
}
int isClear() //게임종료시 상황에 따라 출력되는 문구
{
if (getExit == 1)
{
printf("C L E A R ! !\n");
printf("축하합니다^^\n");
}
else if (getExit == 2)
{
printf("G A M E O V E R\n");
printf("다음엔 더 잘 할 수 있을거에요.\n");
}
}
void Move() //캐릭터가 움직이는 함수
{
char ch;
ch = getch();
system("cls");
map[character[Y]][character[X]] = LastObjectIndex; //현재(이동 전) 캐릭터 좌표에 있는 요소에 있는 것을 지정해 줌. 초기값은 NULL(0)
int Tmp_Character_Position[2];
Tmp_Character_Position[X] = character[X];
Tmp_Character_Position[Y] = character[Y];//현재(이동 전) 캐릭터 위치를 임시 변수에 저장해 놓음
int moveFlag = TRUE;
switch (ch)
{
case LEFT: //moveCheck 함수가 TRUE를 리턴했을 때에만 이동 가능
moveFlag = moveCheck(map[character[Y]][character[X] - 1]);
character[X]--;
break;
case RIGHT:
moveFlag = moveCheck(map[character[Y]][character[X] + 1]);
character[X]++;
break;
case UP:
moveFlag = moveCheck(map[character[Y] - 1][character[X]]);
character[Y]--;
break;
case DOWN:
moveFlag = moveCheck(map[character[Y] + 1][character[X]]);
character[Y]++;
break;
}
if (LastObjectIndex == EXIT) //만약 캐릭터가 출구 위에 있으면 게임 클리어
getExit = 1;
else if (heartCounter <= 0) //체력이 없으면 게임 오버
getExit = 2;
if (moveFlag == TRUE) //이동 가능하면 각 좌표에 알맞는 행동 수행
{
MovePotal();
getHeal();
takeSword();
toggleSwitch();
}
else //적과 퀴즈를 만났을 때엔 각각 해당하는 행동 수행
{
if (moveFlag == MEET_ENEMY)
killEnemy(ch);
else if (moveFlag == MEET_QUIZ)
popUpQuiz(ch);
character[X] = Tmp_Character_Position[X];
character[Y] = Tmp_Character_Position[Y]; //이동을 한 것은 아니기 때문에 이동 전에 저장해 두었던 직전 위치를 다시 캐릭터 현재 위치로 바꿔줌
}
LastObjectIndex = map[character[Y]][character[X]]; //이동 후 캐릭터 좌표에 있는 요소를 저장해준다.
map[character[Y]][character[X]] = CHARACTER; //이동 후 캐릭터 좌표의 요소를 캐릭터 define값으로 바꿔준다. 이러면 맵을 다시 그릴 때 캐릭터가 이동하는 것처럼 보임
//이렇게 쓰는 이유는 캐릭터를 이동하려면 이전 위치에 있던 그림을 지운 후 이동한 위치에 새로 그리는 방식으로 해야 움직이는 것처럼 보이기 때문
}
void main()
{
Init(); //Init 함수는 처음 한 번만 쓰고
while (1) //맵을 그리는 함수와 캐릭터를 움직이는 함수를 무한반복문에서 호출한다.
{
MapDraw();
Move();
if (getExit == 1 || getExit == 2) //게임 종료 조건에 따라 문구를 출력하고 게임 종료
{
isClear();
break;
}
}
}
이걸 만들던 당시엔 프로그래밍 언어를 처음 배우는데다 전공자도 아니었어서 참 많은 당혹감과 혼란스러움 속에서 코드를 썼었는데 지금 보니까 더 간편하게 코드를 짤 수 있을거 같습니다.
이젠 c++도 배웠거든요!
여전히 새로운 숙제가 생기면 허덕이지만...
혹시 코드 관련해서 궁금한 점이 있으면 댓글로 질문해주세요!
'코딩 공부 > C 언어' 카테고리의 다른 글
[C언어 기초] if ~ else와 else if (0) | 2021.10.10 |
---|---|
[C언어 기초] 조건문 if (0) | 2021.10.05 |
C 언어 기초 : 연산자 3 - 비트 연산자 (0) | 2021.04.16 |
C 언어 기초 : 연산자 2 (0) | 2021.04.16 |
C 언어 기초 : 연산자 1 (0) | 2021.04.14 |
- Total
- Today
- Yesterday
- 깊이우선탐색
- 컴퓨터
- 너비우선탐색
- 백준
- 캐나다생활
- 해커랭크
- 컴퓨터사이언스
- 코딩공부
- 그리디
- 기초
- C언어기초
- 캐나다
- 프로그래머스
- 영어공부
- 알고리즘
- hackerrank
- DFS
- 애플
- 프로그래밍
- 문제풀이
- dp
- 아이패드
- c언어
- 컴퓨터공부
- greedy
- 하드웨어
- 다이나믹프로그래밍
- BFS
- 스위프트플레이그라운드
- c++
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |