小组实验报告

附件1:

华南农业大学信息学院

课程设计实验

实验题目:贪吃蛇

一、分析题目要求

经过我们3人小组的讨论,最终确定了用C 语言写贪吃蛇的游戏。

首先,要想实现贪吃蛇就要有游戏界面;其次,还要有写贪吃蛇的设计思

路以及对于数据结构的操作;另外,还应该有游戏的规则实现以及界面的美化

等。因此,我们把这个实验任务分成了3个部分,xxx 具体负责的是蛇的构

造以及食物的生成,xxx 负责界面,xxx 负责游戏规则。

要实现贪吃蛇能够前进,我们需要用到链表来存取蛇的身体,以及蛇所在

的方位。一般情况下,需要用到链表节点的删除,增加,输出等操作。另外,

产生随见事物使用系统自带的Rand ,随机定位一个游戏区域里面的方块,然后

使用gotoxy 函数可以定位到这个方块的坐标,并且涂上颜色,这样就产生了肉

眼可见的食物。关于蛇体,我们将蛇体的每个方块添加到链表里面,使用的是

snake,为什么不用数组,原因是数组的长度总是有限的,你不知道蛇体里面最

终存放多少方格,而且数组比较繁琐。通过不断的生成蛇头以及删除蛇尾循环

画出列表中的每个对象,就成功的画出了蛇体。设置蛇体的初始长度只有蛇头,

并设置蛇头的初始位置为居中。

(1) 各个函数命名,包含的参数,返回值类型

定义#include 这个头文件,它包含了其他Windows 头文件,这些

头文件的某些也包含了其他头文件:

static COORD position;//显示当前指针的坐标

static COORD food_position;//显示当前食物的坐标

static HANDLE hOut;//获取标准输出的句柄。命令行的程序会把字符输出到屏幕

定义#include 头文件,是Console Input/Output(控制台输入输出)

的简写,其中定义了通过控制台进行数据输入和数据输出的函数,主要是通过

按键盘产生的对应操作,比如getch()函数,而在Fsnake.h 中定义的

#define LEFT 75 #define UP 72 #define DOWN 80 #define RIGHT 77 #define ESC 27

#define SPACE 32 #define ENTER 13 即为getch()函数从键盘接收到的各种键值。

在Fsnake.h 中定义的:

#define GREEN 0xe #define L_GREEN 0xB #define RED 0xC 是用到

Windows.h 函数中的setcolor 函数:

以下SetConsoleTextAttribute(hOut, color); //设置字体颜色

各颜色的代码如下:

10=淡绿色 0xa

3=湖蓝色 11=淡浅绿色 0xb

4=红色 12=淡红色 0xc

5=紫色 13=淡紫色 0xd

6=黄色 14=淡黄色 0xe

7=白色 15=亮白色 0xf

在Fsnake.h 中定义的:

#define WIDTH 40

#define HEIGHT 24是地图的长和宽

生成地图的函数:

void start()//游戏地图中的开始界面

void gotoxy(int x, int y, int color)//控制命令行光标位置

int 类型的横纵坐标x ,y ,应经声明的color 。

void map()//生成地图

void HideCursor()//隐藏光标

此外,还有其他的操作函数:

void gotoxy(int x, int y, int color);

void map();

void HideCursor();

void cre_food();

void cre_snake(int x, int y);

int get_key();

void start();

void wipe(int x, int y);

void move();

int is_eat();

int is_dead();

void outputsnake();

void levelscore();

void deteSnaketail();

void checkinput();

(2)、输入输出的形式与达到的功能:

1、定义蛇的结构体为

typedef struct snake

{

int x;

int y;

struct snake *pre;

struct snake *next;

} NODESNAKE; //蛇链表

2、定义运行界面的边框函数为void map();该函数没有输入,输出的

是贪吃蛇的游戏界面范围。图示如下:

3、定义void start();作为游戏开始时候的提示,该函数没有参数,

但是需要输入数据类型的只有fsnake.h 中定义的静态数值”ESC ”和

“ENTER ”;它的输出是清除游戏界面内的文字;图示如下:

4、定义函数void cre_food( );为贪吃蛇创造食物;该函数没有参数,也没

有返回值;并且该函数不需要输入,它由自己用rand( )创造伪随机数用于程

序;该函数的输出即为随机产生的食物,图示如下:

5、定义函数int is_dead( );用于判断蛇是否死亡,死亡有两种形式:一是撞

到自己,二是撞到墙壁;is_dead()的返回值为0或1用于判断蛇的状态,他

没有参数,也不需要输入输出数据。

6、定义函数void move();用于控制贪吃蛇的移动,它所调用的函数有检

测输入函数void checkinput();检测是否吃到食物函数int is_eat();以及可能

会调用删除尾节点函数void deteSnaketail();创建食物函数void cre_food();和

输出分数等级函数void levelscore();并且该函数并不需要输入变量和输出某

一个值。

7、定义函数void outputsnake();用于输出蛇的状态,并且及时输出的前

进方向,该函数没有输入但是输出蛇的身体。

8、游戏一开始按enter 键进入游戏,按↑、↓、←、→控制蛇分别向上、

下、左、右不同方向运动,按空格键暂停,按任意键继续,按Esc 退出。如

果蛇死亡了,还可以按1继续,按任意键退出游戏。程序所达到的功能: 开

始,创建食物,创建蛇,判断蛇是否吃到食物,蛇是否死亡,控制蛇移动的

方向,等级,计分,速度,是否重新开始等功能。

9、我们选的是把地图范围和开始页面设置成绿色,左边工具栏与提示栏设

置为黄色,分数与等级为红色,食物也为红色,蛇为黄色正方形。地图设置

为长为40格,宽为24格,贪吃蛇每移动一次为一格,在横坐标0-8格之内

为工具栏,9-39格是贪吃蛇游戏的地图。

void start()//游戏地图中的开始界面

void gotoxy(int x, int y, int color)//控制命令行光标位置

