치춘짱베리굿나이스

(과제용) 장애물 피하기 / 뱀 게임 본문

C C++/기타

(과제용) 장애물 피하기 / 뱀 게임

치춘 2021. 2. 5. 19:39

대학교 3학년때 임베디드 전공프로젝트로 보드 가지고 아무거나 만들어보는 수업이 있었다

말그대로 자유주제라.. 우리 팀은 보드 (덩치큰 한백보드였음) 안에 있는 기능 최대한 사용해서

고전오락기 컨셉으로 준비했었음

 

장애물피하기 게임

원래 의도했던 건 크롬 오프라인 때 나오는 공룡게임이었는데

한백보드 컬러 LCD화면 쓰자니 반응속도나 이미지 출력면에서 영 못미더웠고

결국 두줄짜리 텍스트 LCD화면을 쓰기로함

 

#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"); //화면을 업데이트함
}
}
view raw obs.c hosted with ❤ by GitHub

결과물은 엄청 단순했다

점프기능을 넣으니 장애물이 날아오는 판정이랑 제자리로 돌아오는 판정이 꼬여서

멋대로 죽거나 안죽어버리는 불상사가 생겼기 때문에

2차선 도로에서 장애물을 피해서 차선변경하는 게임이 되어버림

 

단계가 올라갈수록 장애물 등장 속도도 점점 빨라지긴 하는데

한백보드 자체가 반응속도가 꽤 느린 편이라

일정속도 이상은 포기했다

사실 일정 단계 이후부터 엄청 빠르게 해서 아무도 못이기게 만드는게 목표였는데

 

뱀 게임

그 사과먹고 길어지는 뱀 게임이 맞다

 

 

#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;
}
}
view raw snake.c hosted with ❤ by GitHub

이건 dos 버전

 

#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;
}
}
view raw snake_LCD.c hosted with ❤ by GitHub

이건 컬러 LCD용 버전

뱀이 ooO 가 아니라 흰색 도트그래픽으로 표현되어있다

더 구체적인 표현은 이미지로 표현하자니 뱀 몸늘어나는거라던가 넣는게 불가능해서 색으로 표현

한백보드 반응속도때문에 나름 어렵고 그렇다

 

이게 벌써 2년전 프로젝트라니 시간참 빠르다

이 프로젝트 덕에 C랑 연을 끊게 된 것 같기도 하고... 아닌가? 포인터때문인가...

그래도 팀원들덕에 재밌었음 한편으론 좀 미안하기도하고

Comments