치춘짱베리굿나이스
(과제용) 장애물 피하기 / 뱀 게임 본문
대학교 3학년때 임베디드 전공프로젝트로 보드 가지고 아무거나 만들어보는 수업이 있었다
말그대로 자유주제라.. 우리 팀은 보드 (덩치큰 한백보드였음) 안에 있는 기능 최대한 사용해서
고전오락기 컨셉으로 준비했었음
장애물피하기 게임

원래 의도했던 건 크롬 오프라인 때 나오는 공룡게임이었는데
한백보드 컬러 LCD화면 쓰자니 반응속도나 이미지 출력면에서 영 못미더웠고
결국 두줄짜리 텍스트 LCD화면을 쓰기로함
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <conio.h> | |
#include <time.h> | |
#include <Windows.h> | |
char line1[16]; | |
char line2[16]; | |
int speed = 400; | |
int count = 0; | |
int flag = 2; | |
int score = 0; | |
void shiftleft(char arr[]) { | |
//첫번째 자리 제외 shift 필요 | |
//rotate가 아닌 shift이기 때문에 arr[1]은 밀려 사라진다 | |
for (int i = 1; i < 15; i++) { | |
arr[i] = arr[i + 1]; | |
} | |
arr[15] = ' '; | |
} | |
int main(void) { | |
srand(time(NULL)); | |
int p_obs; | |
for (int i = 0; i < 16; i++) { | |
line1[i] = ' '; | |
line2[i] = ' '; | |
} | |
while (1) { | |
p_obs = rand() % 5; | |
//점프 | |
if (flag == 1 && _kbhit()) { | |
flag = 2; //방향: 2번째 줄로 | |
} | |
else if (flag == 2 && _kbhit()) { | |
flag = 1; //방향: 1번째 줄로 | |
} | |
if (flag == 1) { //A를 1번째 줄로 이동 | |
line1[0] = 'A'; | |
line2[0] = ' '; | |
} | |
else if (flag == 2) { //A를 2번째 줄로 이동 | |
line1[0] = ' '; | |
line2[0] = 'A'; | |
} | |
if (count % 2 == 0) { //2턴에 한번씩 | |
if (p_obs == 1 || p_obs == 3) { //1번째 줄에 장애물 등장 | |
line1[15] = 'o'; | |
line2[15] = ' '; | |
} | |
else if (p_obs == 2 || p_obs == 4) { //2번째 줄에 장애물 등장 | |
line1[15] = ' '; | |
line2[15] = 'o'; | |
} | |
else { | |
line1[15] = ' '; | |
line2[15] = ' '; | |
} | |
} | |
//화면에 표시 | |
printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", | |
line1[0], line1[1], line1[2], line1[3], line1[4], line1[5], line1[6], line1[7], | |
line1[8], line1[9], line1[10], line1[11], line1[12], line1[13], line1[14], line1[15], | |
line2[0], line2[1], line2[2], line2[3], line2[4], line2[5], line2[6], line2[7], | |
line2[8], line2[9], line2[10], line2[11], line2[12], line2[13], line2[14], line2[15]); | |
printf("\n%d\n", score); | |
while (_kbhit()) _getch(); //_kbhit flush | |
count++; //장애물 등장 count를 ++함 | |
//shift 전에 장애물에 닿는지 미리 체크 | |
if (line1[0] == 'A' && line1[1] == 'o') { | |
printf("\nGame Over!!"); | |
return 0; | |
} | |
else if (line2[0] == 'A' && line2[1] == 'o') { | |
printf("\nGame Over!!"); | |
return 0; | |
} | |
//배열을 left shift - 장애물이 이동 | |
shiftleft(line1); | |
shiftleft(line2); | |
Sleep(speed); //delay를 주고 | |
if (score >= 10 && score < 30) { | |
speed = 300; | |
} | |
else if (score >= 30 && score < 50) { | |
speed = 250; | |
} | |
else if (score >= 50 && score < 80) { | |
speed = 200; | |
} | |
else if (score >= 80) { | |
speed = 100; | |
} | |
score++; | |
system("cls"); //화면을 업데이트함 | |
} | |
} |