输入横纵坐标x ,y 把该格定义为color 颜色

void map()//生成地图

生成贪吃蛇的游戏地图,与工具栏,开始页面

void HideCursor()//隐藏光标

隐藏输入的光标

二、解题思路

(1)、游戏界面的生成

1、游戏界面生成思路:

根据codeblocks 的输出框的大小,先找出游戏方框的大小,然后输出游

戏的边框以及文字说明。制作游戏的窗体布局。其中包括窗体的大小,位置,

添加按钮组件,在窗体上面划定游戏区域等。我们需要知道的只是边框的高

度以及宽度。

2、伪代码实现:

For()/*循环次数不超过边框宽度*/

绘出第一行墙壁;

For()/*循环次数不超过边框高度-1*/

绘出中间的竖直部分墙壁;

For()/*循环次数不超过边框宽度*/

绘出最后一行墙壁;

/*通过定为坐标来定为光标*/

写出各个需要提示的游戏说明;

(2)、创建食物和蛇的身体的实验思路:

由于食物的生成需要实现随机分布,所以要用到rand();这个函数来实现 产生随机数,只不过要注意的是我们需要判断食物的产生是否在游戏区域以

及是否和之前产生的蛇是否位置重叠。伪代码实现如下:

P=头指针;

While(p !=NULL){

If (随机数=p的数据域);

则 返回”p!=NULL”之前进行循环操作,;

P=p->next;

}

如果蛇头为空,那么将坐标定位到游戏界面正中央,并且将该处的坐标值

赋给头结点;如果蛇头不为空,那么将创建一个新的蛇头并且将现在的蛇头

下一个坐标赋值给新的蛇头。并且以此循环,直到蛇死亡、游戏结束。伪代

码实现如下:

P=游戏界面坐标;

head->pre=p;

P->next=head;

P->pre=NULL;

Head=p;

(3)、检测键盘是否输入实验思路:

首先要注意的是,我们在游戏时,并不是来自键盘上所有的输入都可以实现

蛇的移动,因此我们需要判断键盘上面的输入是否是我们想要的输入。至于关

于键盘的输入由队友来写,我想说的是,通过方向键设置蛇头的移动,蛇头每

移动一次位置就会变化到另外一个方块,对应的坐标x,y 都是有相应的变化,

例如按下UP 键,坐标的变化就是,x 不变,y 减1。

(4)、删去蛇尾思路:

在这里,只需要对蛇链表进行删除操作,不过要注意的是,在删除之前要

对蛇尾的光标进行擦除操作。最后要注意释放已删除的节点;

(5)、蛇在界面上的输出思路:

我的思路是,为了避免删除蛇尾的操作对整个蛇移动界面的影响,我觉得可

以对蛇链表进行两次遍历,一次是遍历擦除界面上所有的图案,第二次是直接

遍历输出整个蛇的形态。伪代码实现为:

While(p!=NULL){

擦除p 对应坐标的图像;

P=p->next;

}

While(p!=NULL){

定为p 点对应的坐标;

画出蛇的一节;

P=p->next;

}

(6)游戏等级、分数、速度的思路:游戏一开始吃一个食物得10分,当得分足够时升级,吃一个食物得分相应增加,速度也相应加快。用switch 选择相应功能。

(7)是否吃到食物的思路:通过判断蛇头的位置是否与食物位置重合来判断。有函数int is_eat()。

(8)判断蛇是否死亡的思路:蛇的死亡分两种:咬到自己与碰到边界;通过判断蛇头是否与边界或者自己的身体重合来判断是否死亡;有函数nt is_dead()。

三、调试分析

(1)、调试过程中遇到的问题:

首先遇到的问题是关于界面的和怎么样从键盘输入的一些问题,这些函数都

与include库函数有关,经过一番查找,我们找到了关于怎么样从

键盘直接输入数据的的方法,以及在界面上定位光标。因为队友主要负责这个

所以我不在赘述。其次,问体在于怎么样实现贪吃蛇的移动,由于最开始我们

只想到通过增加蛇头以及删除蛇尾的方法来移动,却没有准确判断吃到食物后

蛇的长度变化。

另外,在游戏结束后重新开始进行第二轮游戏时,出现了蛇的长度不变,分

数出现混乱的情况,经过修改发现是链表没有初始化,分数等级也没有初始化

的,所以才会出现乱码,以及图像的扭曲。

还有就是出现了下面这种情况:

经过讨论分析,我们发现是擦除尾部节点的时候覆盖了蛇本身的不需要擦除

的节点,然后我们把输出函数改成了先全部擦除所有的蛇的节点,然后在全部

显示,只不过这样又要多一遍循环。经过修改成功的消除了bug 。

我采用的是每实现一个功能就进行测试,分模块测试时没有出错,合起来就出错

了,经过仔细检查后发现有些语句写错了导致错误,经改正后正常运行。还有一

些for 循环使用不当,造成程序运行失败,经过多次测试后找到错误原因,改进

后正常运行。在调试过程中会发现等级与得分的数字不平衡时,需要数出他们的

横纵坐标,换为一样的,然后中间的开始界面也要确定好坐标值,将第一个字母

放在整个地图横坐标的中间。然后食物的随机生成会生成在了工具栏里,所以我

把的个随机函数要规定在横坐标9-39内生成。我采用的是每实现一个功能就进

行测试,分模块测试时没有出错,合起来就出错了,经过仔细检查后发现有些语

句写错了导致错误,经改正后正常运行。还有一些for 循环使用不当,造成程序

运行失败,经过多次测试后找到错误原因,改进后正常运行。

(2)、使用的测试数据

第一次开始等级为1时:

等级为二时:

复活之后,速度等级分数正常:

(3)、算法的效率分析和改进设想

首先,该算法的效率经过计算为o(n);不算是很复杂,需要改进的地方就

是在处理输出图像的时候,可以把循环更加简化。以及我们由于时间有限我们

还可以加入许多有趣的小东西。比如说,自主选择难度,保存上次的游戏界面,

