이게 뭐라고 또 올립니다...;



사실 테트리스를 만들려다가 급 귀찮아지면 같은 솔루션안에 묶여있는 스네이크 프로젝트를 열고 머리를 비우고 스네이크 게임을 하게 되더라구요...


그러다가 이거좀 고쳐볼까? 생각하게되고 조금 뜯어고쳐보는거죠.



/*

_WINDOW : 윈도우

_C4DROID : 안드로이드 C4Droid 기준

*/

#define _WINDOW


#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <malloc.h>

#ifdef _WINDOW

#include <Windows.h>

#include <conio.h>

#endif

#ifdef _C4DROID

#include <conio.h>

#endif


/*상수 정의*/

#define EMPTY 0

#define WALL 1

#define SNAKE 2

#define FOOD 3


#define UP 0

#define DOWN 1

#define LEFT 2

#define RIGHT 3


/*각종 구조체*/

typedef struct Pos {

int x;

int y;

}Pos;


typedef struct Map {

int** map;

int max_height;

int max_width;


int food_count;

Pos food;

}Map;


typedef struct Snake {

Pos head;

Pos* bodys;

int body_length;

int way;

}Snake;


/*함수 선언*/

void intro(void); //인트로 출력 함수

void gameStart(Map* map, Snake* snake); //게임 시작 함수


Pos setPos(int x, int y); //편의를 위한 좌표 반환 함수


void initMap(Map* map, int height, int width); //맵 기본 설정

void setWall(Map* map); //벽을 설정함

void printMap(const Map* map, const Snake* snake); //맵 출력

char getObjectSymbol(int code); //각 오브젝트 코드별 문자를 반환(아스키코드 내에서만 가능)

void mapReflesh(Map* map, const Snake* snake); //맵에 오브젝트들을 새로고침 함.

void placeFood(Map *map); //맵에 푸드를 배치함.


void initSnake(Snake* snake, int default_length); //스네이크 기본 설정

int moveSnake(Map* map, Snake* snake); //스네이크 이동

void snakeKeyEvent(Snake* snake); //스네이크 이동 경로 변경

void addSnakeBody(Snake* snake); //스네이크 몸 길이 증가

void eatFood(Map* map, Snake* snake); //스네이크가 음식을 먹음


int main(void)

{

const int MAP_MAX_HEIGHT = 20;

const int MAP_MAX_WIDTH = 20;



Map map; //맵

Snake snake; //스네이크


srand(time(NULL));


while (1)

{

intro();

initMap(&map, MAP_MAX_HEIGHT, MAP_MAX_WIDTH);

initSnake(&snake, 5);


gameStart(&map, &snake);

}


return 0;

}


void intro(void)

{

#ifdef _WINDOW

system("cls");

#endif

#ifdef _C4DROID

clrscr();

#endif

printf("==[ C로 만든 텍스트 스네이크 게임 ]==\n");

printf("\n아무 키나 누르면 시작합니다.\n");

#ifdef _WINDOW

_getch();

#endif

#ifdef _C4DROID

getch();

#endif

}

/*Pos 관련 함수 정의*/

Pos setPos(int x, int y)

{

Pos pos;

pos.x = x;

pos.y = y;


return pos;

}

void gameStart(Map* map, Snake* snake)

{

while (1)

{

snakeKeyEvent(snake);


switch (moveSnake(map, snake))

{

case -1:

printf("알 수 없는 오류 발생");

break;


case 1://사망

printf("이런 머리를 박고 사망하였습니다.\n");

/*메모리 반환*/

free(snake->bodys);

#ifdef _DEBUG

printf("[Debug] snake.bodys 메모리 반환 완료\n");

#endif


free(map->map);

#ifdef _DEBUG

printf("[Debug] map.map 메모리 반환 완료\n");

#endif

#ifdef _WINDOW

_getch();

#endif

#ifdef _C4DROID

getch();

#endif

return;

}

placeFood(map);

mapReflesh(map, snake);

printMap(map, snake);

#ifdef _WINDOW

Sleep(80);

#endif

#ifdef _C4DROID

usleep(100000);

#endif

}

}

/***************************

* 맵 관련 함수 정의

****************************/

void setWall(Map *map)

{

int i;


/*상단 벽면 설정*/

for (i = 0; i < map->max_width; i++)

map->map[i][0] = WALL;

/*좌측 벽면 설정*/

for (i = 0; i < map->max_height; i++)

map->map[0][i] = WALL;

/*하단 벽면 설정*/

for (i = 0; i < map->max_width; i++)

map->map[i][map->max_height - 1] = WALL;

/*우측 벽면 설정*/

for (i = 0; i < map->max_height; i++)

map->map[map->max_width - 1][i] = WALL;

}


void printMap(const Map* map, const Snake* snake)

{

int i, j;


#ifdef _WINDOW

system("cls");

#endif

#ifdef _C4DROID

clrscr();

#endif

for (i = 0; i < map->max_height; i++)

{

for (j = 0; j < map->max_width; j++)

putchar(getObjectSymbol(map->map[j][i]));

/*추가 출력*/

switch (i)

{

case 0:

printf(" - 길이 : %d", snake->body_length + 1);

break;

#ifdef _DEBUG

case 1:

printf(" - D:헤드 좌표 : (%d, %d)", snake->head.x, snake->head.y);

break;

case 2:

printf(" - D:진행 방향 : %d", snake->way);

break;

#endif

}

putchar('\n');

}

}


char getObjectSymbol(int code)

