字符游戏——贪吃蛇

还记得在我们小时候玩过的贪吃蛇吗?正在学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;//定义开始时食物数量为0
int 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函数库来优化界面,例如每次循环后清空整个界面,使其更加直观等等,大家可以尝试。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值