CuteHamster 发表于 2019-11-13 00:45

改进网上的一个用C语言写的贪吃蛇程序代码

本帖最后由 CuteHamster 于 2019-11-13 17:39 编辑

我几天前在C Primer Pluse这本书上学完了结构体内容,感觉应该能写一些控制台小程序了,
正好今天有时间,于是在网上找了一个贪吃蛇小游戏的C语言源代码来学习借鉴(附上网上的源代码)

#include<stdio.h>

#include<string.h>

#include<windows.h>

#include<time.h>

#include<conio.h>





#define up 'w'

#define down 's'

#define left 'a'

#define right 'd'

#define stop 'p'



void welcome();               //开始界面

void Finish();                //结束界面

void creatgraph();            //围墙打印

void gotoxy(int x, int y);    //光标跳转,横为X 0,1,2..

void gotoprint(int x, int y); //跳转打印

void gotodelete(int x, int y);//跳转删除

void creatfood();             //食物产生

int ClickControl();         //获取键盘信号

int Judge();                  //游戏结束判断

void MovingBody();      //蛇的移动

void Eating();                //蛇吃到东西后的操作(伸长)

void ChangeBody(int a, int b); //蛇的坐标变换,后一个复制前一个STRUCT,a,b为head之前坐标



/*全局变量 + 预处理:*/

typedef struct Snakes

{

      int x;

      int y;

      struct Snakes* next;

}snake;



snake* head, * tail;



struct Food

{

      int x;

      int y;

}food;

char name;

int score = 0;

char click = 1;

int speed;



/************************************************************/



int main()

{

      system("color F0");

      welcome();

      creatgraph();

      creatfood();

      if (ClickControl() == 0) return 0;

      return 0;

}



/**********************************************************/

void welcome()

{

      gotoxy(15, 10);

      printf("/**********************************************/");

      gotoxy(15, 20);

      printf("/**********************************************/");

      gotoxy(20, 13);

      printf("WELCOME TO THE GAME OF RETRO SNAKE");

      gotoxy(14, 16);

      printf("请在英文输入法中操作,反向移动等同于吃到自己,wasd控制p暂停");

      gotoxy(20, 18);

      printf("PLEASE ENTER YOUR NAME:");

      scanf_s("%s", &name, 10);

      system("cls");

}

/**********************************************************/

void creatgraph()

{

      int i;

      for (i = 0; i < 58; i += 2)//打印上下边框

      {

                gotoprint(i, 0);

                gotoprint(i, 26);

      }

      for (i = 1; i < 26; i++)

      {

                gotoprint(0, i);

                gotoprint(56, i);

      }

      gotoxy(63, 10);

      printf("hello %s,Welcome To Play", name);

      gotoxy(63, 15);

      printf("Your Score Is:%d    = ̄ω ̄= ", score);

      gotoxy(63, 20);

      printf("This Game Is Created By JOKER");

      head = (snake*)malloc(sizeof(snake));

      head->x = 16;

      head->y = 15;

      //gotoprint(head->x, head->y);

      tail = (snake*)malloc(sizeof(snake));

      snake* p = (snake*)malloc(sizeof(snake));

      snake* q = (snake*)malloc(sizeof(snake));

      p->x = 16;

      p->y = 16;

      q->x = 16;

      q->y = 17;

      head->next = p;

      p->next = q;

      q->next = tail;

      //gotoprint(p->x, p->y);

      //gotoprint(q->x, q->y);

      tail->next = NULL;

      tail->x = 4;

      tail->y = 2;



}

/**********************************************************/

void gotoxy(int x, int y)

{

      COORD pos;

      HANDLE hOutput;

      pos.X = x;

      pos.Y = y;

      hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

      SetConsoleCursorPosition(hOutput, pos);

}

/**********************************************************/

void gotoprint(int x, int y)

{

      gotoxy(x, y);

      printf("■");

}

/**********************************************************/

void gotodelete(int x, int y)

{

      gotoxy(x, y);

      printf("");

}

/**********************************************************/

void creatfood()

{

      srand((int)time(NULL));

lable:

      food.y = rand() % (25 - 1 + 1) + 1;

      food.x = rand() % (54 - 2 + 1) + 2;

      if (food.x % 2 != 0)

      {

                food.x = food.x + 1;

      }

      snake* judge = head;

      while (1)//遍历排除蛇身重复

      {

                if (judge->next == NULL) break;

                if (food.x == judge->x && food.y == judge->y)

                {

                        goto lable;

                }

                judge = judge->next;

      }

      gotoxy(food.x, food.y);

      printf("◎");

}

/**********************************************************/

int ClickControl()

{

      char c;

      while (1)

      {

                if (Judge() == 0) return 0;

                if (_kbhit())

                {

                        click = _getch();

                }

                MovingBody();

                Eating();

      }

      return 1;

}

/**********************************************************/

void MovingBody()

