字符游戏——贪吃蛇

来源:互联网 发布:算法设计 克林伯格 编辑:程序博客网 时间:2024/05/21 11:11

还记得在我们小时候玩过的贪吃蛇吗?正在学C语言的我,现在虽然无法完全还原那些年玩过的贪吃蛇经典小游戏,但却可以利用字符设计出低配版贪吃蛇啦!在下用了很长时间,终于完成了自己制作的第一款C语言游戏,在这里和大家分享!

                             字符·贪吃蛇游戏设计及算法

在设计之前,我们首先要回忆贪吃蛇的基本玩法和细节:
1.蛇只能进行上下左右四个方向的移动
2.蛇可以吃到食物,吃到食物后会在蛇尾增加一节长度
3.蛇头碰到墙壁或者自己的身体时游戏结束
这便是最基本的贪吃蛇,此外还有很多拓展出的功能,这里不再一一赘述。
在此基础上,我制作了人工控制移动的贪吃蛇。
最开始,我们需要了解贪吃蛇的基本元素,如蛇头蛇尾,食物,边界等。

#define SNAKE_MAX_LENGTH 50#define SNAKE_HEAD 'H'#define SNAKE_BODY 'X'#define BLANK_CELL ' '#define SNAKE_FOOD '$'#define WALL_CELL '*'char map[12][13] = { "************","*HXXXX     *","*          *","*          *","*          *","*          *","*          *","*          *","*          *","*          *","*          *","************" };

人工蛇,顾名思义,就是自己控制蛇的走位,不断吃食物壮大自己。
因此,上述三个基本特征分别可以通过下面的方法实现:
1.输入一个字符,判断移动方向,以WASD为例:W向上,A向左,S向下,D向右
2.在地图上食物以“H坐标相等时蛇长+1
3.用IF条件判断蛇头H坐标与墙壁或者与任意一处蛇身X坐标是否相同,若是,游戏结束
总体框架用伪代码表示出来就是
print map[12][13]
while(not 游戏结束)
生成食物
输入字符c
SWIHCH(c)
CASE a:蛇头向左走 break;
CASE d:蛇头向右走 break;
CASE w:蛇头向上走 break;
CASE s:蛇头向下走 break;
END switch
END WHILE
print Game Over!!!
之后,利用函数不断的完善。

其中,关于移动的函数是最重要的,算法如下:
length为蛇长;
i为循环值,初始化为0;
snakex为蛇身的x坐标,snakey为蛇身的y坐标(snakex[0] snakey[0]分别为蛇头的x坐标,y坐标);

当输入字符时,判断蛇头走向,以决定snakex[0]和snakey[0]的值变化,然后:

while(i <=length-1)DO
{snakex[i]=snakex[i-1]
snakey[i]=snakey[i-1]
}
便完成了移动。

之后,还有蛇吃食物的蛇长变化,
为使其在蛇尾部分长度+1,设计如下算法:
length++;
lastx=snakex[length-1]
lasty=snakey[length-1]
MOVE;
IF(snake eat food)THEN
snakex[length-1]=lastx;
snakey[length-1]=lasty;
这样就完成了蛇长的变化。
在总框架的基础上,完成各部分的函数,这样,贪吃蛇之人工蛇版本便完成了:

