本程序思想参考《C语言程序设计与游戏开发实践教程》,但并未照搬代码,难免疏漏,请多批评。
游戏截图:
#include<stdio.h>
#include<conio.h>
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#pragma warning(disable:4996)//在vs环境下屏蔽C4996错误:因为使用getch()
#define High 21//地图的高度
#define Width 21//地图的宽度
#define Speed 200//控制每次刷新的频率(蛇的速度)
int num = 1;//给出大号食物与小号食物的比例
int direction=3, d =3 ;//初始化方向(1上2左3右4下)、d记录前进方向
int x_h, y_h;//蛇头位置
int scare=0,s=0;//判断应得大号食物的20分还是小号食物的10分
int a[High][Width] = { 0 };//将地图清零
void Point();//去掉光标
void Setxy(short x, short y);
void Food();//生成食物
void Start();//初始化
void Show();//打印
int UpdateWithoutInput(int *);//与输入无关操作
void UpdateWithInput();//与输入有关操作
int main()
{
char c1;
int live = 1;//判断游戏是否结束、
Start();
while (live)//一直运行,直到死亡
{
Sleep(Speed);
Show();
UpdateWithInput();
if (UpdateWithoutInput(&live)==1)
continue;
}
system("cls");//清屏函数:只执行一次,且以下输出不能覆盖已输出的最后一次地图,故使用
printf("\n\n\n\n\n\n\n\n\n\n\t\t\t\t\t\tYou lose."
"\n\t\t\t\t\t Your scare is %d."
"\n\t\t\t\t\t 按任意键退出\n\n\n\n", scare);
c1 = getch();//在失败界面上停留直到按下任意键
return 0;
}
void Start()
{
system("CLS");//清屏
Point();//移去光标
printf("\n\n\n\n\n\n\n\n\n\n\t\t\t\t\t请将输入法切换成英文,再按任意键开始\n"
"\t\t\t\t\t\t使用wasd移动");
srand((unsigned)time(NULL));//初始化随机数种子,以便food()中利用rand()生成伪随机数
char b = getch();
system("CLS");
for (int i = 0; i < Width; i++)
{
a[0][i] = a[High - 1][i] = -1;
if (i < High)
{
a[i][0] = a[i][Width - 1] = -1;
}
}//用一个for循环来定边界
a[y_h = High / 2][x_h = Width / 2] = 1;//初始化蛇头为地图中间
for (int i = 1; i <= 4; i++)
{
a[High / 2][Width / 2 - i] = i + 1;
}//蛇身
Food();//生成食物
}
void Point() {
HANDLE fd = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cinfo;
cinfo.dwSize = 1;
cinfo.bVisible = 0;
SetConsoleCursorInfo(fd, &cinfo);
}
void Setxy(short x, short y) //x,y为光标座标
{
COORD coord = { x, y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void Food() {
int a1, b1;//
if (num % 4 == 0)//每三个小号食物生成一个大号食物
{
while (1)
{
srand((unsigned)time(NULL));
a1 = (int)(rand() % High);
b1 = (int)(rand() % Width);
if (a[a1][b1] == 0) {//找到一个空白处
a[a1][b1] = -2;
break;
}
}
}
else
{
while (1) {
a1 = (int)(rand() % (High - 3) + 1);
b1 = (int)(rand() % (Width - 3) + 1);
if (a[a1][b1] == 0 && a[a1 + 1][b1] == 0 && a[a1][b1 + 1] == 0 && a[a1 + 1][b1 + 1] == 0)
{
a[a1][b1] = a[a1 + 1][b1] = a[a1][b1 + 1] = a[a1 + 1][b1 + 1] = -2;
break;
}
}
}
num++;
}
void Show()
{
Setxy(0, 0);//代替cls,界面不闪动,将坐标改到(0,0),覆盖上一次输出(在每次输出范围相同时使用)
for (size_t i = 0; i < High; i++)
{
for (size_t j = 0; j < Width; j++)
{
if (a[i][j] == -1)//边界
printf("■");
else if (a[i][j] == 1)//蛇头
printf("■");
else if (a[i][j] > 1)//蛇身
printf("□");
else if (a[i][j] == -2)//食物
printf("* ");
else
printf(" ");//空白
}
if (i == 10)
printf("Scrae:%d", scare);
printf("\n");
}
}
int UpdateWithoutInput(int* p_live)
{
int i, j;
int max = 0, x_max = 0, y_max = 0;
for (i = 0; i < High; i++)
{
for (j = 0; j < Width; j++)
{
if (a[i][j] > max)
{
y_max = i;
x_max = j;
max = a[i][j];//应交换值
}
}
}//挑出最大值
a[y_max][x_max] = 0;//将蛇尾置0,找出蛇尾:生成时即为最大值
for (i = 0; i < High; i++)
{
for (j = 0; j < Width; j++)
{
if (a[i][j] > 0)
a[i][j]++;
}
}
if ((d + direction) % 5 == 0)//识别输入的方向与正在行进的方向相反,违规操作
{
d = direction;
return 1;
}
d = direction;
switch (direction)
{
case 1:--y_h; break;
case 2:--x_h; break;
case 3:++x_h; break;
case 4:++y_h; break;
}//修改蛇头位置
if (a[y_h][x_h] == -2)//蛇头吃到了食物
{
int i1, j1, i2, j2;
int max = 0;
for (i = 0; i < High; i++)
{
for (j = 0; j < Width; j++)
{
if (a[i][j] == -2)
{
a[i][j] = 0;
s++;
}
if (a[i][j] > max)
{
i1 = i; j1 = j;
max = a[i][j];
}
}
}
if (s == 1)
scare += 10;
else
scare += 20;
s = 0;
if (a[i1 - 1][j1] == max - 1)
{
a[i1 + 1][j1] = max + 1;
}
else if (a[i1 + 1][j1] == max - 1)
{
a[i1 - 1][j1] = max + 1;
}
else if (a[i1][j1 + 1] == max - 1)
{
a[i1][j1 - 1] = max + 1;
}
else if (a[i1][j1 - 1] == max - 1)
{
a[i1][j1 + 1] = max + 1;
}
Food();
}
else if (a[y_h][x_h] != 1 && a[y_h][x_h] != 0 )//蛇头咬住了自己或撞上了墙
{
*p_live = 0;
}
a[y_h][x_h] = 1;//设置新蛇头值为1
return 0;
}
void UpdateWithInput()
{
if (kbhit())//接受到键盘输入,函数值置1,否则置0。与main函数中while(live)结合,解决自动行走问题
{
char c = getch();
switch (c)
{
case 'w':direction = 1; break;
case 'a':direction = 2; break;
case 'd':direction = 3; break;
case 's':direction = 4; break;
default:break;
}
}
}
教训:
- 几乎所有在两个函数都使用的变量都设置为全局变量,耦合性太强,不易修改调试
核心方法:
- 在移动时将蛇尾消去:在初始化时,将蛇尾设为地图数组的最大值,移动时将它设为与空白相同的值。
- 蛇头为1(以下皆为数组中的值),与蛇头更近(逻辑上)的值越小,蛇尾值最大。