{

      int count = 0;

      int a = head->x, b = head->y;

      snake* p = head;

      //通过先清空后打印实现动画效果

      while (1)

      {

                if (p->next == NULL) break;

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

                count++;

                p = p->next;

      }

      switch (click)

      {

      case up:

                head->y -= 1;

                ChangeBody(a, b);

                break;

      case down:

                head->y += 1;

                ChangeBody(a, b);

                break;

      case left:

                head->x -= 2;

                ChangeBody(a, b);

                break;

      case right:

                head->x += 2;

                ChangeBody(a, b);

                break;

      case stop:

                click =_getch();

                break;

      }

      p = head;

      while (1)

      {

                if (p->next == NULL) break;

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

                p = p->next;

      }

      p = head;

      gotoxy(0, 28);

      if (count <= 10) speed = 150;

      else if (count > 10 && count <= 20) speed = 100;

      else if (count > 20 && count <= 40) speed = 50;

      else speed = 10;

      Sleep(speed);

}

/**********************************************************/

void Eating()

{

      if (head->x == food.x && head->y == food.y)

      {

                creatfood();

                snake* _new = (snake*)malloc(sizeof(snake));

                snake* p;

                p = head;

                while (1)

                {

                        if (p->next->next == NULL) break;

                        p = p->next;

                }

                p->next = _new;

                _new->next = tail;

                score += 10;

                gotoxy(77, 15);

                printf("%d", score);

      }

}

/**********************************************************/

void ChangeBody(int a, int b)

{

      snake* p = head->next;

      int mid1, mid2, _mid1, _mid2;

      mid1 = p->x;

      mid2 = p->y;

      while (1)

      {

                if (p->next->next == NULL) break;

                _mid1 = p->next->x;

                _mid2 = p->next->y;

                p->next->x = mid1;

                p->next->y = mid2;

                mid1 = _mid1;

                mid2 = _mid2;

                p = p->next;

      }

      p = head->next;

      //if (p->next!= NULL)

      {

                p->x = a;

                p->y = b;

      }

}

/**********************************************************/

int Judge()

{

      if (head->x == 0 || head->x == 56 || head->y == 0 || head->y == 26)

      {

                Finish();

                return 0;

      }

      snake* p = head->next;

      while (1)

      {
               
                if (p->next == NULL) break;

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

                {

                        Finish();

                        return 0;

                }

                p = p->next;

      }

      return 1;

}

/**********************************************************/

void Finish()

{

      system("cls");

      gotoxy(15, 10);

      printf("/**********************************************/");

      gotoxy(15, 20);

      printf("/**********************************************/");

      gotoxy(18, 14);

      printf("GAME   OVER      o(* ̄▽ ̄*)o");

      gotoxy(20, 16);

      printf("Your Score is %d    hiahiahia", score);

      gotoxy(18, 18);

      printf("还不错哦,   继续努力O(∩_∩)O");

      gotoxy(0, 27);

      system("pause");

}


仔细阅读之后我发现这个程序还有可以改进的地方:
1.此程序蛇头向后运动会被判定撞上身体,而正常的贪吃蛇是不应该响应向后运动的按键的
2.此程序的食物产生机制,产生一次食物可能要调用多次随机数生成函数,蛇的身体长了运算量可能会比较大,应该一次随机就生成一份食物
3.此程序在每次蛇移动时都会把蛇的身体全部刷新一遍,而事实上只需要改变蛇尾的位置就可以实现蛇的移动了

于是在借鉴原程序的基础上我又自己从头开始重新编了一个C程序,实现了上面的改进:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <windows.h>

enum
{
      WIDTH = 40,//墙的宽度
      HEIGHT = 30,//墙的高度
};

//蛇和食物的结构体定义
struct Snake
{
      int x;
      int y;
      Snake* next;
      Snake* former;
};

//全局变量
Snake* head;
Snake* tail;
Snake* Food;
char direction = 's';//键盘录入蛇的运动方向
int score = 0;//得分
int PointArray = { 0 };//用于生成食物及判定撞击

/*******************************************************************/
void movcoord(int x, int y);//用于操作光标位置
void printstraction(int x, int y);//用于绘制蛇身基本单元
void printbrick(int x, int y);//用于绘制墙的基本单元
void drawwalls();//绘制墙体
void setArray();//用于初始化数组
void changeArray(Snake* p, int value);//用于改变数组里数字的值
void ChangeScore(int score);//用于更新得分
void iniSnake();//用于初始化蛇的图像
void add(Snake* body);//增加蛇的身体长度
void del(Snake* body);//删除蛇的身体单元
void getfoodposition();//生成食物位置
void setfood();//绘制食物
void creatfood();//产生食物
int move();//蛇的前进与进食
                   //把蛇尾移至新蛇头的位置,然后让旧的蛇尾变成新的蛇头
         //在蛇尾添加身体单元
/*******************************************************************/

