티스토리 뷰

반응형

 

학원에서 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
링크
«   2024/11   »
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
글 보관함