{

switch (code)

{

case EMPTY:

return ' ';

case WALL:

return '@';

case SNAKE:

return '#';

case FOOD:

return '*';

default:

return 'E';

}

}


void initMap(Map* map, int height, int width)

{

int i;

map->max_height = height;

map->max_width = width;

map->food_count = 0;

map->map = (int**)malloc(sizeof(int*) * width);


for (i = 0; i < width; i++)

{

map->map[i] = (int*)malloc(sizeof(int) * height);

}

}


void mapReflesh(Map* map, const Snake* snake)

{

int i, j;

/*비움*/

for (i = 0; i < map->max_width; i++)

{

for (j = 0; j < map->max_height; j++)

{

map->map[i][j] = EMPTY;

}

}


/*벽 표시*/

setWall(map);


/*스네이크 표시*/

map->map[snake->head.x][snake->head.y] = SNAKE;

for (i = 0; i < snake->body_length; i++)

{

map->map[snake->bodys[i].x][snake->bodys[i].y] = SNAKE;

}


/*푸드 표시*/

map->map[map->food.x][map->food.y] = FOOD;

}


void placeFood(Map *map)

{

if (map->food_count == 0)

{

do

{

map->food = setPos((rand() % (map->max_width - 2) + 1), (rand() % (map->max_height - 2) + 1));

map->food_count = 1;

} while (map->map[map->food.x][map->food.y] == SNAKE);

}

}


/*****************************

* 스네이크 관련 함수 정의

******************************/

void initSnake(Snake* snake, int default_length)

{

int i;


snake->way = RIGHT;

snake->head = setPos(4, 4);


snake->bodys = (Pos*)malloc(sizeof(Pos) * default_length);

snake->body_length = default_length;


for (i = 0; i < default_length; i++)

{

snake->bodys[i] = snake->head;

}

}


int moveSnake(Map* map, Snake* snake)

{

int i;

int x = snake->head.x, y = snake->head.y; //뱀 헤드 좌표의 임시 변수

Pos temp_pos;

Pos temp_pos2;


temp_pos = snake->head;

switch (snake->way)

{

case UP:

switch (map->map[x][y - 1])

{

case FOOD: eatFood(map, snake);

case 0:

snake->head.y--;

break;

case 1:

case 2:

return 1;

}

break;


case DOWN:

switch (map->map[x][y + 1])

{

case FOOD: eatFood(map, snake);

case 0:

snake->head.y++;

break;

case 1:

case 2:

return 1;

}

break;


case LEFT:

switch (map->map[x - 1][y])

{

case FOOD: eatFood(map, snake);

case 0:

snake->head.x--;

break;

case 1:

case 2:

return 1;

}

break;


case RIGHT:

switch (map->map[x + 1][y])

{

case FOOD: eatFood(map, snake);

case 0:

snake->head.x++;

break;

case 1:

case 2:

return 1;

}

break;

default:

return -1;

}


for (i = 0; i<snake->body_length; i++)

{

temp_pos2 = snake->bodys[i];

snake->bodys[i] = temp_pos;


temp_pos = temp_pos2;

}

return 0;

}


void snakeKeyEvent(Snake* snake)

{

#ifdef _WINDOW

if (_kbhit())

{

switch (_getch())

#endif

#ifdef _C4DROID

if (kbhit())

{

switch (getch())

#endif

{

case 'w':

case 'W':

if (!(snake->way == DOWN))

snake->way = UP;

break;

case 'a':

case 'A':

if (!(snake->way == RIGHT))

snake->way = LEFT;

break;

case 's':

case 'S':

if (!(snake->way == UP))

snake->way = DOWN;

break;

case 'd':

case 'D':

if (!(snake->way == LEFT))

snake->way = RIGHT;

break;

case 'p':

case 'P':

getchar();

}

}

}


void addSnakeBody(Snake* snake)

{

snake->body_length++;

snake->bodys = (Pos*)realloc(snake->bodys, sizeof(Pos) * snake->body_length);

}


void eatFood(Map* map, Snake* snake)

{

map->food_count = 0;

addSnakeBody(snake);

}



자꾸 goto문이 거슬려서.. 사실 switch와 그 위에 있는 while문을 같이 탈출 해야하는데.

귀찮아서 goto문으로 때워버렸었지요... ㅂㄷㅂㄷㅂㄷ


뭐 계속 보다보니 거슬려서 그냥 함수로 떼어내서 고쳤습니다.



매크로 정의
_DEBUG : 디버그용 매크로 입니다. VisualStudio기준 디버그모드로 실행하게 되면 이 매크로가 자동으로 정의됩니다. 이를 정의하면 일부 디버그용 메시지가 표시됩니다.
_WINDOW : 윈도우OS전용 매크로 입니다. 소스코드내에 이미 정의상태로 되어있습니다. 이 매크로를 정의하게 되면 윈도우에 종속적인 일부 함수들이 활성화 됩니다. _C4DROID 등 OS를 위한 매크로와 중복 적용하지 마십시오.
_C4DROId : 안드로이드의 C4Droid 앱을 위한 매크로 입니다. 이 매크로를 정의하게 되면 이에 맞는 일부 함수들이 활성화 됩니다. _WINDOW등 OS를 위한 매크로와 중복 적용하지 마십시오.



조작키

WASD - 이동
P - 일시 정지
Enter - 일시정지 해제


게임방법
머리를 본인의 몸(#)이나 벽(@)에 박으면 사망.
*을 먹으면 몸 길이가 늘어남.


게임 목표
의미 없이 몸이나 늘리세요.


스네이크 게임.exe


x86에 맞춘 빌드 입니다.