//用于操作光标位置
void movcoord(int x, int y)
{
      COORD coord;
      HANDLE ophandle;
      coord.X = x;
      coord.Y = y;
      ophandle = GetStdHandle(STD_OUTPUT_HANDLE);
      SetConsoleCursorPosition(ophandle, coord);
}

//用于绘制墙体和蛇身的基本结构
void printstraction(int x, int y)
{
      x *= 2;
      movcoord(x, y);
      printf("◆");
}

//用于绘制墙的基本单元
void printbrick(int x, int y)
{
      x *= 2;
      movcoord(x, y);
      printf("■");
}

//绘制墙体
void drawwalls()
{
      int i;
      system("color F0");

      for (i = 0;i < WIDTH;i++)
      {
                printbrick(i, 0);
                printbrick(i, HEIGHT-1);
      }
      for (i = 0;i < HEIGHT;i++)
      {
                printbrick(0, i);
                printbrick(WIDTH - 1, i);
      }
      movcoord(2 * WIDTH + 4, 0);
      printf("方向键:WASD");
      movcoord(2 * WIDTH + 4, 2);
      printf("按其他键暂停");
      movcoord(2 * WIDTH + 4, 4);
      printf("Your Score:");
}

//用于初始化数组
void setArray()
{
      int i;
      for (i = 0;i < WIDTH;i++)
      {
                PointArray = 1;
                PointArray = 1;
      }
      for (i = 0;i < HEIGHT;i++)
      {
                PointArray = 1;
                PointArray = 1;
      }
}

//用于改变数组里数字的值
void changeArray(Snake* p,int value)
{
      PointArray = value;
}

//用于更新得分
void ChangeScore(int score)
{
      movcoord(2 * WIDTH + 4, 5);
      printf("%d", score);
}

//用于初始化蛇的图像
void iniSnake()
{
      Snake* p = head;
      while (1)
      {
                printstraction(p->x, p->y);
                if (p->next == NULL)
                        break;
                p = p->next;
      }
}

//增加蛇的身体长度
void add(Snake* body)
{
      printstraction(body->x, body->y);
      movcoord(0, HEIGHT - 1);
      changeArray(body, 1);
}

//删除蛇的身体单元
void del(Snake* body)
{
      movcoord(2 * body->x, body->y);
      printf("");
      changeArray(body, 0);
}

//生成食物位置
void getfoodposition()
{
      static int general = (WIDTH - 2) * (HEIGHT - 2);
      int temp, i, j, flag = 0;
      srand(time(NULL));
      temp = rand() % (general - score - 3);
      for (i = 1;i < WIDTH - 1;i++)
      {
                for (j = 1;j < HEIGHT - 1;j++)
                {
                        if (temp == 0)
                        {
                              flag = 1;
                              break;
                        }
                        else if (!PointArray)
                              temp--;
                }
                if (flag)
                        break;
      }
      Food->x = i;
      Food->y = j;
}

//绘制食物
void setfood()//放置食物
{
      changeArray(Food, 2);
      Food->x *= 2;
      movcoord(Food->x, Food->y);
      printf("◎");
}

//产生食物
void creatfood()
{
      getfoodposition();
      setfood();
}

//蛇的前进与进食
//把蛇尾移至新蛇头的位置,然后让旧的蛇尾变成新的蛇头
//在蛇尾添加身体单元
int move()
{
      static Snake temp;//用于保存蛇尾移动之前的位置
      static int speed;

      if (score < 10)
                speed = 150;
      else if (score < 50)
                speed = 100;
      else
                speed = 50;

      temp.x = tail->x;
      temp.y = tail->y;

      switch (direction)
      {
      case 'w':
      case 'W':
                tail->x = head->x;
                tail->y = head->y - 1;
                break;
      case 'a':
      case 'A':
                tail->x = head->x - 1;
                tail->y = head->y;
                break;
      case 's':
      case 'S':
                tail->x = head->x;
                tail->y = head->y + 1;
                break;
      case 'd':
      case 'D':
                tail->x = head->x + 1;
                tail->y = head->y;
                break;
      }

      if (PointArray == 1)
      {
                return 1;
      }

      //增加蛇的长度
      else if (PointArray == 2)
      {
                Snake* p;
                p = (Snake*)malloc(sizeof(Snake));
                p->x = temp.x;
                p->y = temp.y;

                add(tail);

                tail->next = head;
                head->former = tail;
                head = tail;
                p->former = tail->former;
                tail = p;
                head->former = nullptr;

                ChangeScore(++score);
                creatfood();

                Sleep(speed);
                return 0;
      }

      else
      {
                del(&temp);
                add(tail);

                tail->next = head;
                head->former = tail;
                head = tail;
                tail = tail->former;
                head->former = nullptr;

                Sleep(speed);
                return 0;
      }
}

/***********************************************************************/