等等其他的功能。另外,可以用文件储存游戏者之前的游戏记录,在每次开始

游戏时,游戏者可选择是继续上次游戏,或者是重新开始游戏,那么游戏记录

将会清空。还可以在一次游戏结束后询问游戏者是否重新开始,或者是复活。

复活时等级不变可是得分清零。画面的话可以增大游戏地图的面积,让游戏者

根据难度等级选择不同的地图。

(4)、经验和体会

在这个游戏的编写过程中,还是有一些不懂的地方,

但经过查阅书籍和网上学习,

都一点一点克服了,达到了学习的目的;对于每一个内容,可以试着将其进一步细分,分成多个函数来写,这样既有条理,也容易调试、修改;对于每个要求,可以一个一个来实现。如果中途需要中断程序编写时,可以做个标记,方便下次快速找到编写的位置。由于这次我设计的是游戏界面,所以先要大概打一下草稿,确定好游戏的界面要怎样布局,需要有哪些数据在里面。然后先把外层的“围栏”确定好,之后再去弄里面的东西,对于数据的位置,一定要有耐心,一个一个数清楚,不然很容易会出错。

四、源程序

Main . cpp

#include "Fsnake.h"

#include

#include

#include

#include

#include

typedef struct snake

{

int x;

int y;

struct snake *pre;

struct snake *next;

} NODESNAKE; //蛇链表

static NODESNAKE *head;

static NODESNAKE *tail;

static COORD position;

static COORD food_position;

static HANDLE hOut;

int score;

int sleep_time = 500;

int level;

int direct = RIGHT;

int main()

{

int i;

system("贪吃蛇---三贱客");

while(1){

head=NULL;

system("cls");

int level = 1; //难度

score=0;

HideCursor();

map();

start();

cre_snake(24, 12); //创建蛇头

cre_food(); //生成食物

gotoxy(3, 11, RED);

printf("%d", 1);

gotoxy(2, 15, RED);

printf("%3d", 0);

do

{

move();

extern int sleep_time;

Sleep(sleep_time);

} while (!is_dead());

gotoxy(22, 11, RED);

printf("Game over!");

gotoxy(19, 12, RED);

printf("按1重新开始,按其他退出程序\n");

gotoxy(24, 13, RED);

scanf("%d",&i);

if(i != 1){

break;

}

}

/*getch();*/

return 0;

}

void start()

{

int ch;

gotoxy(19, 11, L_GREEN);

printf("Enter键开始,Esc 退出!!!");

/*system("cls");*/

while ((ch = get_key()) != ENTER && ch != ESC);

if (ch == ESC)

exit(0);

else

{

gotoxy(19, 11, L_GREEN);

printf(""); //清空开始菜单文字

}

}

void gotoxy(int x, int y, int color)

