- 概述
- 采用的库
- 功能规划
- 最初规划
- 已完成功能
- 游戏界面
- 代码实现
- 规划中的数据结构
- 具体实现
为了熟悉linux下的c语言编程,所以尝试做的,具体功能及实现有可能会慢慢更改。
仓库地址
图形化将采用curses库,存储存档及得分榜采用ndbm库,利用groff编写manpage,编译及安装采用make。
功能规划 最初规划 使用命令行启动游戏,利用命令行参数可以打印版本信息、帮助信息,初始化得分榜,开启作弊模式(碰到自身或墙壁不算游戏失败)。如果不带参数则直接启动游戏。
开始界面右侧窗口显示得分榜,左侧显示新游戏、载入存档、删除存档、退出游戏四个选项。顶上一行显示标题,最底下一行显示临时状态。
游戏界面左边窗口为游戏窗口,右上窗口显示操作指南,右下窗口显示当前得分及速度等级。
- 参数模式打印版本、帮助信息
- 开始界面新游戏、退出游戏
- 游戏界面正常游戏
- 游戏界面显示操作指南
- 游戏界面显示当前得分及速度等级
游戏将有两个界面,即主界面和游戏界面。
-
主界面:
主界面将要完成开启新游戏、载入存档和删除存档的任务,及循环显示得分榜。
-
游戏界面:
主要分为左、右上、右下三个板块,游戏区域,操作说明区域,当前得分及速度的显示区域。
-
流程图:
这是最初规划的流程图,实际实现会有一定的增删。
- 蛇的身体:
将实现为一个结构体数组,数组长度为整个游戏区的格数,数组中每个结构体含有x,y两个成员,即身体每个点的坐标。 - 蛇的身体的当前长度:
由一个全局变量保存 - 当前未占用的格子:
维护一个全局的二维布尔数组,每次更新蛇的身体的同时,将蛇的身体所占用的格子对应的这个布尔数组的成员设为false。这个数组所有为true的成员即代表对应的格子允许生成食物。 - 现存的食物:
与蛇的身体一样的结构体。 - 当前得分:
与蛇的身体的总长相关 - 游戏者姓名:
一个字符数组保存 - 当前速度:
根据蛇身体的总长变化,每一次刷新的间隔,其区间将是1000ms~50ms。 - 存储文件
存档文件,每条数据由一个编号,一个姓名,蛇身体长度,蛇的身体的数组组成。
得分榜文件,每条数据由姓名及得分组成。
data.h
#ifndef _DATA_H #define _DATA_H #include#include #include #include #include #include #include #define WINDOW_WIDTH 40 #define WINDOW_HEIGHT 20 #define TOTLE_POINT ((WINDOW_WIDTH-2)*(WINDOW_HEIGHT-2)) #define SPEED_MAX 1000 #define STR_LEN 38 #define VERSION ("1.00") typedef struct { int x; int y; }node; typedef node food; typedef node direct; typedef node *snake; //存档数据和得分榜数据处理函数 //游戏逻辑函数 void init_status(WINDOW *win_ptr,direct *d_ptr,food *f_ptr,snake greedy,char *name); void destory_status(snake greedy); void end_game(WINDOW *win_ptr,char *string); //用于参数模式的函数 int command_mode(int argc,char *argv[]); //用于开始界面的函数 void draw_base_window(void); void draw_select_window(WINDOW *win_ptr,char *options[],int current_highlight,int start_row,int start_col); void clear_start_screen(void); int getchoice(WINDOW *win_ptr,char *choices[]); //用于游戏界面的函数 void Checkmap(snake greedy); void draw_snake_window(WINDOW *win_ptr,snake greedy,food f1); void draw_status_window(WINDOW *win_ptr,char *name); void update_snake(snake greedy,direct d,bool *eated); void init_keyboard(WINDOW *w_ptr); void get_key(direct *d); void close_keyboard(WINDOW *w_ptr); bool Eatfood(snake greedy,food f1); bool Isover(snake greedy); bool Iswin(void); void Createfood(food *fd); #endif
frontend.c
#define _GNU_SOURCE
#include "data.h"
//用于参数模式的函数声明
static void version(void);
static void help(void);
static void opt_error(char c);
//用于开始界面的函数声明
//用于参数模式的函数定义
static void version(void)
{
fprintf(stdout,"greedy snake version %sn",VERSION);
}
static void help(void)
{
fprintf(stdout,"Usage: snake [options]n");
fprintf(stdout,"Options:n");
fprintf(stdout,"t-v/--versiontdisplay the version informationn");
fprintf(stdout,"t-h/--helptdisplay the help informationn");
fprintf(stdout,"t-i/--inittinitialize the ranking listn");
fprintf(stdout,"t-c/--cheattinto the cheat mode, you will not die until got full marksn");
}
static void opt_error(char c)
{
fprintf(stderr,"unknown option: %cnplease use snake --help to get more informationn",c);
}
static void cheat(void)
{
extern bool Cheat;
Cheat=true;
}
int command_mode(int argc,char *argv[])
{
extern bool Cheat;
int result=0,opt;
struct option longopts[]=
{
{"version",0,NULL,'v'},
{"help",0,NULL,'h'},
{"init",0,NULL,'i'},
{"cheat",0,NULL,'c'},
{0,0,0,0}
};
while((opt=getopt_long(argc,argv,":vhic",longopts,NULL))!=-1)
{
switch(opt)
{
case 'v':
version();
result=1;
break;
case 'h':
help();
result=1;
break;
case 'i':
result=0;
break;
case 'c':
cheat();
result=0;
break;
case '?':
opt_error(optopt);
result=2;
break;
}
}
return result;
}
//用于游戏逻辑的函数定义
void init_status(WINDOW *win_ptr,direct *d_ptr,food *f_ptr,snake greedy,char *name)
{
char *prompt[]=
{
"enter your name: ",
0
};
extern int Current_len;
int seed;
seed=rand()%4;
switch(seed)//随机产生初始方向
{
case 0:
d_ptr->x=0;
d_ptr->y=-1;
break;
case 1:
d_ptr->x=0;
d_ptr->y=1;
break;
case 2:
d_ptr->x=-1;
d_ptr->y=0;
break;
case 3:
d_ptr->x=1;
d_ptr->y=0;
break;
}
Current_len=0;
Checkmap(greedy);
while(true)//产生一个不靠边框的蛇头
{
Createfood(&greedy[0]);
if(greedy[0].x>1&&greedy[0].x1&&greedy[0].yx;
greedy[1].y=greedy[0].y-d_ptr->y;
Current_len=2;
Checkmap(greedy);
Createfood(f_ptr);
move(LINES-2,1);
clrtoeol();
mvprintw(LINES-2,1,"touch enter to save your name.");
refresh();
draw_select_window(win_ptr,prompt,-1,WINDOW_HEIGHT/2-1,WINDOW_WIDTH/2-10);
wgetnstr(win_ptr,name,STR_LEN-1);
}
void destory_status(snake greedy)
{
free(greedy);
}
void end_game(WINDOW *win_ptr,char *string)
{
wclear(win_ptr);
box(win_ptr,ACS_VLINE,ACS_HLINE);
mvwprintw(win_ptr,WINDOW_HEIGHT/2-1,WINDOW_WIDTH/2-10,"%s",string);
wrefresh(win_ptr);
sleep(2);
}
//用于开始界面的函数定义
void draw_base_window(void)
{
clear();
box(stdscr,ACS_VLINE,ACS_HLINE);
mvprintw(1,COLS/2-7,"%s","Greedy Snake");
refresh();
}
void draw_select_window(WINDOW *win_ptr,char *options[],int current_highlight,int start_row,int start_col)
{
wclear(win_ptr);
box(win_ptr,ACS_VLINE,ACS_HLINE);
int current_row=0;
char **option_ptr;
char *txt_ptr;
option_ptr=options;
while(*option_ptr)
{
if(current_row==current_highlight)
wattron(win_ptr,A_STANDOUT);
txt_ptr=options[current_row];
mvwprintw(win_ptr,start_row+current_row*2,start_col,"%s",txt_ptr);
if(current_row==current_highlight)
wattroff(win_ptr,A_STANDOUT);
current_row++;
option_ptr++;
}
wrefresh(win_ptr);
}
void clear_start_screen(void)
{
clear();
mvprintw(1,COLS/2-7,"%s","Greedy Snake");
refresh();
}
int getchoice(WINDOW *win_ptr,char *choices[])
{
static int selected_row=0;
int max_row=0;
int start_screenrow=WINDOW_HEIGHT/2-5,start_screencol=WINDOW_WIDTH/2-6;
char **options;
int selected;
int key=0;
options=choices;
mvprintw(LINES-2,1,"Move highlight then press enter");
refresh();
while(*options)
{
max_row++;
options++;
}
keypad(stdscr,true);
cbreak();
noecho();
while(key!=KEY_ENTER&&key!='n')
{
if(key==KEY_UP)
{
if(selected_row==0)
selected_row=max_row-1;
else
selected_row--;
}
if(key==KEY_DOWN)
{
if(selected_row==max_row-1)
selected_row=0;
else
selected_row++;
}
selected=*choices[selected_row];
draw_select_window(win_ptr,choices,selected_row,start_screenrow,start_screencol);
key=getch();
}
keypad(stdscr,false);
nocbreak();
echo();
return selected;
}
//用于游戏界面的函数定义
void draw_snake_window(WINDOW *win_ptr,snake greedy,food f1)
{
extern int Current_len;
wclear(win_ptr);
box(win_ptr,ACS_VLINE,ACS_HLINE);
mvwaddch(win_ptr,f1.y,f1.x,'@');
for(int i=Current_len-1;i>=0;i--)
{
if(i==0)
mvwaddch(win_ptr,greedy[i].y,greedy[i].x,'#');
else
mvwaddch(win_ptr,greedy[i].y,greedy[i].x,'*');
}
wrefresh(win_ptr);
}
void draw_status_window(WINDOW *win_ptr,char *name)
{
extern int Current_len;
int score=Current_len;
char speed_string[STR_LEN];
char score_string[STR_LEN];
sprintf(score_string,"Current Score = %d",score);
sprintf(speed_string,"Current Speed level = %d",(Current_len/35));
wclear(win_ptr);
box(win_ptr,ACS_VLINE,ACS_HLINE);
mvwprintw(win_ptr,WINDOW_HEIGHT/9,WINDOW_WIDTH/4,"Hello %s",name);
mvwprintw(win_ptr,WINDOW_HEIGHT/5,WINDOW_WIDTH/4,"%s",score_string);
mvwprintw(win_ptr,WINDOW_HEIGHT/3,WINDOW_WIDTH/4,"%s",speed_string);
wrefresh(win_ptr);
}
void Checkmap(snake greedy)
{
extern bool Map[WINDOW_HEIGHT-2][WINDOW_WIDTH-2];
extern int Current_len;
int index_x,index_y,i;
for(index_y=0;index_y0;i--)
{
greedy[i]=greedy[i-1];
}
greedy[0]=temp;
Checkmap(greedy);
}
void init_keyboard(WINDOW *w_ptr)
{
keypad(stdscr,true);
noecho();
cbreak();
leaveok(w_ptr,true);
timeout(SPEED_MAX);
}
void get_key(direct *d)
{
int key;
if((key=getch())!=ERR)
{
switch(key)
{
case 'A':
case 'a':
case KEY_LEFT:
if(d->x!=1)
{
d->x=-1;
d->y=0;
}
break;
case 'D':
case 'd':
case KEY_RIGHT:
if(d->x!=-1)
{
d->x=1;
d->y=0;
}
break;
case 'W':
case 'w':
case KEY_UP:
if(d->y!=1)
{
d->x=0;
d->y=-1;
}
break;
case 'S':
case 's':
case KEY_DOWN:
if(d->y!=-1)
{
d->x=0;
d->y=1;
}
break;
}
}
}
void close_keyboard(WINDOW *w_ptr)
{
keypad(stdscr,false);
echo();
timeout(-1);
nocbreak();
leaveok(w_ptr,false);
}
bool Eatfood(snake greedy,food f1)
{
if(greedy[0].x==f1.x&&greedy[0].y==f1.y)
return true;
else
return false;
}
bool Isover(snake greedy)
{
extern int Current_len;
bool flag=false;
if(greedy[0].x==0||greedy[0].x==(WINDOW_WIDTH-1)||greedy[0].y==0||greedy[0].y==(WINDOW_HEIGHT-1))
flag=true;
for(int i=1;ix=index_x+1;
fd->y=index_y+1;
}
main.c
#include#include #include #include #include "data.h" bool Cheat=false; int Current_len; bool Map[WINDOW_HEIGHT-2][WINDOW_WIDTH-2]; int main(int argc,char *argv[]) { char *start_menu[]= { "new game", "load game", "delete data", "quit", 0 }; char *instructions[]= { "use up/down/left/right", "or w/s/a/d", "to control the snake", "Esc to save/end the game", 0 }; srand(time(0)); snake greedy; greedy=malloc(sizeof(food)*TOTLE_POINT); bool eatedfood=false; int key; food f; direct d; char name[STR_LEN]; //参数处理 int command_result; if(argc>1) { command_result=command_mode(argc,argv); if(command_result) exit(command_result); } //开始界面 initscr(); draw_base_window(); WINDOW *start_rank_win=newwin(WINDOW_HEIGHT,WINDOW_WIDTH,(LINES-WINDOW_HEIGHT)/2,COLS/2+3); WINDOW *select_win=newwin(WINDOW_HEIGHT,WINDOW_WIDTH,2,6); draw_select_window(start_rank_win,start_menu,-1,1,1); do { command_result=getchoice(select_win,start_menu); switch(command_result) { case 'n': init_status(select_win,&d,&f,greedy,name); command_result='g'; break; case 'l': command_result='g'; break; case 'd': break; case 'q': exit(EXIT_SUCCESS); } }while(command_result!='g'); delwin(start_rank_win); draw_base_window(); //游戏界面 //现在select_win作为游戏窗口 WINDOW *instructions_win=newwin(WINDOW_HEIGHT/2,WINDOW_WIDTH,(LINES-WINDOW_HEIGHT)/2,COLS/2+3); WINDOW *status_win=newwin(WINDOW_HEIGHT/2,WINDOW_WIDTH,LINES/2,COLS/2+3); draw_select_window(instructions_win,instructions,-1,1,WINDOW_WIDTH/4); init_keyboard(select_win); while(true) { timeout(SPEED_MAX-(Current_len/35)*50);//改变速度 draw_status_window(status_win,name); draw_snake_window(select_win,greedy,f); if(Isover(greedy)) { end_game(select_win,"Game Over!"); break; } if(Iswin()) { end_game(select_win,"You Win!"); break; } if(Eatfood(greedy,f)) { Createfood(&f); eatedfood=true; } get_key(&d); update_snake(greedy,d,&eatedfood); } close_keyboard(select_win); delwin(select_win); delwin(instructions_win); delwin(status_win); endwin(); destory_status(greedy); exit(EXIT_SUCCESS); }
注:现在的游戏逻辑已基本完善,接下来会更新数据存储,更新的代码在仓库内查看