void main()
{
      extern Snake* head;//蛇头
      Snake* middle;//蛇肚
      extern Snake* tail;//蛇尾
      extern Snake* Food;//食物

      extern char direction;//键盘录入蛇的运动方向
      extern int score;//得分
      extern int PointArray;//用于生成食物及判定撞击

      setArray();//初始化数组
      drawwalls();//画出墙体

      head = (Snake*)malloc(sizeof(Snake));//为蛇头分配空间
      middle = (Snake*)malloc(sizeof(Snake));//为蛇肚分配空间
      tail = (Snake*)malloc(sizeof(Snake));//为蛇尾分配空间
      Food = (Snake*)malloc(sizeof(Snake));//为食物分配空间

      head->x = WIDTH / 2 - 1;
      head->y = HEIGHT / 2 - 1;
      head->former = nullptr;
      head->next = middle;
      PointArray = 1;

      middle->x = head->x;
      middle->y = (head->y + 1);
      middle->former = head;
      middle->next = tail;
      PointArray = 1;

      tail->x = middle->x;
      tail->y = (middle->y + 1);
      tail->former = middle;
      tail->next = nullptr;
      PointArray = 1;

      creatfood();//创造食物
      iniSnake();//画出蛇
      ChangeScore(score);//打印实时得分

      while ((direction == 's' || direction == 'S') ||
                        (
                              direction != 'a' && direction != 'A' &&
                              direction != 'w' && direction != 'W' &&
                              direction != 'd' && direction != 'D'
                              ))
                direction = _getch();//初始化蛇的前进方向

      while (1)
      {
                static char temp;
                static char judge;//用于判断按键是否反向
                temp = direction;

                //录入按键
                if (_kbhit())
                {
                        do
                        {
                              direction = _getch();
                        } while (direction != 'a' && direction != 'A' &&
                                       direction != 'w' && direction != 'W' &&
                                       direction != 's' && direction != 'S' &&
                                       direction != 'd' && direction != 'D'
                              );
                        judge = temp - direction;
                        if (
                              judge == 3 || judge == -3 ||
                              judge == 4 || judge == -4 ||
                              judge == 28 || judge == -28 ||
                              judge == 29 || judge == -28 ||
                              judge == 35 || judge == -35 ||
                              judge == 36 || judge == -36
                              )
                              direction = temp;
                }

                if (move())//判断撞击
                //释放内存
                {
                        Snake* p = tail;
                        while (p->former != nullptr)
                        {
                              p = p->former;
                              free(p->next);
                        }
                        free(p);
                        break;
                }
      }
      Sleep(500);
      system("cls");
      movcoord(WIDTH / 2, HEIGHT / 2);
      printf("你的得分:%d\n", score);
      system("pause");
}

程序运行效果图我发在下面评论里面了

CuteHamster 发表于 2019-11-13 12:39

本帖最后由 CuteHamster 于 2019-11-13 18:47 编辑

对第二个代码做了改进:
*.修复了蛇的头尾衔接时判定为撞击的错误判定,同时将蛇头单独打印图标
*.对生成食物位置的方法做了改正
1.补上了程序结束时对Food指针所指空间的释放
2.对生成的食物的位置增加了一个反馈验证,减小了运行时出错的概率
3.把打印所得分数的功能移进了产生食物的函数内
4.把主函数移到了其他函数定义前面
5.之前的代码在Visual Studio2019上可以编译,但用MinGW编译就不能通过,因此修改了一下代码使其在MinGW 下也能编译
(附上程序运行效果图 o(* ̄▽ ̄*)o )
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <windows.h>

enum
{
      WIDTH = 40,//墙的宽度
      HEIGHT = 30,//墙的高度
};

//蛇和食物的结构体定义
struct Snake
{
      int x;
      int y;
      struct Snake* next;
      struct Snake* former;
};

//全局变量
struct Snake* head;
struct Snake* tail;
struct Snake* Food;
char direction = 's';//键盘录入蛇的运动方向
int score = 0;//得分
int PointArray = { 0 };//用于生成食物及判定撞击

/*******************************************************************/
void MovCoord(int x, int y);//用于操作光标位置
void PrintStruction(int x, int y);//用于绘制蛇身基本单元
void PrintBrick(int x, int y);//用于绘制墙的基本单元
void DrawWalls();//绘制墙体
void SetArray();//用于初始化数组
void ChangeArray(struct Snake* p, int value);//用于改变数组里数字的值
void ChangeScore(int score);//用于更新得分
void iniSnake();//用于初始化蛇的图像
void Add(struct Snake* body);//增加蛇的身体长度
void PrintHead(struct Snake* body);//打印蛇头
void Del(struct Snake* body);//删除蛇的身体单元
void GetFoodPosition();//生成食物位置
void SetFood();//绘制食物
void CreatFood();//产生食物
int Move();//蛇的前进与进食
                   //把蛇尾移至新蛇头的位置,然后让旧的蛇尾变成新的蛇头
                   //在蛇尾添加身体单元
/*******************************************************************/

