字符游戏贪吃蛇设计

前言

贪吃蛇,一款极其经典的游戏。也是众多程序员入门的小程序。
但是贪吃蛇中却蕴含着大学问。如何让蛇自己动起来,能自己吃食物,并且还不能死亡以使蛇长度尽可能地长。这是一个很普通也很深奥的问题。网上有很多大牛设计出了自己的算法,如下图:
这里写图片描述
总结起来,主要有以下几种:

  • BFS(广度优先搜索)算法
  • A STAR算法
  • 哈密顿回路
算法设计

由于本人能力有限,只能通过曼哈顿距离,最简单的贪婪算法来进行设计。其主要思想是:

  • 计算四个不同方向(上下左右)的曼哈顿,选择距离最短的方向
  • 判断选择该方向会不会死亡,否则选择距离第二短的方向,以此类推。

虽然这个算法很不理智,蛇很容易死掉,但是这个算法应该是最容易理解的了。代码实现如下:

char whereGoNext() {
    char movable[4]= {'D','A','S','W'};
    int min=9999,minDir;
    for(int i=0; i<4; i++) {
        snakeX[0]+=direx[i];
        snakeY[0]+=direy[i];

        if(!gameover()) {
            int cost=fabs(snakeX[0]-foody)+fabs(snakeY[0]-foodx);
            if(min>cost) {
                min=cost;
                minDir=i;
            }
        }

        snakeX[0]-=direx[i];
        snakeY[0]-=direy[i];
    }
    if(min<9999)
        return movable[minDir];
    else return 0;
}

最主要的部分说完了,还剩下一些细节怎么处理呢?比如如何实现蛇的移动,如何投放食物…..

  • 蛇移动的实现:

    • 用两个数组存放蛇的位置坐标:snakeX[] 和 snakeY[],每次移动一个单位,改变数组的值。注意要分吃到食物和没吃到食物两种情况来讨论。
  • 如何投放食物

    • 用一二维数组存放可以投放食物的位置的坐标。
    • 遍历整个地图,将空位置记录在这个二维数组上
    • 用随机数决定选择哪个坐标投放食物。
  • 如何打印蛇

    • 为了避免调用system(“cls”)产生闪烁效果而导致用户不友好,在此我们选择用VT100控制码 来设置光标的位置,从而避免了每次都刷新屏幕。

全部的代码看下方,实验环境是Red Hat Enterprise Linux 6,makefile 文件我也放在了代码后面,实测编译通过(就是死得比较快)….

/**
* mysnake.c
* version 1.0
* Red Hat Enterprise Linux 6
* Jzin Ou 2017/12/18
*/

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<unistd.h>

#define SNAKE_MAX_LENGTH 150
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'
#define MAP_LENGTH 12
#define DELAY_TIME  100000  //delay 100ms

/**
* move snake
* @param dir int movement direction
*/
void snakeMove(int dir);

/**
* output cells of the grid
*/
void show(void);

/**
* output the snake
*/
void output(void);

/**
* put a food randomized on a blank cell
* @return int 1 for put successfully, 0 for fail
*/
int putFood(void);

/**
* judge whether gameover
* @return int 0 for not over,1 for game over
*/
int gameover(void);

/**
* decide the smart snake's direction
* @return snake's movement direction
*/
char whereGoNext(void);

char map[MAP_LENGTH][MAP_LENGTH+1]= {//initial status
    "************",
    "*XXXXH     *",
    "*      *   *",
    "*      *   *",
    "*     **   *",
    "*      *   *",
    "*          *",
    "*          *",
    "*          *",
    "*          *",
    "*          *",
    "************"
};
const int direx[4]= {0,0,1,-1};     //four movement direction: 
const int direy[4]= {1,-1,0,0};     //(0,1)turn down (0,-1)turn up (1,0)turn right (-1,0)turn left
int snakeX[SNAKE_MAX_LENGTH]= {5,4,3,2,1};  //snake's coordinate
int snakeY[SNAKE_MAX_LENGTH]= {1,1,1,1,1};  //(snakeX[0],snakeY[0]) means head   (snakeX[length-1],snakeY[length-1]) means tail 
int snakeLength=5;
int foodx,foody,eatFood=1;

