小小贪吃蛇的进化之路

  《贪吃蛇》毫无疑问是一款非常经典的游戏(虽然现在的贪吃蛇完全变了模样,游戏规则十分简单,但现在我尝试用C语言做出字符贪吃蛇游戏,才发现非常困难。以下便是我用C语言做贪吃蛇并不断将其改进的过程。


贪吃蛇1.0

  这便是我第一次写出的完整的贪吃蛇程序了,还没有开始完善,自然有很多不足,例如程序需要有输入才能继续运行(阻塞输入),输入一次运行一次。但至少我还是做出来了,心里还是很激动的(毕竟写代码五分钟,debug两小时),检查错误不知道花了多长时间。

#include 
#include 
#include 

char map[12][13] = {
  "************",
  "*          *",
  "*          *",
  "*          *",
  "*          *",
  "*          *",
  "*          *",
  "*          *",
  "*          *",
  "*          *",
  "*          *",
  "************",
};

char blank = ' ';
char head = 'H';
char body = 'X';
char wall = '*';
char food = '$';
char M,N;

int headX = 1, headY = 1;
int headcopyX, headcopyY;
int bodyX[100] = {0}, bodyY[100] = {0};
int bodyLen = 0;
int foodX = 0, foodY = 0;
int willBeLonger = 0;

void printMap() {  //打印地图
  system("cls");
  for (int i = 0; i < 12; ++i) {
    printf("%s\n", map[i]);
  }
}

void spawnFood() {  //随机生成食物
  // Random food position
  foodX = rand() % 10 + 1;
  foodY = rand() % 10 + 1;
  while (map[foodX][foodY] != blank) {
    foodX = rand() % 10 + 1;
    foodY = rand() % 10 + 1;
  }
  map[foodX][foodY] = food;
}

void isGameOver(void) {
        printf("\nGAME OVER!\n");
}

void HeadMove (char control) {  //头部移动
    headcopyX = headX;
    headcopyY = headY;
    switch (control) {
      case 'w':
        headX--;
        N = 1;
        break;
      case 's':
        headX++;
        N = 1;
        break;
      case 'a':
        headY--;
        N = 1;
        break;
      case 'd':
        headY++;
        N = 1;
        break;
      default:
        N = 0;
    }
    map[headcopyX][headcopyY] = blank;
    M = map[headX][headY];
    map[headX][headY] = head;
}

void beLonger () {  //身体变长
    bodyLen++;
    bodyX[bodyLen - 1] = headcopyX;
    bodyY[bodyLen - 1] = headcopyY;
    map[bodyX[bodyLen - 1]][bodyY[bodyLen - 1]] = body;
}

void bodymove (void) {  //身体移动
    if (bodyLen > 0) {
        map[bodyX[0]][bodyY[0]] = blank;
        int i;
        for(i = 0; i < bodyLen - 1; i++) {
            bodyX[i] = bodyX[i + 1];
            bodyY[i] = bodyY[i + 1];
            map[bodyX[i]][bodyY[i]] = body;
        }
        bodyX[i] = headcopyX;
        bodyY[i] = headcopyY;
        map[bodyX[i]][bodyY[i]] = body;
    }
}

int main() {
    char control;
    srand(time(NULL));
    map[headX][headY] = head;
    spawnFood();
    printMap();
    while(1) {
        scanf("%c",&control);
        HeadMove(control);  //首先头部移动
        if(N == 0) continue;  //如果输入的字符不是'W','A','S','D'则跳过
        if (M == food) {  //如果吃到食物则身体加长,否则身体一动
            beLonger();
            spawnFood();
        } else {
            bodymove();
        }

    printMap();
    if ( M == wall || M == body) {  //判断头部是否撞墙或撞到身体
        isGameOver();
        break;
    }
    }
}

贪吃蛇2.0

  随后我将程序进行了一下改进,上网百度了很久终于得到了Windows下命令行非阻塞输入的办法,这下就不用按一步敲一次回车键了,没有输入的时候蛇依然会按照原来的方向移动。但新的问题也产生了:依靠系统命令“cls”完成的地图刷新会造成严重的闪屏,于是我又开始了百度之旅。。。。

while(1) {
        Sleep(100);
        if(!_kbhit()) {
            control = control;
            mainFuction(control);
        } else {
            control = _getch();
            mainFuction(control);
        }
        if ( M == wall || M == body) {  //判断头部是否撞墙或撞到身体
            isGameOver();
            break;
        }
    }

  我把主要的移动和吃食物的操作都移入到mainFuction函数里,利用_kbhit()_getch()函数实现非阻塞输入。


贪吃蛇2.1

  这次完善更多了一点,刷新方式改变了,刷新速度得到了控制,因行进方向改变造成的延迟也消除了。总之闪屏是不会了,虽然有光标不断在闪但也无伤大雅。然而下面对于智能蛇的尝试更是令人抓狂。

void printMap() {  
  system("cls"); //清屏
  for (int i = 0; i < 12; ++i) {
    printf("%s\n", map[i]);
  }
}

  之前的刷新是前者,而后者则解决了闪屏的问题

void printMap() {  
  hStdout = GetStdHandle(STD_OUTPUT_HANDLE); //读取当前光标位置
  cursorPos.X = 0;
  for (int i = 0; i < 12; ++i) {
    cursorPos.Y = i;
    SetConsoleCursorPosition(hStdout, cursorPos); //移动光标
    printf("%s\n", map[i]);
  }
}

贪吃蛇3.0(未完善)

  为什么说未完善呢,是因为大致思路、规划都已经完成了,就卡在写代码这一步。如果按照下面的伪代码写出智能蛇,一开始倒是没什么毛病,但仔细观察就会发现,这条蛇毫不注意就会钻进死胡同,撞到自己身体,游戏结束。

// Hx,Hy: 头的位置
    // Fx,Fy:食物的位置
    function whereGoNext(Hx,Hy,Fx,Fy) {
    // 用数组movable[3]={“a”,”d”,”w”,”s”} 记录可走的方向
    // 用数组distance[3]={0,0,0,0} 记录离食物的距离
    // 分别计算蛇头周边四个位置到食物的距离。H头的位置,F食物位置
    //     例如:假设输入”a” 则distance[0] = |Fx – (Hx-1)| + |Fy – Hy|
    //           如果 Hx-1,Hy 位置不是Blank,则 distance[0] = 9999
    // 选择distance中存最小距离的下标p,注意最小距离不能是9999
    // 返回 movable[p]
    }

  所以又经过漫长的查阅之后,我发现把地图按照哈密顿回路的思路,竟然能100%成功,当然这里的时间并不是最短的,确实最稳的。然而我只做出了第一步。。。

char autoway(void) {
    char control;
    if(headY % 2 == 0) {
        if(headX == 2) {
            control = 'd';
            if(headY == 10) control = 'w';
        } else control = 'w';
    } else {
        if(headX == 10) {
            control = 'd';
        } else control = 's';
    }
    if(headX == 1) {
        if(headY == 1) control = 's';
        else control = 'a';
    }
    return control;
}

  第一步也只是规划了最大的回路,保证包括到所有的点。智能蛇的路还很长。。。希望我能做出下图的贪吃蛇。

小小贪吃蛇的进化之路_第1张图片

你可能感兴趣的:(游戏代码)