void main()
{
      SetArray();//初始化数组
      DrawWalls();//画出墙体

      iniSnake();//为蛇分配空间并画出蛇
      CreatFood();//创造食物并打印分数

      while ((direction == 's' || direction == 'S') ||
                (
                        direction != 'a' && direction != 'A' &&
                        direction != 'w' && direction != 'W' &&
                        direction != 'd' && direction != 'D'
                        ))
                direction = _getch();//初始化蛇的前进方向

      while (1)
      {
                static char temp;
                static char judge;//用于判断按键是否反向
                temp = direction;

                //录入按键
                if (_kbhit())
                {
                        do
                        {
                              direction = _getch();
                        } while (direction != 'a' && direction != 'A' &&
                              direction != 'w' && direction != 'W' &&
                              direction != 's' && direction != 'S' &&
                              direction != 'd' && direction != 'D'
                              );
                        judge = temp - direction;
                        if (
                              judge == 3 || judge == -3 ||
                              judge == 4 || judge == -4 ||
                              judge == 28 || judge == -28 ||
                              judge == 29 || judge == -28 ||
                              judge == 35 || judge == -35 ||
                              judge == 36 || judge == -36
                              )
                              direction = temp;
                }

                if (Move())//判断撞击
                //释放内存
                {
                        struct Snake* p = tail;
                        while (p->former != NULL)
                        {
                              p = p->former;
                              free(p->next);
                        }
                        free(p);
                        free(Food);
                        break;
                }
      }
      Sleep(500);
      system("cls");
      MovCoord(WIDTH / 2, HEIGHT / 2);
      printf("你的得分:%d\n", score);
      system("pause");
}

/***********************************************************************/

//用于操作光标位置
void MovCoord(int x, int y)
{
      COORD coord;
      HANDLE ophandle;
      coord.X = x;
      coord.Y = y;
      ophandle = GetStdHandle(STD_OUTPUT_HANDLE);
      SetConsoleCursorPosition(ophandle, coord);
}

//用于绘制墙体和蛇身的基本结构
void PrintStruction(int x, int y)
{
      x *= 2;
      MovCoord(x, y);
      printf("◆");
}

//用于绘制墙的基本单元
void PrintBrick(int x, int y)
{
      x *= 2;
      MovCoord(x, y);
      printf("■");
}

//绘制墙体
void DrawWalls()
{
      int i;
      system("color F0");

      for (i = 0;i < WIDTH;i++)
      {
                PrintBrick(i, 0);
                PrintBrick(i, HEIGHT - 1);
      }
      for (i = 0;i < HEIGHT;i++)
      {
                PrintBrick(0, i);
                PrintBrick(WIDTH - 1, i);
      }
      MovCoord(2 * WIDTH + 4, 0);
      printf("方向键:WASD");
      MovCoord(2 * WIDTH + 4, 2);
      printf("按其他键暂停");
      MovCoord(2 * WIDTH + 4, 4);
      printf("按下W开始游戏 o(* ̄▽ ̄*)o");
      MovCoord(2 * WIDTH + 4, 6);
      printf("Your Score:");
}

//用于初始化数组
void SetArray()
{
      int i;
      for (i = 0;i < WIDTH;i++)
      {
                PointArray = 1;
                PointArray = 1;
      }
      for (i = 0;i < HEIGHT;i++)
      {
                PointArray = 1;
                PointArray = 1;
      }
}

//用于改变数组里数字的值
void ChangeArray(struct Snake* p, int value)
{
      PointArray = value;
}

//用于更新得分
void ChangeScore(int score)
{
      MovCoord(2 * WIDTH + 4, 7);
      printf("%d", score);
}

//用于初始化蛇的图像
void iniSnake()
{
      struct Snake* middle = (struct Snake*)malloc(sizeof(struct Snake));//为蛇肚分配空间
      head = (struct Snake*)malloc(sizeof(struct Snake));//为蛇头分配空间
      tail = (struct Snake*)malloc(sizeof(struct Snake));//为蛇尾分配空间
      Food = (struct Snake*)malloc(sizeof(struct Snake));//为食物分配空间

      head->x = WIDTH / 2 - 1;
      head->y = HEIGHT / 2 - 1;
      head->former = NULL;
      head->next = middle;
      PointArray = 1;

      middle->x = head->x;
      middle->y = (head->y + 1);
      middle->former = head;
      middle->next = tail;
      PointArray = 1;

      tail->x = middle->x;
      tail->y = (middle->y + 1);
      tail->former = middle;
      tail->next = NULL;
      PointArray = 1;

      {
                struct Snake* p = head;
                PrintHead(p);
                while (1)
                {
                        if (p->next == NULL)
                              break;
                        p = p->next;

                        PrintStruction(p->x, p->y);
                }
      }
}

//增加蛇的身体长度
void Add(struct Snake* body)
{
      PrintStruction(body->x, body->y);
      MovCoord(0, HEIGHT - 1);
      ChangeArray(body, 1);
}