#include <stdio.h>#include <stdlib.h>#include<time.h>#define SNAKE_MAX_LENGTH 50#define SNAKE_HEAD 'H'#define SNAKE_BODY 'X'#define BLANK_CELL ' '#define SNAKE_FOOD '$'#define WALL_CELL '*'char map[12][13] ={"************","*XXXXH     *","*          *","*          *","*          *","*          *","*          *","*          *","*          *","*          *","*          *","************" };//全局变量:初始状态表int food = 0;//定义开始时食物数量为0int x = 0, y = 0;//定义食物的坐标(x,y)int snakex[SNAKE_MAX_LENGTH] = { 5, 4, 3, 2, 1 };//蛇身的横坐标int snakey[SNAKE_MAX_LENGTH] = { 1, 1, 1, 1, 1 };//蛇身的纵坐标int lastX = 1, lastY = 1;//表示蛇尾部的坐标int length = 5;//定义初始长度,在该变量完成蛇长的变化void move(int x, int y);//蛇移动函数void output(void);//蛇移动效果打印函数void clear(void);//蛇移动前的蛇图像清空void printmap(void);//打印int gameover(void);//判定游戏结束(碰到身子或边界)void snakefood(void);//用来生成食物void eatfood(void);//蛇吃食物后身体增长int main() {    int i;    printmap();    //打印初始状态图    char c;    int a = 0, b = 0;    while ((scanf_s("%c", &c)) != EOF)//输入字母W,A,S,D    {        switch (c){//进行移动        case 'a'://左移时            a = 1;            b = 0;            snakefood();            move(a, b);            if (gameover())//判定游戏是否结束            {                printf("\nGAME OVER!!!\n");                break;            }            else                printmap();            break;        case 'd'://右移            b = 1;            a = 0;            snakefood();            move(a, b);            if (gameover())//判定游戏是否结束            {                printf("\nGAME OVER!!!\n");                break;            }            else                printmap();            break;        case 's'://上移            a = -1;            b = 0;            snakefood();            move(a, b);            if (gameover())//判定游戏是否结束            {                printf("\nGAME OVER!!!\n");                break;            }            else                printmap();            break;        case 'w'://下移            b = -1;            a = 0;            snakefood();            move(a, b);            if (gameover())//判定游戏是否结束            {                printf("\nGAME OVER!!!\n");                break;            }            else                printmap();            break;        }    }//判定贪吃蛇的移动(通过move函数实现)并通过output函数实现map上的变化    return 0;}void move(int a, int b){    int i;    if (a == 1 && b == 0)    {        clear();//清空map        lastX = snakex[length - 1];        lastY = snakey[length - 1];//记录当前蛇尾坐标        for (i = length - 1; i >= 1; i--)        {            snakex[i] = snakex[i - 1];            snakey[i] = snakey[i - 1];//移动        }        snakex[0]--;//蛇头移动        eatfood();//判断是否吃了食物        output();//移动完成    }    if (a == 0 && b == 1)    {        clear();        lastX = snakex[length - 1];        lastY = snakey[length - 1];        for (i = length - 1; i >= 1; i--)        {            snakex[i] = snakex[i - 1];            snakey[i] = snakey[i - 1];        }        snakex[0]++;        eatfood();        output();    }    if (a == -1 && b == 0)    {        clear();        lastX = snakex[length - 1];        lastY = snakey[length - 1];        for (i = length - 1; i >= 1; i--)        {            snakex[i] = snakex[i - 1];            snakey[i] = snakey[i - 1];        }        snakey[0]++;        eatfood();        output();    }    if (a == 0 && b == -1)    {        clear();        lastX = snakex[length - 1];        lastY = snakey[length - 1];        for (i = length - 1; i >= 1; i--)        {            snakex[i] = snakex[i - 1];            snakey[i] = snakey[i - 1];        }        snakey[0]--;        eatfood();        output();    }}void clear(void){    int i;    for (i = 0; i < length; i++)        map[snakey[i]][snakex[i]] = BLANK_CELL;//将蛇原来位置清空}void output(void){    int i;    map[snakey[0]][snakex[0]] = SNAKE_HEAD;    for (i = 1; i < length; i++)        map[snakey[i]][snakex[i]] = SNAKE_BODY;//蛇移动后的位置}void printmap(void){    int i;    for (i = 0; i < 12; i++)    {        printf("%s\n", map[i]);    }    //蛇可以完成移动,并打印}int gameover(void){    int i;    int fail = 0;    for (i = 1; i<length; i++)//用来判断是否头碰身子    {        if (snakex[0] == snakex[i] && snakey[0] == snakey[i])            fail = 1;    }    if (snakex[0]>10 || snakey[0] > 10 || snakex[0] < 1 || snakey[0] < 1 || fail == 1)//前四个条件为是否触碰边界,后一个条件判断是否头碰身子        return 1;    else        return 0;}void snakefood(void){    srand(time(NULL));    if (food == 0)    {           x = rand() % 10 + 1;        y = rand() % 10 + 1;        if (map[x][y] == ' ')//确保在空白位置出现食物        {            map[x][y] = SNAKE_FOOD;//在地图可到达位置上随机生成食物            food++;//确保每次只出现一个食物        }    }}void eatfood(void){    if (snakey[0] == x&&snakex[0] == y)//判断蛇是否吃到食物    {        length++;        food = 0;//食物已经被吃掉,清空        snakex[length - 1] = lastX;        snakey[length - 1] = lastY;//蛇长增加一个    }}

游戏成果如下:这里写图片描述

在后期,可以通过引用windows函数库来优化界面,例如每次循环后清空整个界面,使其更加直观等等,大家可以尝试。

我们可以看到,先写出总控代码是多么重要,它的清晰程度决定了你设计算法以及对应函数需要的时间和思考量。