{

position.X = 2*x;

position.Y = y;

//如果hOut 为空,则将标准输出句柄赋给它,否则保持原来的值

hOut == NULL ? (hOut = GetStdHandle(STD_OUTPUT_HANDLE)) : hOut;

SetConsoleCursorPosition(hOut, position); //将光标定位到指定坐标

SetConsoleTextAttribute(hOut, color); //设置字体颜色 }

void map()

{

int i; //行坐标

int j; //列坐标

//画出第一行

for (j = 0; j

gotoxy(j, 0, L_GREEN);

printf("■");

}

//画出中间部分

for (i = 1; i

gotoxy(0, i, L_GREEN);

printf("■");

gotoxy(8, i, L_GREEN);

printf("■");

gotoxy(WIDTH - 1, i, L_GREEN);

printf("■");

}

//画出底部

for (j = 0; j

gotoxy(j, HEIGHT - 1, L_GREEN);

printf("■");

}

gotoxy(3, 1, YELLOW);

printf("贪吃蛇");

gotoxy(1, 3, YELLOW);

printf("Snake---三贱客");

gotoxy(3, 5, YELLOW);

printf("操作方法");

gotoxy(2, 7, YELLOW);

printf("← ↑ ↓ →");

gotoxy(2, 8, YELLOW);

printf("左 上 下 右");

gotoxy(3, 10, YELLOW);

printf("等 级");

gotoxy(3, 13, YELLOW);

printf("得 分");

gotoxy(2, 17, YELLOW);

printf("提示:");

gotoxy(1, 19, YELLOW);

printf("空格键 暂停");

gotoxy(1, 21, YELLOW);

printf("任意键 继续");

}

//擦除某个坐标上的图像

void wipe(int x, int y)

{

gotoxy(x, y, YELLOW);

printf("");

}

int get_key()

{

char ch1;

ch1 = getch();

if (ch1 == ENTER || ch1 == ESC || ch1 == SPACE)

return ch1;

else if (ch1 == -32)

return getch();

else

return 0;

}

//生成食物

void cre_food()

{

int x, y;

NODESNAKE *p = head;

srand((unsigned int)time(NULL));

again:

do

{

x = 9 + rand() % (WIDTH - 10);

y = rand() %(HEIGHT - 1);

} while (x

//检查食物是否与蛇冲突

while (p != NULL){

//如果冲突,则重新生成

if (x == p->x&&y == p->y)

goto again;

p = p->next;

}

food_position.X = x;

food_position.Y = y;

gotoxy(x, y, RED);

printf("●");

}

//创建蛇的一节

void cre_snake(int x,int y)

{

int i;

NODESNAKE *pNew = NULL;

if ((pNew = (NODESNAKE *)malloc(sizeof(NODESNAKE))) == NULL) {

gotoxy(18, 11, RED);

printf("分配内存失败! 按任意键退出!");

getch();

exit(0);

}

//如果是创建蛇头

if (head == NULL)

{

head = pNew;

head->x = 24;

head->y = 12;

head->next = NULL;

head->pre = NULL;

tail = head;

}

else/*如果蛇头不为空,那么生成蛇的身体*/

{

pNew->x = x;

pNew->y = y;

head->pre = pNew;

pNew->next = head;

pNew->pre = NULL;

head = pNew;

}

}

//检测当前键盘的输入值

void checkinput(){

//检测当前键盘是否有输入

if (_kbhit())

{

int key = 0;

int temp = direct;

fflush(stdin); //清空输入缓冲

key = get_key();

if ((temp == RIGHT || temp == LEFT) && (key == UP || key == DOWN))

direct = key;

else if ((temp == UP || temp == DOWN) && (key == LEFT || key == RIGHT))

direct = key;

else if (key == SPACE)

getch();

}

//画出新蛇头

switch (direct)

{

case UP:cre_snake(head->x, head->y - 1); break;/*坐标的变化是,x 不变,y 减1。下面的同理*/

case DOWN:cre_snake(head->x, head->y + 1); break;

case LEFT:cre_snake(head->x - 1, head->y); break;

case RIGHT:cre_snake(head->x + 1, head->y); break;

}

}

//判断是否吃到食物

int is_eat()

{

if (head->x == food_position.X&&head->y == food_position.Y) return 1;

else

return 0;

}

//删去蛇尾

void deteSnaketail(){

wipe(tail->x, tail->y);/*首先擦掉蛇尾*/

NODESNAKE *p = tail;

tail = tail->pre;

tail->next = NULL;

free(p);

}

//判断蛇是否死亡

int is_dead()

{

NODESNAKE *p = head->next;

//如果咬到自己则死亡

while (p != NULL)

{

if (head->x == p->x&&head->y == p->y)

return 1;

p = p->next;

}

//如果越界则死亡

if (head->x == 8 || head->y == 0 || head->x == 39 || head->y == HEIGHT-1)

return 1;

return 0;

}

//记录分数、自动升级等级

void levelscore(){

//计分, 更改循环周期,更改等级

switch (score / 100)

{

case 0:score += 10;

sleep_time = 500;

level = 1;

break;

case 1:score += 15;

sleep_time = 350;

level = 2;

break;

case 2:

case 3:score += 25;

sleep_time = 200;

level = 3;

break;

case 4:

case 5:score += 35;

sleep_time = 150;

level = 4;

break;

case 6:

case 7:

case 8:

case 9:score += 50;

sleep_time = 100;

level = 5;

break;

default:score += 80;

sleep_time = 80;

level = 6;

}

gotoxy(3, 11, RED);

printf("%d", level);

gotoxy(3, 15, RED);

printf("%3d", score);

}

/*控制蛇的移动*/

void move()

{

checkinput();

//如果没有吃到食物,就擦去蛇尾

if (!is_eat())

{

deteSnaketail();

outputsnake();

}

//否则重新生成食物,记录分数,控制等级

else

{

outputsnake();

cre_food();

levelscore();

}

}

/*输出蛇的形态*/

void outputsnake(){

NODESNAKE *p = NULL;

for(p = head;p != NULL;p = p->next){

wipe(p->x,p->y);

}

for(p = head;p != NULL;p = p->next){

gotoxy(p->x,p->y, YELLOW);

printf("■");

}

}

//隐藏光标

void HideCursor()

{

CONSOLE_CURSOR_INFO cur_info = {1,0};

SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cur_info);

}

Fsnake.h

#define WIDTH 40

#define HEIGHT 24

#define LEFT 75

#define UP 72

#define DOWN 80

#define LEFT 75

#define RIGHT 77

#define ESC 27

#define SPACE 32

#define ENTER 13

#define YELLOW 0xe #define L_GREEN 0xB #define RED 0xC

#define TRUE 1

#define FALSE 0

void gotoxy(int x, int y, int color); void map();

void HideCursor();

void cre_food();

void cre_snake(int x, int y); int get_key();

void start();

void wipe(int x, int y); void move();

int is_eat();

int is_dead();

void outputsnake();

void levelscore();

void deteSnaketail(); void checkinput();

附件1:

华南农业大学信息学院

课程设计实验

实验题目:贪吃蛇

一、分析题目要求

经过我们3人小组的讨论,最终确定了用C 语言写贪吃蛇的游戏。

首先,要想实现贪吃蛇就要有游戏界面;其次,还要有写贪吃蛇的设计思

路以及对于数据结构的操作;另外,还应该有游戏的规则实现以及界面的美化

等。因此,我们把这个实验任务分成了3个部分,xxx 具体负责的是蛇的构

造以及食物的生成,xxx 负责界面,xxx 负责游戏规则。

要实现贪吃蛇能够前进,我们需要用到链表来存取蛇的身体,以及蛇所在

的方位。一般情况下,需要用到链表节点的删除,增加,输出等操作。另外,

产生随见事物使用系统自带的Rand ,随机定位一个游戏区域里面的方块,然后

使用gotoxy 函数可以定位到这个方块的坐标,并且涂上颜色,这样就产生了肉

眼可见的食物。关于蛇体,我们将蛇体的每个方块添加到链表里面,使用的是

snake,为什么不用数组,原因是数组的长度总是有限的,你不知道蛇体里面最

终存放多少方格,而且数组比较繁琐。通过不断的生成蛇头以及删除蛇尾循环

画出列表中的每个对象,就成功的画出了蛇体。设置蛇体的初始长度只有蛇头,

并设置蛇头的初始位置为居中。

(1) 各个函数命名,包含的参数,返回值类型

定义#include 这个头文件,它包含了其他Windows 头文件,这些

头文件的某些也包含了其他头文件:

static COORD position;//显示当前指针的坐标

static COORD food_position;//显示当前食物的坐标

static HANDLE hOut;//获取标准输出的句柄。命令行的程序会把字符输出到屏幕

定义#include 头文件,是Console Input/Output(控制台输入输出)

的简写,其中定义了通过控制台进行数据输入和数据输出的函数,主要是通过

按键盘产生的对应操作,比如getch()函数,而在Fsnake.h 中定义的

#define LEFT 75 #define UP 72 #define DOWN 80 #define RIGHT 77 #define ESC 27

#define SPACE 32 #define ENTER 13 即为getch()函数从键盘接收到的各种键值。

在Fsnake.h 中定义的:

#define GREEN 0xe #define L_GREEN 0xB #define RED 0xC 是用到

Windows.h 函数中的setcolor 函数:

以下SetConsoleTextAttribute(hOut, color); //设置字体颜色

各颜色的代码如下:

10=淡绿色 0xa

3=湖蓝色 11=淡浅绿色 0xb

4=红色 12=淡红色 0xc

5=紫色 13=淡紫色 0xd

6=黄色 14=淡黄色 0xe

7=白色 15=亮白色 0xf

在Fsnake.h 中定义的:

#define WIDTH 40

#define HEIGHT 24是地图的长和宽

生成地图的函数:

void start()//游戏地图中的开始界面

void gotoxy(int x, int y, int color)//控制命令行光标位置

int 类型的横纵坐标x ,y ,应经声明的color 。

void map()//生成地图

void HideCursor()//隐藏光标

此外,还有其他的操作函数:

void gotoxy(int x, int y, int color);

void map();

void HideCursor();

void cre_food();

void cre_snake(int x, int y);

int get_key();

void start();

void wipe(int x, int y);

void move();

int is_eat();

int is_dead();

void outputsnake();

void levelscore();

void deteSnaketail();

void checkinput();

(2)、输入输出的形式与达到的功能:

1、定义蛇的结构体为

typedef struct snake

{

int x;

int y;

struct snake *pre;

struct snake *next;

} NODESNAKE; //蛇链表

2、定义运行界面的边框函数为void map();该函数没有输入,输出的

是贪吃蛇的游戏界面范围。图示如下:

3、定义void start();作为游戏开始时候的提示,该函数没有参数,

但是需要输入数据类型的只有fsnake.h 中定义的静态数值”ESC ”和

“ENTER ”;它的输出是清除游戏界面内的文字;图示如下:

4、定义函数void cre_food( );为贪吃蛇创造食物;该函数没有参数,也没

有返回值;并且该函数不需要输入,它由自己用rand( )创造伪随机数用于程

序;该函数的输出即为随机产生的食物,图示如下:

5、定义函数int is_dead( );用于判断蛇是否死亡,死亡有两种形式:一是撞

到自己,二是撞到墙壁;is_dead()的返回值为0或1用于判断蛇的状态,他

没有参数,也不需要输入输出数据。

6、定义函数void move();用于控制贪吃蛇的移动,它所调用的函数有检

测输入函数void checkinput();检测是否吃到食物函数int is_eat();以及可能

会调用删除尾节点函数void deteSnaketail();创建食物函数void cre_food();和

输出分数等级函数void levelscore();并且该函数并不需要输入变量和输出某

一个值。

7、定义函数void outputsnake();用于输出蛇的状态,并且及时输出的前

进方向,该函数没有输入但是输出蛇的身体。

8、游戏一开始按enter 键进入游戏,按↑、↓、←、→控制蛇分别向上、

下、左、右不同方向运动,按空格键暂停,按任意键继续,按Esc 退出。如

果蛇死亡了,还可以按1继续,按任意键退出游戏。程序所达到的功能: 开

始,创建食物,创建蛇,判断蛇是否吃到食物,蛇是否死亡,控制蛇移动的

方向,等级,计分,速度,是否重新开始等功能。

9、我们选的是把地图范围和开始页面设置成绿色,左边工具栏与提示栏设

置为黄色,分数与等级为红色,食物也为红色,蛇为黄色正方形。地图设置

为长为40格,宽为24格,贪吃蛇每移动一次为一格,在横坐标0-8格之内

为工具栏,9-39格是贪吃蛇游戏的地图。

void start()//游戏地图中的开始界面

void gotoxy(int x, int y, int color)//控制命令行光标位置

输入横纵坐标x ,y 把该格定义为color 颜色

void map()//生成地图

生成贪吃蛇的游戏地图,与工具栏,开始页面

void HideCursor()//隐藏光标

隐藏输入的光标

二、解题思路

(1)、游戏界面的生成

1、游戏界面生成思路:

根据codeblocks 的输出框的大小,先找出游戏方框的大小,然后输出游

戏的边框以及文字说明。制作游戏的窗体布局。其中包括窗体的大小,位置,

添加按钮组件,在窗体上面划定游戏区域等。我们需要知道的只是边框的高

度以及宽度。

2、伪代码实现:

For()/*循环次数不超过边框宽度*/

绘出第一行墙壁;

For()/*循环次数不超过边框高度-1*/

绘出中间的竖直部分墙壁;

For()/*循环次数不超过边框宽度*/

绘出最后一行墙壁;

/*通过定为坐标来定为光标*/

写出各个需要提示的游戏说明;

(2)、创建食物和蛇的身体的实验思路:

由于食物的生成需要实现随机分布,所以要用到rand();这个函数来实现 产生随机数,只不过要注意的是我们需要判断食物的产生是否在游戏区域以

及是否和之前产生的蛇是否位置重叠。伪代码实现如下:

P=头指针;

While(p !=NULL){

If (随机数=p的数据域);

则 返回”p!=NULL”之前进行循环操作,;

P=p->next;

}

如果蛇头为空,那么将坐标定位到游戏界面正中央,并且将该处的坐标值

赋给头结点;如果蛇头不为空,那么将创建一个新的蛇头并且将现在的蛇头

下一个坐标赋值给新的蛇头。并且以此循环,直到蛇死亡、游戏结束。伪代

码实现如下:

P=游戏界面坐标;

head->pre=p;

P->next=head;

P->pre=NULL;

Head=p;

(3)、检测键盘是否输入实验思路:

首先要注意的是,我们在游戏时,并不是来自键盘上所有的输入都可以实现

蛇的移动,因此我们需要判断键盘上面的输入是否是我们想要的输入。至于关

于键盘的输入由队友来写,我想说的是,通过方向键设置蛇头的移动,蛇头每

移动一次位置就会变化到另外一个方块,对应的坐标x,y 都是有相应的变化,

例如按下UP 键,坐标的变化就是,x 不变,y 减1。

(4)、删去蛇尾思路:

在这里,只需要对蛇链表进行删除操作,不过要注意的是,在删除之前要

对蛇尾的光标进行擦除操作。最后要注意释放已删除的节点;

(5)、蛇在界面上的输出思路:

我的思路是,为了避免删除蛇尾的操作对整个蛇移动界面的影响,我觉得可

以对蛇链表进行两次遍历,一次是遍历擦除界面上所有的图案,第二次是直接

遍历输出整个蛇的形态。伪代码实现为:

While(p!=NULL){

擦除p 对应坐标的图像;

P=p->next;

}

While(p!=NULL){

定为p 点对应的坐标;

画出蛇的一节;

P=p->next;

}

(6)游戏等级、分数、速度的思路:游戏一开始吃一个食物得10分,当得分足够时升级,吃一个食物得分相应增加,速度也相应加快。用switch 选择相应功能。

(7)是否吃到食物的思路:通过判断蛇头的位置是否与食物位置重合来判断。有函数int is_eat()。

(8)判断蛇是否死亡的思路:蛇的死亡分两种:咬到自己与碰到边界;通过判断蛇头是否与边界或者自己的身体重合来判断是否死亡;有函数nt is_dead()。

三、调试分析

(1)、调试过程中遇到的问题:

首先遇到的问题是关于界面的和怎么样从键盘输入的一些问题,这些函数都

与include库函数有关,经过一番查找,我们找到了关于怎么样从

键盘直接输入数据的的方法,以及在界面上定位光标。因为队友主要负责这个

所以我不在赘述。其次,问体在于怎么样实现贪吃蛇的移动,由于最开始我们

只想到通过增加蛇头以及删除蛇尾的方法来移动,却没有准确判断吃到食物后

蛇的长度变化。

另外,在游戏结束后重新开始进行第二轮游戏时,出现了蛇的长度不变,分

数出现混乱的情况,经过修改发现是链表没有初始化,分数等级也没有初始化

的,所以才会出现乱码,以及图像的扭曲。

还有就是出现了下面这种情况:

经过讨论分析,我们发现是擦除尾部节点的时候覆盖了蛇本身的不需要擦除

的节点,然后我们把输出函数改成了先全部擦除所有的蛇的节点,然后在全部

显示,只不过这样又要多一遍循环。经过修改成功的消除了bug 。

我采用的是每实现一个功能就进行测试,分模块测试时没有出错,合起来就出错

了,经过仔细检查后发现有些语句写错了导致错误,经改正后正常运行。还有一

些for 循环使用不当,造成程序运行失败,经过多次测试后找到错误原因,改进

后正常运行。在调试过程中会发现等级与得分的数字不平衡时,需要数出他们的

横纵坐标,换为一样的,然后中间的开始界面也要确定好坐标值,将第一个字母

放在整个地图横坐标的中间。然后食物的随机生成会生成在了工具栏里,所以我

把的个随机函数要规定在横坐标9-39内生成。我采用的是每实现一个功能就进

行测试,分模块测试时没有出错,合起来就出错了,经过仔细检查后发现有些语

句写错了导致错误,经改正后正常运行。还有一些for 循环使用不当,造成程序

运行失败,经过多次测试后找到错误原因,改进后正常运行。

(2)、使用的测试数据

第一次开始等级为1时:

等级为二时:

复活之后,速度等级分数正常:

(3)、算法的效率分析和改进设想

首先,该算法的效率经过计算为o(n);不算是很复杂,需要改进的地方就

是在处理输出图像的时候,可以把循环更加简化。以及我们由于时间有限我们

还可以加入许多有趣的小东西。比如说,自主选择难度,保存上次的游戏界面,

等等其他的功能。另外,可以用文件储存游戏者之前的游戏记录,在每次开始

游戏时,游戏者可选择是继续上次游戏,或者是重新开始游戏,那么游戏记录

将会清空。还可以在一次游戏结束后询问游戏者是否重新开始,或者是复活。

复活时等级不变可是得分清零。画面的话可以增大游戏地图的面积,让游戏者

根据难度等级选择不同的地图。

(4)、经验和体会

在这个游戏的编写过程中,还是有一些不懂的地方,

但经过查阅书籍和网上学习,

都一点一点克服了,达到了学习的目的;对于每一个内容,可以试着将其进一步细分,分成多个函数来写,这样既有条理,也容易调试、修改;对于每个要求,可以一个一个来实现。如果中途需要中断程序编写时,可以做个标记,方便下次快速找到编写的位置。由于这次我设计的是游戏界面,所以先要大概打一下草稿,确定好游戏的界面要怎样布局,需要有哪些数据在里面。然后先把外层的“围栏”确定好,之后再去弄里面的东西,对于数据的位置,一定要有耐心,一个一个数清楚,不然很容易会出错。

四、源程序

Main . cpp

#include "Fsnake.h"

#include

#include

#include

#include

#include

typedef struct snake

{

int x;

int y;

struct snake *pre;

struct snake *next;

} NODESNAKE; //蛇链表

static NODESNAKE *head;

static NODESNAKE *tail;

static COORD position;

static COORD food_position;

static HANDLE hOut;

int score;

int sleep_time = 500;

int level;

int direct = RIGHT;

int main()

{

int i;

system("贪吃蛇---三贱客");

while(1){

head=NULL;

system("cls");

int level = 1; //难度

score=0;

HideCursor();

map();

start();

cre_snake(24, 12); //创建蛇头

cre_food(); //生成食物

gotoxy(3, 11, RED);

printf("%d", 1);

gotoxy(2, 15, RED);

printf("%3d", 0);

do

{

move();

extern int sleep_time;

Sleep(sleep_time);

} while (!is_dead());

gotoxy(22, 11, RED);

printf("Game over!");

gotoxy(19, 12, RED);

printf("按1重新开始,按其他退出程序\n");

gotoxy(24, 13, RED);

scanf("%d",&i);

if(i != 1){

break;

}

}

/*getch();*/

return 0;

}

void start()

{

int ch;

gotoxy(19, 11, L_GREEN);

printf("Enter键开始,Esc 退出!!!");

/*system("cls");*/

while ((ch = get_key()) != ENTER && ch != ESC);

if (ch == ESC)

exit(0);

else

{

gotoxy(19, 11, L_GREEN);

printf(""); //清空开始菜单文字

}

}

void gotoxy(int x, int y, int color)

{

position.X = 2*x;

position.Y = y;

//如果hOut 为空,则将标准输出句柄赋给它,否则保持原来的值

hOut == NULL ? (hOut = GetStdHandle(STD_OUTPUT_HANDLE)) : hOut;

SetConsoleCursorPosition(hOut, position); //将光标定位到指定坐标

SetConsoleTextAttribute(hOut, color); //设置字体颜色 }

void map()

{

int i; //行坐标

int j; //列坐标

//画出第一行

for (j = 0; j

gotoxy(j, 0, L_GREEN);

printf("■");

}

//画出中间部分

for (i = 1; i

gotoxy(0, i, L_GREEN);

printf("■");

gotoxy(8, i, L_GREEN);

printf("■");

gotoxy(WIDTH - 1, i, L_GREEN);

printf("■");

}

//画出底部

for (j = 0; j

gotoxy(j, HEIGHT - 1, L_GREEN);

printf("■");

}

gotoxy(3, 1, YELLOW);

printf("贪吃蛇");

gotoxy(1, 3, YELLOW);

printf("Snake---三贱客");

gotoxy(3, 5, YELLOW);

printf("操作方法");

gotoxy(2, 7, YELLOW);

printf("← ↑ ↓ →");

gotoxy(2, 8, YELLOW);

printf("左 上 下 右");

gotoxy(3, 10, YELLOW);

printf("等 级");

gotoxy(3, 13, YELLOW);

printf("得 分");

gotoxy(2, 17, YELLOW);

printf("提示:");

gotoxy(1, 19, YELLOW);

printf("空格键 暂停");

gotoxy(1, 21, YELLOW);

printf("任意键 继续");

}

//擦除某个坐标上的图像

void wipe(int x, int y)

{

gotoxy(x, y, YELLOW);

printf("");

}

int get_key()

{

char ch1;

ch1 = getch();

if (ch1 == ENTER || ch1 == ESC || ch1 == SPACE)

return ch1;

else if (ch1 == -32)

return getch();

else

return 0;

}

//生成食物

void cre_food()

{

int x, y;

NODESNAKE *p = head;

srand((unsigned int)time(NULL));

again:

do

{

x = 9 + rand() % (WIDTH - 10);

y = rand() %(HEIGHT - 1);

} while (x

//检查食物是否与蛇冲突

while (p != NULL){

//如果冲突,则重新生成

if (x == p->x&&y == p->y)

goto again;

p = p->next;

}

food_position.X = x;

food_position.Y = y;

gotoxy(x, y, RED);

printf("●");

}

//创建蛇的一节

void cre_snake(int x,int y)

{

int i;

NODESNAKE *pNew = NULL;

if ((pNew = (NODESNAKE *)malloc(sizeof(NODESNAKE))) == NULL) {

gotoxy(18, 11, RED);

printf("分配内存失败! 按任意键退出!");

getch();

exit(0);

}

//如果是创建蛇头

if (head == NULL)

{

head = pNew;

head->x = 24;

head->y = 12;

head->next = NULL;

head->pre = NULL;

tail = head;

}

else/*如果蛇头不为空,那么生成蛇的身体*/

{

pNew->x = x;

pNew->y = y;

head->pre = pNew;

pNew->next = head;

pNew->pre = NULL;

head = pNew;

}

}

//检测当前键盘的输入值

void checkinput(){

//检测当前键盘是否有输入

if (_kbhit())

{

int key = 0;

int temp = direct;

fflush(stdin); //清空输入缓冲

key = get_key();

if ((temp == RIGHT || temp == LEFT) && (key == UP || key == DOWN))

direct = key;

else if ((temp == UP || temp == DOWN) && (key == LEFT || key == RIGHT))

direct = key;

else if (key == SPACE)

getch();

}

//画出新蛇头

switch (direct)

{

case UP:cre_snake(head->x, head->y - 1); break;/*坐标的变化是,x 不变,y 减1。下面的同理*/

case DOWN:cre_snake(head->x, head->y + 1); break;

case LEFT:cre_snake(head->x - 1, head->y); break;

case RIGHT:cre_snake(head->x + 1, head->y); break;

}

}

//判断是否吃到食物

int is_eat()

{

if (head->x == food_position.X&&head->y == food_position.Y) return 1;

else

return 0;

}

//删去蛇尾

void deteSnaketail(){

wipe(tail->x, tail->y);/*首先擦掉蛇尾*/

NODESNAKE *p = tail;

tail = tail->pre;

tail->next = NULL;

free(p);

}

//判断蛇是否死亡

int is_dead()

{

NODESNAKE *p = head->next;

//如果咬到自己则死亡

while (p != NULL)

{

if (head->x == p->x&&head->y == p->y)

return 1;

p = p->next;

}

//如果越界则死亡

if (head->x == 8 || head->y == 0 || head->x == 39 || head->y == HEIGHT-1)

return 1;

return 0;

}

//记录分数、自动升级等级

void levelscore(){

//计分, 更改循环周期,更改等级

switch (score / 100)

{

case 0:score += 10;

sleep_time = 500;

level = 1;

break;

case 1:score += 15;

sleep_time = 350;

level = 2;

break;

case 2:

case 3:score += 25;

sleep_time = 200;

level = 3;

break;

case 4:

case 5:score += 35;

sleep_time = 150;

level = 4;

break;

case 6:

case 7:

case 8:

case 9:score += 50;

sleep_time = 100;

level = 5;

break;

default:score += 80;

sleep_time = 80;

level = 6;

}

gotoxy(3, 11, RED);

printf("%d", level);

gotoxy(3, 15, RED);

printf("%3d", score);

}

/*控制蛇的移动*/

void move()

{

checkinput();

//如果没有吃到食物,就擦去蛇尾

if (!is_eat())

{

deteSnaketail();

outputsnake();

}

//否则重新生成食物,记录分数,控制等级

else

{

outputsnake();

cre_food();

levelscore();

}

}

/*输出蛇的形态*/

void outputsnake(){

NODESNAKE *p = NULL;

for(p = head;p != NULL;p = p->next){

wipe(p->x,p->y);

}

for(p = head;p != NULL;p = p->next){

gotoxy(p->x,p->y, YELLOW);

printf("■");

}

}

//隐藏光标

void HideCursor()

{

CONSOLE_CURSOR_INFO cur_info = {1,0};

SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cur_info);

}

Fsnake.h

#define WIDTH 40

#define HEIGHT 24

#define LEFT 75

#define UP 72

#define DOWN 80

#define LEFT 75

#define RIGHT 77

#define ESC 27

#define SPACE 32

#define ENTER 13

#define YELLOW 0xe #define L_GREEN 0xB #define RED 0xC

#define TRUE 1

#define FALSE 0

void gotoxy(int x, int y, int color); void map();

void HideCursor();

void cre_food();

void cre_snake(int x, int y); int get_key();

void start();

void wipe(int x, int y); void move();

int is_eat();

int is_dead();

void outputsnake();

void levelscore();

void deteSnaketail(); void checkinput();


相关内容

  • [计算机组装与维护]实验指导书
  • 内江职业技术学院 <计算机组装与维护> 实验指导书 信息电子工程系 熊永胜 二○○九年二月 目 录 实验1 拆装主机箱 .................................................. 1 实验2 微机硬件市场调查 .................. ...

  • 实验室检测方法管理标准
  • QB 实验室检测方法管理标准 攀枝花钢铁有限责任公司 发布 QG/YX0137-2004 前 言 为规范冶金产品理化检测实验室取样.样品制备/加工.校准及检测方法的选择和确定.试验和确认.审批和应用,特制订本标准. 本标准由攀枝花钢铁有限责任公司提出. 本标准由攀枝花钢铁有限责任公司委托质量计量管理 ...

  • [企业经营决策电子沙盘模拟]实验报告撰写要求
  • <企业经营决策电子沙盘模拟>实验报告撰写要求 1. 要求写两份报告.一份报告为实验小组集体撰写的实验报告, 报告建议内容如下: ① 要求有完整的企业战略规划和虚拟企业中的各岗位的岗 位说明书 ② 要求详细说明每期的经营决策模拟过程,包括市场预测. 企业发展战略.供货与市场营销策略.采购与 ...

  • 合作学习结题报告
  • 一 课题实验与研究的提出 小学数学作为义务教育一门重要的基础性学科,除了应传授给学生一些初步的数学知识以外,还担负着发展学生思维能力,培养创新意识.实践能力和提高学习数学的兴趣,养成良好学习习惯的历史重任.传统教育模式的数学课堂教学中普遍存在忽视知识的发生过程.忽视情意教学目标.忽视学生主体地位的现 ...

  • 实验4 链路状态路由算法原理实验报告
  • 电子科技大学通信学院 <计算机通信网实验报告> 链路状态路由算法原理实验 班 级 学 生 学 号 教 师 实验4:链路状态路由算法原理实验报告 [实验目的] 1.要求实验者利用路由选择算法模拟软件提供的通信功能,模拟链路状态路由选择算法的初始化.路由信息扩散过程和路由计算方法: 2.掌握 ...

  • 保险业务模拟操作家庭财产保险实验报告
  • 文本目录: 1. 实验人 陈梦雨.黄发昕.罗海燕.毛思颖.张冠华 2. 实验时间 2015年5月13日 3. 试验地点 协和机房 4. 实验目的 掌握家庭财产险包含的要素,满足不同需求的客户对个人和家庭投保的保障. 熟悉家庭财产险投保流程,正确缮制各类单据,了解家庭财产险投保意义. 五.实验内容 在 ...

  • 初中学生成立体育课题研究小组的实验报告
  • 初中学生成立体育课题研究小组的实验报告 天津市宝坻区 金光 [内容摘要]素质教育要求我们的教学要以培养学生的创新精神和实践能力为重点,激发学生独立思考和创新的意识,培养学生的科学精神和创造思维习惯.遵循这一指导思想,我在体育课上成立了若干运动项目的课题研究小组.让学生自己选择练习项目,加入教师提出的 ...

  • 市场调查实验报告(三)
  • <市场调查> 学生姓名: 学生学号: 指导教师:李洪祥 专业年级: 实 验 报 告 (三) 2016-04-11 一.实验项目名称: <市场调研>实验项目 二.实验目的 通过实训,使学生了解市场调研的全过程,包括市场调查内容确定.市场调查方式和方法选择.问卷设计.数据收集.分 ...

  • 市场调查与预测实训课程教学大纲
  • <市场调查实训>课程教学大纲 一.课程基本信息 二.实验的性质.地位和任务 <市场调查实训>课程是市场营销专业集中实践性环节之一,是在<市场营销学>.<市场调查与预测>课程的基础一次全面的综合练习.市场调查是市场营销专业的学生在学校必须掌握的基本技能, ...

  • [蜡烛燃烧产物的实验探究]观课议课材料
  • <蜡烛燃烧产物的实验探究>观课议课材料 研究主题:初中化学课堂探究活动的有效性 合作体:全县初中化学教师 任教教师:旌德二中杜世华老师 授课内容:初中化学"蜡烛燃烧产物的实验探究" 合作程序: (1)课前会议(2012年11月9日第一节课) (2)课堂观察(2012年 ...