void PrintHead(struct Snake* head)
{
      MovCoord(2 * head->x, head->y);
      printf("☉");

      MovCoord(2 * head->next->x, head->next->y);
      printf("◆");

      ChangeArray(head, 1);

      MovCoord(0, HEIGHT - 1);
}

//删除蛇的身体单元
void Del(struct Snake* body)
{
      MovCoord(2 * body->x, body->y);
      printf("");
      ChangeArray(body, 0);
}

//生成食物位置
void GetFoodPosition()
{
      int temp, i, j, flag = 0;
      static int general = (WIDTH - 2) * (HEIGHT - 2);
      static int D_value;//围墙内剩余的空位
      while (!flag)
      {
                D_value = general - score - 3;
                if (!D_value)
                {
                        {
                              struct Snake* p = tail;
                              while (p->former != NULL)
                              {
                                        p = p->former;
                                        free(p->next);
                              }
                              free(p);
                              free(Food);
                              break;
                        }
                        system("cls");
                        MovCoord(WIDTH / 2, HEIGHT / 2);
                        printf("恭喜过关!\n");
                        printf("你的得分:%d\n", score);
                        system("pause");
                        exit(0);
                }
                srand(time(NULL));
                temp = rand() % D_value + 1;
                for (i = 1;i < WIDTH - 1;i++)
                {
                        for (j = 1;j < HEIGHT - 1;j++)
                        {
                              if (!PointArray)
                                        temp--;
                              if (temp == 0)
                              {
                                        flag = 1;
                                        break;
                              }
                        }
                        if (flag)
                              break;
                }
                if (PointArray)
                        flag = 0;

                Food->x = i;
                Food->y = j;
      }
}

//绘制食物
void SetFood()
{
      ChangeArray(Food, 2);
      MovCoord(2 * Food->x, Food->y);
      printf("●");
}

//产生食物并打印分数
void CreatFood()
{
      GetFoodPosition();//生成食物位置
      SetFood();//绘制食物
      ChangeScore(score);//打印分数
}

//蛇的前进与进食
//把蛇尾移至新蛇头的位置,然后让旧的蛇尾变成新的蛇头
//在蛇尾添加身体单元
int Move()
{
      static struct Snake temp;//用于保存蛇尾移动之前的位置
      static int speed;

      if (score < 10)
                speed = 120;
      else if (score < 50)
                speed = 90;
      else
                speed = 60;

      temp.x = tail->x;
      temp.y = tail->y;

      switch (direction)
      {
      case 'w':
      case 'W':
                tail->x = head->x;
                tail->y = head->y - 1;
                break;
      case 'a':
      case 'A':
                tail->x = head->x - 1;
                tail->y = head->y;
                break;
      case 's':
      case 'S':
                tail->x = head->x;
                tail->y = head->y + 1;
                break;
      case 'd':
      case 'D':
                tail->x = head->x + 1;
                tail->y = head->y;
                break;
      }

      Del(&temp);//删除蛇尾图像

      if (PointArray == 1)
      {
                return 1;
      }

      //增加蛇的长度
      else if (PointArray == 2)
      {
                struct Snake* p;
                p = (struct Snake*)malloc(sizeof(struct Snake));
                p->x = temp.x;
                p->y = temp.y;

                Add(p);

                tail->next = head;
                head->former = tail;
                head = tail;
                p->former = tail->former;
                tail = p;
                tail->former->next = tail;
                head->former = NULL;

                PrintHead(head);

                score++;
                CreatFood();

                Sleep(speed);
                return 0;
      }

      else
      {
                tail->next = head;
                head->former = tail;
                head = tail;
                tail = tail->former;
                head->former = NULL;

                PrintHead(head);

                Sleep(speed);
                return 0;
      }
}

CuteHamster 发表于 2019-11-15 16:51

本帖最后由 CuteHamster 于 2021-2-26 01:47 编辑

dkgaolinxian 发表于 2019-11-15 00:48
刚开始学c的小萌新希望大佬告诉我我想写出这种程度的代码要多久?
我不是软件相关专业的,上学期开始上的C语言程序设计基础课,上完指针和大概提了一下结构体就期末考试然后草草结课了,老师平时安排的作业也就是将合数分解成质数,计算自然对数的底数或正余弦值之类的小功能,上个学期的课程我上到指针后就一直在划水(当时有点懵),然后这学期开始觉得之前花了这么多时间学C语言,结果什么也不会写太不甘心了,就在网上买了书自己学,断断续续看了大概一个多月,觉得自己应该能写一些小程序了才在网上搜了一个能运行的贪吃蛇程序来学习,自己编程序,在解决问题的过程中也确实学到一些经验,其实在第二次代码的基础上我自己又重新写了一遍贪吃蛇,基本功能的实现没有多大变化,就是为了体验一次先从主函数开始写,一级一级往下先规划并声明函数的作用,等要用的所有函数的声明都写完了,再根据之前规划的功能底层填充函数的定义,在这个过程中我又体验到了驱动程序的重要作用,而且写起来也快了不少,我感觉自己写一个像控制台贪吃蛇这样的程序确实让我学到新的东西。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <conio.h>
#define _CRT_SECURE_NO_WARNINGS