결과물은 엄청 단순했다
점프기능을 넣으니 장애물이 날아오는 판정이랑 제자리로 돌아오는 판정이 꼬여서
멋대로 죽거나 안죽어버리는 불상사가 생겼기 때문에
2차선 도로에서 장애물을 피해서 차선변경하는 게임이 되어버림
단계가 올라갈수록 장애물 등장 속도도 점점 빨라지긴 하는데
한백보드 자체가 반응속도가 꽤 느린 편이라
일정속도 이상은 포기했다
사실 일정 단계 이후부터 엄청 빠르게 해서 아무도 못이기게 만드는게 목표였는데
뱀 게임
그 사과먹고 길어지는 뱀 게임이 맞다
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<stdio.h> | |
#include<windows.h> | |
#include<conio.h> | |
#include<stdlib.h> | |
#include<time.h> | |
//키패드 | |
#define LEFT 75 | |
#define RIGHT 77 | |
#define UP 72 | |
#define DOWN 80 | |
//모눈칸 (도트매트릭스 10*7) | |
#define MAP_X 20 | |
#define MAP_Y 20 | |
int x[100], y[100]; | |
int food_x, food_y; //food x, y | |
int length; //body | |
int speed; | |
int score; | |
int dir; | |
int button; //keypad button | |
int ggflag = 0; | |
void gotoxy(int x, int y, char* s) { | |
COORD pos = { x, y }; //ASCII 문자열은 2바이트 | |
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos); | |
printf("%s", s); | |
} | |
void reset(void); //게임을 초기화 | |
void move(int dir); //뱀머리를 이동 | |
void food(void); // 음식 생성 | |
int main() { | |
reset(); | |
while (1) { | |
if (_kbhit()) do { button = _getch(); } while (button == 224); | |
//키를 입력받으면 button에 방향 저장 | |
Sleep(speed); | |
switch (button) { | |
case LEFT: //left, right, up, down 모두 | |
case RIGHT: //down의 실행문이 수행됨 | |
case UP: | |
case DOWN: | |
if ((dir == LEFT && button != RIGHT) || (dir == RIGHT && button != LEFT) || | |
(dir == DOWN && button != UP) || (dir == UP && button != DOWN)) | |
dir = button; //180도 방향전환 방지용 | |
button = 0; //키값 초기화 | |
break; | |
//Pause, ESC 없음 | |
} | |
move(dir); | |
if (ggflag == 1) { | |
printf("GAME OVER!!\n"); | |
return 0; | |
} | |
} | |
} | |
void reset(void) { | |
system("cls"); | |
while (_kbhit()) _getch(); //키보드 버퍼 비우기 | |
dir = LEFT; //방향 초기화 | |
speed = 200; //속도 초기화 | |
length = 2; //뱀 길이 초기화 | |
score = 0; //점수 초기화 | |
for (int i = 0; i < length; i++) { | |
x[i] = MAP_X / 2 + i; //맵의 중심에서 시작 | |
y[i] = MAP_Y / 2; | |
gotoxy(x[i], y[i], "o"); | |
} | |
gotoxy(x[0], y[0], "O"); | |
food(); | |
} | |
void move(int dir) { | |
if (x[0] == food_x && y[0] == food_y) { | |
//음식 먹으면 | |
score += 10; | |
food(); //랜덤하게 음식 다시 생성 | |
length++; //길이 증가 | |
x[length - 1] = x[length - 2]; | |
y[length - 1] = y[length - 2]; | |
} | |
if (x[0] == 0 || x[0] == MAP_X - 1 || y[0] == 0 || y[0] == MAP_Y - 1) { | |
//벽에 충돌했을 경우 | |
ggflag = 1; | |
return; | |
} | |
for (int i = 1; i < length; i++) { | |
if (x[0] == x[i] && y[0] == y[i]) { | |
//지 몸과 충돌했을 경우 | |
ggflag = 1; | |
return; | |
} | |
} | |
gotoxy(x[length - 1], y[length - 1], " "); | |
for (int i = length - 1; i > 0; i--) { | |
//좌표 한칸씩 이동 | |
x[i] = x[i - 1]; | |
y[i] = y[i - 1]; | |
} | |
gotoxy(x[0], y[0], "o"); | |
//움직일 방향에 따라 머리의 좌표 변경 | |
if (dir == LEFT) --x[0]; | |
if (dir == RIGHT) ++x[0]; | |
if (dir == UP) --y[0]; | |
if (dir == DOWN) ++y[0]; | |
gotoxy(x[0], y[0], "O"); | |
} | |
void food(void) { | |
int i; | |
int food_crush_on = 0;//food가 뱀 몸통좌표에 생길 경우 on | |
int r = 0; //난수 생성에 사동되는 변수 | |
gotoxy(0, MAP_Y + 1, " YOUR SCORE: "); //점수표시 | |
printf("%3d", score); | |
while (1) { | |
food_crush_on = 0; | |
srand((unsigned)time(NULL) + r); //난수표생성 | |
food_x = (rand() % (MAP_X - 2)) + 1; //난수를 좌표값에 넣음 | |
food_y = (rand() % (MAP_Y - 2)) + 1; | |
for (i = 0; i < length; i++) { //food가 뱀 몸통과 겹치는지 확인 | |
if (food_x == x[i] && food_y == y[i]) { | |
food_crush_on = 1; //겹치면 food_crush_on 를 on | |
r++; | |
break; | |
} | |
} | |
if (food_crush_on == 1) continue; //겹쳤을 경우 while문을 다시 시작 | |
gotoxy(food_x, food_y, "v"); //안겹쳤을 경우 좌표값에 food를 찍고 | |
speed -= 10; //속도 증가 | |
break; | |
} | |
} |
이건 dos 버전
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <windows.h> | |
#include <conio.h> | |
#include <stdlib.h> | |
#include <time.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <sys/ioctl.h> // ioctl | |
#include <sys/mman.h> // mmap PROT_ | |
#include <linux/fb.h> | |
#define FBDEV_FILE "/dev/fb0" | |
//키패드 | |
#define LEFT 75 | |
#define RIGHT 77 | |
#define UP 72 | |
#define DOWN 80 | |
//모눈칸 (한블럭은 4*4) | |
#define MAP_X 200 | |
#define MAP_Y 120 | |
//뱀게임 변수 | |
int x[100], y[100]; | |
int food_x, food_y; //food x, y | |
int length; //body | |
int speed; | |
int score; | |
int dir; | |
int button; //keypad button | |
int ggflag = 0; | |
//GraphicLCD 변수 | |
int screen_width; | |
int screen_height; | |
int bits_per_pixel; | |
int line_length; | |
int fb_fd; | |
struct fb_var_screeninfo fbvar; | |
struct fb_fix_screeninfo fbfix; | |
unsigned char* fb_mapped; | |
int mem_size; | |
unsigned short* ptr; | |
//좌표값만큼 for문 돌리기 위한 변수 | |
int coor_y; | |
int coor_x; | |
int i; | |
void init(void); //GraphicLCD 값 initialize | |
void reset(void); //게임 & 화면을 초기화 | |
void move(int dir); //뱀머리를 이동 | |
void food(void); // 음식 생성 | |
void gotoxy(int x, int y, unsigned short color) { | |
int i, j; | |
int x1, y1; | |
for (i = 4*y - 3; i < 4*y + 1; i++) { | |
ptr = (unsigned short)*fb_mapped + screen_width * i; | |
for (j = 4*x - 3; j < 4*x + 1; j++) { | |
*ptr++ = color; | |
} | |
} | |
} | |
int main(int argc, char** argv) { | |
printf("SNAKE GAME\nEAT SOME APPLES!!\n"); | |
init(); | |
reset(); | |
while (1) { | |
if (_kbhit()) do { button = _getch(); } while (button == 224); | |
//키를 입력받으면 button에 방향 저장 | |
Sleep(speed); | |
switch (button) { | |
case LEFT: //left, right, up, down 모두 | |
case RIGHT: //down의 실행문이 수행됨 | |
case UP: | |
case DOWN: | |
if ((dir == LEFT && button != RIGHT) || (dir == RIGHT && button != LEFT) || | |
(dir == DOWN && button != UP) || (dir == UP && button != DOWN)) | |
dir = button; //180도 방향전환 방지용 | |
button = 0; //키값 초기화 | |
break; | |
//Pause, ESC 없음 | |
} | |
move(dir); | |
if (ggflag == 1) { | |
printf("GAME OVER!!\n"); | |
munmap(fb_mapped, mem_size); | |
close(fb_fd); | |
return 0; | |
} | |
} | |
} | |
void init(void) { | |
if (access(FBDEV_FILE, F_OK)) | |
{ | |
printf("%s: access error\n", FBDEV_FILE); | |
exit(1); | |
} | |
if ((fb_fd = open(FBDEV_FILE, O_RDWR)) < 0) | |
{ | |
printf("%s: open error\n", FBDEV_FILE); | |
exit(1); | |
} | |
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &fbvar)) | |
{ | |
printf("%s: ioctl error - FBIOGET_VSCREENINFO \n", FBDEV_FILE); | |
exit(1); | |
} | |
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fbfix)) | |
{ | |
printf("%s: ioctl error - FBIOGET_FSCREENINFO \n", FBDEV_FILE); | |
exit(1); | |
} | |
screen_width = fbvar.xres; // 스크린의 픽셀 폭 | |
screen_height = fbvar.yres; // 스크린의 픽셀 높이 | |
bits_per_pixel = fbvar.bits_per_pixel; // 픽셀 당 비트 개수 | |
line_length = fbfix.line_length; // 한개 라인 당 바이트 개수 | |
mem_size = screen_width * screen_height * 2; | |
fb_mapped = (unsigned char*)mmap(0, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0); | |
} | |
void reset(void) { | |
for (coor_y = 0; coor_y < 480; coor_y++) { | |
ptr = (unsigned short*)fb_mapped + screen_width * coor_y; | |
for (coor_x = 0; coor_x < 800; coor_x++) { | |
*ptr++ = 0x0000; | |
} | |
} | |
while (_kbhit()) _getch(); //키보드 버퍼 비우기 | |
dir = LEFT; //방향 초기화 | |
speed = 200; //속도 초기화 | |
length = 4; //뱀 길이 초기화 | |
score = 0; //점수 초기화 | |
for (int i = 0; i < length; i++) { | |
x[i] = MAP_X / 2 + i; //맵의 중심에서 시작 | |
y[i] = MAP_Y / 2; | |
gotoxy(x[i], y[i], 0xffff); | |
} | |
gotoxy(x[0], y[0], 0x07e0); | |
food(); | |
} | |
void move(int dir) { | |
if (x[0] == food_x && y[0] == food_y) { | |
//음식 먹으면 | |
score += 10; | |
food(); //랜덤하게 음식 다시 생성 | |
length++; //길이 증가 | |
x[length - 1] = x[length - 2]; | |
y[length - 1] = y[length - 2]; | |
} | |
if (x[0] == 0 || x[0] == MAP_X - 1 || y[0] == 0 || y[0] == MAP_Y - 1) { | |
//벽에 충돌했을 경우 | |
ggflag = 1; | |
return; | |
} | |
for (int i = 1; i < length; i++) { | |
if (x[0] == x[i] && y[0] == y[i]) { | |
//지 몸과 충돌했을 경우 | |
ggflag = 1; | |
return; | |
} | |
} | |
gotoxy(x[length - 1], y[length - 1], 0x0000); | |
for (int i = length - 1; i > 0; i--) { | |
//좌표 한칸씩 이동 | |
x[i] = x[i - 1]; | |
y[i] = y[i - 1]; | |
} | |
gotoxy(x[0], y[0], 0xffff); | |
//움직일 방향에 따라 머리의 좌표 변경 | |
if (dir == LEFT) --x[0]; | |
if (dir == RIGHT) ++x[0]; | |
if (dir == UP) --y[0]; | |
if (dir == DOWN) ++y[0]; | |
gotoxy(x[0], y[0], 0x07e0); | |
} | |
void food(void) { | |
int i; | |
int food_crush_on = 0;//food가 뱀 몸통좌표에 생길 경우 on | |
int r = 0; //난수 생성에 사동되는 변수 | |
//gotoxy(0, MAP_Y + 1, " YOUR SCORE: "); //점수표시 | |
printf("YOUR SCORE : %3d", score); | |
while (1) { | |
food_crush_on = 0; | |
srand((unsigned)time(NULL) + r); //난수표생성 | |
food_x = (rand() % (MAP_X - 2)) + 1; //난수를 좌표값에 넣음 | |
food_y = (rand() % (MAP_Y - 2)) + 1; | |
for (i = 0; i < length; i++) { //food가 뱀 몸통과 겹치는지 확인 | |
if (food_x == x[i] && food_y == y[i]) { | |
food_crush_on = 1; //겹치면 food_crush_on 를 on | |
r++; | |
break; | |
} | |
} | |
if (food_crush_on == 1) continue; //겹쳤을 경우 while문을 다시 시작 | |
gotoxy(food_x, food_y, 0xf800); //안겹쳤을 경우 좌표값에 food를 찍고 | |
if (speed >= 50) speed -= 10; //속도 증가 | |
break; | |
} | |
} |
이건 컬러 LCD용 버전
뱀이 ooO 가 아니라 흰색 도트그래픽으로 표현되어있다
더 구체적인 표현은 이미지로 표현하자니 뱀 몸늘어나는거라던가 넣는게 불가능해서 색으로 표현
한백보드 반응속도때문에 나름 어렵고 그렇다
이게 벌써 2년전 프로젝트라니 시간참 빠르다
이 프로젝트 덕에 C랑 연을 끊게 된 것 같기도 하고... 아닌가? 포인터때문인가...
그래도 팀원들덕에 재밌었음 한편으론 좀 미안하기도하고