int main(void) {
    char s;
    srand(time(NULL));
    printf("\033[2J");//clear the screen
    printf("\033[1;1H");
    show();
    do {
        s=whereGoNext();
        if(s==0)break;
        switch(s) {
            case 'D':
                snakeMove(0);
                break;
            case 'A':
                snakeMove(1);
                break;
            case 'S':
                snakeMove(2);
                break;
            case 'W':
                snakeMove(3);
                break;
        }
        output();
        usleep(DELAY_TIME);
    } while(!gameover());
    printf("\033[13;1HGame over!!!\n");
    return 0;
}

void snakeMove(int dir) {
    //first clear the tail
    printf("\033[%d;%dH%c\n",snakeY[snakeLength-1]+1,snakeX[snakeLength-1]+1,BLANK_CELL);
    map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=BLANK_CELL;

    if(snakeX[0]+direx[dir]==foody&&snakeY[0]+direy[dir]==foodx) {//eat food
        eatFood=1;
        snakeLength++;//increase length
    }

    for(int i=snakeLength-1; i>0; i--) {//body move
        snakeX[i]=snakeX[i-1];
        snakeY[i]=snakeY[i-1];
    }

    snakeX[0]+=direx[dir];//head move
    snakeY[0]+=direy[dir];
}

void show() {
    for(int i=0; i<MAP_LENGTH; i++) {//output map
        for(int j=0; j<MAP_LENGTH; j++) {
            printf("%c",map[i][j]);
        }
        printf("\n");
    }
}

void output(void) {
    printf("\033[%d;%dH%c\n",snakeY[0]+1,snakeX[0]+1,SNAKE_HEAD);/print snake/
    printf("\033[%d;%dH%c\n",snakeY[1]+1,snakeX[1]+1,SNAKE_BODY);
    printf("\033[%d;%dH%c\n",snakeY[snakeLength-1]+1,snakeX[snakeLength-1]+1,SNAKE_BODY);
    //must empty buffers: printf+'\n' or fflush(stdout);

    map[snakeY[0]][snakeX[0]]=SNAKE_HEAD;
    map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_BODY;

    if(eatFood) {
        eatFood=0;
        if(putFood()) { //put food
            printf("\033[%d;%dH%c\n",foodx+1,foody+1,SNAKE_FOOD);
        }
    } else {
        printf("\033[%d;%dH%c\n",foodx+1,foody+1,SNAKE_FOOD);
    }
    printf("\033[13;1H\n");
}

int gameover(void) {
    if(snakeLength>=(MAP_LENGTH-2)*(MAP_LENGTH-2))return 1;//the map is full

    if(map[snakeY[0]][snakeX[0]]==WALL_CELL)return 1;//not touch wall

    for(int j=1; j<snakeLength; j++) {//doesn't eat itself
        if(snakeX[0]==snakeX[j]&&snakeY[0]==snakeY[j])return 1;
    }
    return 0;
}

int putFood(void) {
    int foodMap[MAP_LENGTH*MAP_LENGTH][2],len,ran;  
    len=0;
    for(int i=1; i<MAP_LENGTH-1; i++) {//put empty place into foodMap
        for(int j=1; j<MAP_LENGTH-1; j++) {
            if(map[i][j]==BLANK_CELL) {
                foodMap[len][0]=i;
                foodMap[len][1]=j;
                len++;
            }
        }
    }

    if(len>1)ran=rand()%len;//place food randomly
    else if(len==0)return 0;
    else ran=0;

    foodx=foodMap[ran][0];
    foody=foodMap[ran][1];
    return 1;
}

char whereGoNext() {
    char movable[4]= {'D','A','S','W'};
    int min=9999,minDir;
    for(int i=0; i<4; i++) {
        snakeX[0]+=direx[i];
        snakeY[0]+=direy[i];

        if(!gameover()) {
            int cost=fabs(snakeX[0]-foody)+fabs(snakeY[0]-foodx);
            if(min>cost) {
                min=cost;
                minDir=i;
            }
        }

        snakeX[0]-=direx[i];
        snakeY[0]-=direy[i];
    }
    if(min<9999)
        return movable[minDir];
    else return 0;
}
#makefile文件
.SUFFIXES: .c .o

CC=gcc

SRCS1=mysnake.c
OBJS1=$(SRCS1:.c=.o)
EXEC1=mysnake

all: $(OBJS1)
    $(CC) -std=c99 -o $(EXEC1) $(OBJS1) 

    @echo '-------------ok--------------'

.c.o: 
    $(CC) -std=c99 -Wall -g -o $@ -c $<

clean:
    rm -f $(OBJS1) $(EXEC1)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值