//设置墙的宽和高
enum { width = 25, height = 25 };
//设置每走一步的间隔时间(ms)
enum { interval = 120 };

//蛇的身体单元
typedef struct snakeunit
{
        struct snakeunit* prior;        //蛇的前一节
        int existence;        //值为0则代表空气,-1代表墙或身体,-2代表食物
        struct snakeunit* next;                //蛇的后一节
        int x;
        int y;
}snakeunit;
//坐标
typedef struct coord
{
        int x;
        int y;
}coord;

//各种地图组件
char* brick_icon = "■";
char* head_icon = "⊙";
char* tail_icon = "●";
char* food_icon = "◆";
char* air_icon = "";


/*全局变量*/
//蛇的可移动空间
snakeunit mov_zone = { 0 };
//蛇身长度(分数)
int score = 0;
//蛇下一步前进的方向
char direction = 'd';
//游戏中蛇头、蛇尾、食物的坐标(以最左上角的墙体方块为原点,水平向右为X轴正向)
coord head_coord;
coord tail_coord;
coord food_coord;

/*函数声明*/
//初始化游戏界面
void InitializeGame();
//进行游戏
void PlayGame();
//显示最终分数
void EndGame();

//初始化墙体
void InitializeWall();
//放置蛇身
void InitializeSnake();
//放置食物
void SetFood();

//检查撞击
void CheckStrick(int* end_flag);
//移动蛇身
void MovSnake();

//打印
void PrintUnit(int x, int y, char* icon);
void HideCursor();//隐藏光标
void MovCoord(int x, int y);//移动光标坐标


/*函数定义*/
void main()
{
        InitializeGame();
        PlayGame();
        EndGame();
}

void InitializeGame()
{
        HideCursor();
        InitializeWall();
        InitializeSnake();
        SetFood();
}

void PlayGame()
{
        static int area = (width - 2) * (height - 2);
        static int end_flag = 1;
        static char temp;
        static char chart = { 'w','a','d','s' };

        while (end_flag)
        {
                temp = direction;
#if 1        /*        if后面的数字如果为1,则人可以干预贪吃蛇的轨迹,如果为0,则人不能干预贪吃蛇的轨迹
                        wasd控制蛇的前进方向                                                                                                                        */
                if (_kbhit())
                {
                        do
                        {
                                direction = tolower(_getch());
                        } while (direction != 'w' && direction != 'a' && direction != 's' && direction != 'd');

                        {
                                //此部分用于防止蛇头撞向颈部
                                int i, j;
                                for (i = 0; i < 4; i++)
                                {
                                        if (temp == chart)
                                                break;
                                }
                                for (j = 0; j < 4; j++)
                                {
                                        if (direction == chart)
                                                break;
                                }
                                if (i + j == 3)
                                        direction = temp;
                        }
                }
#endif
                CheckStrick(&end_flag);
                MovSnake();
                if (score == area)
                        end_flag = 0;
        }
}

void EndGame()
{
        int area = (width - 2) * (height - 2);

        system("cls");
        if (score == area)
        {
                printf("恭喜过关!\n");
        }
        else
        {
                printf("闯关失败!\n");
        }
        printf("蛇的长度: %d", score);
        while(1)
                _getch();
}

void InitializeWall()
{
        system("color 7D");

        for (int i = 0; i < width; i++)
        {
                for (int j = 0; j < height; j++)
                {
                        mov_zone.existence = 0;
                        mov_zone.x = i;
                        mov_zone.y = j;
                }
        }

        for (int i = 0; i < width; i++)
        {
                mov_zone.existence = -3;
                mov_zone.existence = -3;
                PrintUnit(i, 0, brick_icon);
                PrintUnit(i, height - 1, brick_icon);
        }
        for (int i = 0; i < height; i++)
        {
                mov_zone.existence = -3;
                mov_zone.existence = -3;
                PrintUnit(0, i, brick_icon);
                PrintUnit(width - 1, i, brick_icon);
        }
}

void InitializeSnake()
{
        mov_zone.existence = mov_zone.existence = mov_zone.existence = -3;
        PrintUnit(2, 2, head_icon);
        PrintUnit(2, 3, tail_icon);
        PrintUnit(2, 4, tail_icon);
        head_coord.x = head_coord.y = 2;
        tail_coord.x = 2;
        tail_coord.y = 4;
        mov_zone.next = &mov_zone;
        mov_zone.prior = &mov_zone;
        mov_zone.next = &mov_zone;
        mov_zone.prior = &mov_zone;

        score = 2;
}

void SetFood()
{
        static int left_area = (width - 2) * (height - 2) - 2;
        static unsigned int n, i, j;
        static int flag = 1;

        score++;
        left_area--;
        if (!left_area)
                flag = 0;
        if (flag)
        {
                srand(time(NULL));
                n = rand();
                n = n * n % left_area + 1;
                for (i = 1; i < width - 1; i++)
                {
                        for (j = 1; j < height - 1; j++)
                        {
                                if (!mov_zone.existence)
                                        n--;
                                if (!n)
                                        break;
                        }
                        if (!n)
                                break;
                }

                mov_zone.existence = -2;
                PrintUnit(i, j, food_icon);
                food_coord.x = i;
                food_coord.y = j;
        }
}

void CheckStrick(int* end_flag)
{
        static int x, y;

        switch (direction)
        {
        case 'w':
                x = head_coord.x;        y = head_coord.y - 1;
                break;
        case 's':
                x = head_coord.x;        y = head_coord.y + 1;
                break;
        case 'd':
                x = head_coord.x + 1;        y = head_coord.y;
                break;
        case 'a':
                x = head_coord.x - 1;        y = head_coord.y;
                break;
        default:
                system("cls");
                printf("Something wrong happened!\n");
                printf("%c", direction);
                _getch();
                break;
        }

        if (
                mov_zone.existence == -3 &&
                !(x == tail_coord.x && y == tail_coord.y)
                )
        {
                *end_flag = 0;
        }
}

void MovSnake()
{
        static coord temp;
        switch (direction)
        {
        case 'a':
                temp.x = head_coord.x - 1;
                temp.y = head_coord.y;
                break;
        case 'd':
                temp.x = head_coord.x + 1;
                temp.y = head_coord.y;
                break;
        case 'w':
                temp.x = head_coord.x;
                temp.y = head_coord.y - 1;
                break;
        case 's':
                temp.x = head_coord.x;
                temp.y = head_coord.y + 1;
                break;
        default:
                break;
        }

        if (mov_zone.existence == -2)
        {
                /*将蛇头向指定方向移动,保留蛇尾*/
                mov_zone.existence = -3;
                mov_zone.prior = &mov_zone;
                mov_zone.next = &mov_zone;
                PrintUnit(head_coord.x, head_coord.y, tail_icon);
                head_coord.x = temp.x;
                head_coord.y = temp.y;
                PrintUnit(head_coord.x, head_coord.y, head_icon);
                SetFood();
        }
        else
        {
                /*将蛇头向指定方向移动,删除蛇尾图标*/
                mov_zone.existence = -3;
                mov_zone.prior = &mov_zone;
                mov_zone.next = &mov_zone;
                PrintUnit(head_coord.x, head_coord.y, tail_icon);
                head_coord.x = temp.x;
                head_coord.y = temp.y;
                mov_zone.existence = 0;
                PrintUnit(tail_coord.x, tail_coord.y, air_icon);
                PrintUnit(head_coord.x, head_coord.y, head_icon);
                temp.x = tail_coord.x;
                temp.y = tail_coord.y;
                tail_coord.x = mov_zone.prior->x;
                tail_coord.y = mov_zone.prior->y;
        }

        Sleep(interval);        //暂停指定时间数
}

void PrintUnit(int x, int y, char* icon)
{
        MovCoord(x, y);
        printf(icon);
}

void MovCoord(int x, int y)
{
        COORD coord;
        HANDLE handle;
        coord.X = 2 * x;
        coord.Y = y;
        handle = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleCursorPosition(handle, coord);
}

void HideCursor()
{
        CONSOLE_CURSOR_INFO cursor;
        cursor.bVisible = FALSE;
        cursor.dwSize = sizeof(cursor);
        HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleCursorInfo(handle, &cursor);
}

世俗红尘 发表于 2019-11-13 08:23

膜拜大佬

龍謹 发表于 2019-11-13 08:42

厉害了我的锅,收藏学习,谢谢!

香辣鸡肉面 发表于 2019-11-13 08:48

真的是学无止境

coolcalf 发表于 2019-11-13 08:53

我要看运行效果

liehuo2400 发表于 2019-11-13 09:00

太厉害了,小白表示看不懂。

Zzhao233 发表于 2019-11-14 11:18

围观大佬!辛苦啦

dkgaolinxian 发表于 2019-11-15 00:48

刚开始学c的小萌新希望大佬告诉我我想写出这种程度的代码要多久?

sdbusqqq 发表于 2019-11-15 09:22

本帖最后由 sdbusqqq 于 2019-11-15 09:25 编辑

dkgaolinxian 发表于 2019-11-15 00:48
刚开始学c的小萌新希望大佬告诉我我想写出这种程度的代码要多久?
我个人觉得看基础的, 楼主这个例子,各种流程控制 ,指针都用到了。
也就是说,得把语法学完吧。少则两月,多的就直接放弃了。

写程序有两种,
一种是告诉你程序实现方法,用语法描述出来。
一种是告诉你我要什么, 你自己想办法用程序实现。

漫漫长路刚起步,萌新加油吧,
页: [1] 2 3
查看完整版本: 改进网上的一个用C语言写的贪吃蛇程序代码