控制台简易程序——贪吃蛇

控制台简易程序——贪吃蛇_第1张图片

目录

1.目标

2.预备知识

①WIN32 API

②控制台程序

③控制台屏幕上的COORD 

④GetStdHandle

⑤GetConsoleCursorInfo

 CONSOLE_CURSOR_INFO

SetConsoleCursorInfo

 ⑥SetConsoleCursorPosition

⑦GetAsyncKeyState

3.实现过程

游戏主体逻辑

①游戏开始

本地化

②游戏过程

③游戏结束

4.游戏整体流程

5.源码


1.目标

在控制台中实现一个简易的贪吃蛇小程序,蛇体可用●代替,作为边界的墙体用□表示,食物用★表示。

最终效果如下

控制台简易程序——贪吃蛇_第2张图片

同时实现蛇的移动,吃食物的反馈,以及游戏结束条件判断。

2.预备知识

①WIN32 API

有关部分WIN32 API的介绍。window系统提供了一些函数接口,可以让我们对应用程序进行一些操作。这些函数大概就是WIN32 API。

②控制台程序

控制台也是一种应用程序。可以在控制台中使用一些指令完成一些操作。

我们可以使⽤cmd命令来设置控制台窗⼝的⻓宽:设置控制台窗⼝的⼤⼩,30⾏,100列

mode con cols=100 lines=30

而类似这种指令也可以调用C语言函数system执行

#include 
int main()
{
    //设置控制台窗⼝的⻓宽:设置控制台窗⼝的⼤⼩,30⾏,100列
    system("mode con cols=100 lines=30");
    //设置cmd窗口名称
    system("title 贪吃蛇");
    return 0;
}

③控制台屏幕上的COORD 

COORD是Windows API中定义的⼀个结构体,表⽰⼀个字符在控制台屏幕上的坐标。

typedef struct _COORD {
    SHORT X;
    SHORT Y;
} COORD, *PCOORD;

这是其定义。

对于控制台来说,坐标意义如下

控制台简易程序——贪吃蛇_第3张图片

如图为x轴和y轴。

④GetStdHandle

“GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标
准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。”

这是官方的解释,通俗来讲,你可以同时开多个控制台窗口,并且你可以获得其中的一个窗口的句柄,用这个句柄来操作那个对应的控制台。

HANDLE GetStdHandle(DWORD nStdHandle); 

nStdHandle只有三种取值

控制台简易程序——贪吃蛇_第4张图片

分别对应标准输入,标准输出,和标准错误。

这里我们要使用标准输出。

HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);

这样得到一个标准输出的句柄。

⑤GetConsoleCursorInfo

BOOL WINAPI GetConsoleCursorInfo(
  _In_  HANDLE               hConsoleOutput,
  _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);

这是该函数声明。使用该函数可获得关于光标的有关信息,下图中白色闪烁的就是光标。

 hConsoleOutput就是通过④获得的句柄,lpConsoleCursorInfo就是一个指向光标的指针。

HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo); //获取控制台光标信息

 CONSOLE_CURSOR_INFO

typedef struct _CONSOLE_CURSOR_INFO {
    DWORD dwSize;
    BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

这个结构体就是光标结构体,dwSize指光标的大小,范围为1~99,bVisible为true意为光标可见,

为假意为光标隐藏。

SetConsoleCursorInfo

BOOL WINAPI SetConsoleCursorInfo(
    HANDLE hConsoleOutput,
    const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

这是该函数声明,使用它可以改变指定的控制台的光标状态,比如 光标是否隐藏,大小又为多少。

//获得句柄
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);


//隐藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;

GetConsoleCursorInfo(hOutput, &CursorInfo); //获取控制台光标信息

CursorInfo.bVisible = false; //隐藏控制台光标

SetConsoleCursorInfo(hOutput, &CursorInfo); //设置控制台光标状态

 ⑥SetConsoleCursorPosition

BOOL WINAPI SetConsoleCursorPosition(
    HANDLE hConsoleOutput,
    COORD pos
);

将光标设置到目标位置。

将其封装成设置光标位置的函数

//设置光标坐标
void SetPos(short x, short y)
{
	COORD pos = { x,y };
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(handle, pos);
}

⑦GetAsyncKeyState

SHORT GetAsyncKeyState(
    int vKey
);

vKey 是虚拟按键值,可以在微软官方查到Virtual-Key Codes (Winuser.h) - Win32 apps | Microsoft Learn

可获取键盘上的按键的状态,分辨是否为按压,抬起,或已被按过,或未被按过等等。

GetAsyncKeyState返回值是short类型,当返回数据最低位是1时说明该按键已被按过。

可封装为宏

#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 ) 

3.实现过程

开始前想到需要管理蛇,食物,和整个游戏的各项参数(如单个食物得分,总得分,游戏是否正常进行。

我们使用链表来管理蛇,链表节点数据中存储蛇节点的坐标。

//蛇的节点坐标为数据类型
typedef SnakeNodePos SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SLT;
//蛇节点的坐标
typedef struct SnakeNodePos
{
	int x;
	int y;
}SnakeNodePos;
//蛇的每个节点
typedef SLT SnakeNode;

食物也需要存储坐标,因此也使用 SnakeNode来存储。

同时还需要管理整个游戏的相关参数,我们用


//游戏状态
enum GameStatus
{
	//正常
	OK = 1,
	//ESC退出
	ESC,
	//撞墙结束
	KILL_BY_WALL,
	//撞到自己结束
	KILL_BY_SELF
};


typedef enum Direction
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
}Direction;


typedef struct Maintenance
{
	//蛇头
	SnakeNode* ps;
	//游戏状态
	enum GameStatus status;
	//食物
	SnakeNode* pFood;
	//蛇运动方向
	Direction direction;
	//总得分
	int Score;
	//当前吃掉食物所获得的分数
	int Add;
	//停顿时间
	int SleepTime;
}Maintenance;

游戏主体逻辑

游戏分为开始,过程和结束,并且能玩很多次

void test()
{
	Maintenance* maintence = NULL;
	char ch = 0;
	do
	{
		//游戏开始前的准备工作
		GameStart(&maintence);
		//游戏开始
		GameRun(&maintence);
		//游戏结束
		GameEnd(&maintence);
		system("pause");
		SetPos(5, COL / 2 + 1);
		printf("再来一局?Y/N");
		
		//读取字符是否为y或Y
		ch = getchar();
		getchar();
		SetPos(0, COL + 1);
	} while (ch == 'y' || ch == 'Y');
}

①游戏开始

玩家开始操作蛇之前要完成的功能有打印地图边界,初始化一条五个节点的蛇,并且打印出一个食物。

那么,如何打印出这些信息?

事实上,之前决定用的字符,在默认的c标准下是不能直接打印的,像●,□,★都占了两个字节,属于宽字符。

这里简单讲下C语言国际化标准,早期C语言不适合非英语国家使用,因为ascii编码的字符数量太少,不足以满足世界各地的字符使用需求(汉字就有几万个)。为了解决这个问题,C语言引入了本地化。

本地化

该库提供的函数中可以根据地区不同进行不同的行为,如货币量的格式,⽇期和时间的表⽰形式,

使用该库中的setlocale函数,可以改变一个或多个类项。

char* setlocale (int category, const char* locale); 

这里我们直接

setlocale(LC_ALL, " "); //切换到本地环境 

就可以存储并使用宽字符。

初始化蛇,建立5个节点并链接起来,可以使用链表尾插的方式建立五个节点的链表。

然后决定地图边界的坐标,将其打印。

在按照蛇的每个节点存储的坐标,打印出蛇的每个节点。

最后生成食物,并打印到控制台,这里需要注意的是:

食物不能生成在地图边界

食物不能生成在蛇内

食物的x坐标是偶数

因为宽字符占了两位

控制台简易程序——贪吃蛇_第5张图片

如图,两个字符a和●平齐。

保持食物x坐标是偶数可以使得蛇吃食物成功时视觉效果更好,且避免出现意想不到的bug。

②游戏过程

游戏运行,完成蛇的移动,加速减速,吃到食物的反馈及蛇身的变化。

移动需要确定方向,默认方向这里我们设置为左。

根据W,A,S,D是否被按下,确定蛇的运动方向。这里就可以用到上面在⑦中写的宏。

确定运动方向之后,就来实现运动功能。

不妨将运动的过程分解一下,分解为一个节点的运动,蛇向某个方向运动,就记录下,蛇头的下一个到达的坐标,若给坐标为食物坐标,就将这个节点头插到蛇头,若不是食物坐标,就头插后把尾节点删除。完成这步操作后,就打印新的蛇头节点。然后用循环,就可以连续运动。再借助Sleep函数,就可以产生运动的视觉效果。

还有暂停效果的实现:

使用Sleep函数和循环即可。

最后判断是否咬到自己和是否撞到边界较简单。

③游戏结束

结束较为简单,只需要释放蛇占用的空间即可。

void GameEnd(Maintenance** ppM)
{
	assert(ppM);
	SnakeNode* cur = (*ppM)->ps;
	
    //释放空间
    while (cur)
	{
		SnakeNode* next = cur->next;
		free(cur);
		cur = next;
	}
	

    //重新将各项数值变成原始状态,以便开始新的一局
    (*ppM)->ps = NULL;
	(*ppM)->direction = LEFT;

	(*ppM)->status = OK;
	(*ppM)->pFood = NULL;
	(*ppM)->Score = 0;
	(*ppM)->Add = 20;
	(*ppM)->SleepTime = 200;
}

4.游戏整体流程

控制台简易程序——贪吃蛇_第6张图片

5.源码

头文件存放函数声明和有关结构体

Snake.h
#pragma once

#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)

//地图长宽
#define ROW 58//对应x坐标
#define COL 27//对应y坐标
//蛇身初始长度
#define SnakeLength 5


#define WALL L'□'
#define NODE L'●'
#define FOOD L'★'
//声明链表结构
typedef struct SListNode SLT;

//蛇节点的坐标
typedef struct SnakeNodePos
{
	int x;
	int y;
}SnakeNodePos;

//蛇的每个节点
typedef SLT SnakeNode;


//游戏状态
enum GameStatus
{
	//正常
	OK = 1,
	//ESC退出
	ESC,
	//撞墙结束
	KILL_BY_WALL,
	//撞到自己结束
	KILL_BY_SELF
};


typedef enum Direction
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT
}Direction;

typedef struct Maintenance
{
	//蛇头
	SnakeNode* ps;
	//游戏状态
	enum GameStatus status;
	//食物
	SnakeNode* pFood;
	//蛇运动方向
	Direction direction;
	//总得分
	int Score;
	//当前吃掉食物所获得的分数
	int Add;
	//停顿时间
	int SleepTime;
}Maintenance;

//设置光标位置
void SetPos(short x, short y);

//游戏开始的欢迎语和地图打印
void GameStart(Maintenance** ppM);
void PrintMap(void);


//初始化蛇为5个节点
void SnakeInit(Maintenance** ppM);

//打印蛇
void PrintSnake(SnakeNode* ps);

//创建食物
bool FoodInSnake(Maintenance** ppM);
void CreateFood(Maintenance** ppM);

//游戏运行
void GameRun(Maintenance** ppM);
void MoveSnake(Maintenance** ppM, SnakeNode** Next);
void Pause(Maintenance** ppM);
void EatFood(Maintenance** ppM, SnakeNode** Next);
void NotEatFood(Maintenance** ppM, SnakeNode** Next);
void PrintHelpInfo(Maintenance** ppM);
bool KillBySelf(Maintenance** ppM, SnakeNode** Next);
bool KillByWall(SnakeNode** Next);


//游戏结束
void GameEnd(Maintenance** ppM);

实现函数接口的源文件

Snake.c

#include"SList.h"

//设置光标坐标
void SetPos(short x, short y)
{
	COORD pos = { x,y };
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(handle, pos);
}


void PrintMap(void)
{
	int i = 0;
	setlocale(LC_ALL, "");
	wchar_t wall = WALL;
	for (i = 0; i < ROW; i += 2)
		wprintf(L"%c", wall);
	SetPos(0, 1);
	for (i = 0; i < COL - 1; i++)
		wprintf(L"%c\n", wall);
	SetPos(ROW - 2, 1);
	for (i = 0; i < COL - 1; i++)
	{
		wprintf(L"%c", wall);
		SetPos(ROW - 2, i + 2);
	}
	SetPos(0, 27);
	for (i = 0; i < ROW; i += 2)
		wprintf(L"%c", wall);

}

void GameStart(Maintenance** ppM)
{
	//调整控制台大小
	system("mode con cols=100 lines=35");
	system("title 贪吃蛇");


	//隐藏光标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(handle, &CursorInfo);


	//欢迎语
	SetPos(40, 16);
	printf("欢迎来到贪吃蛇");
	SetPos(40, 20);
	system("pause");
	printf("按空格以继续");
	system("cls");


	//游戏操作说明
	SetPos(32, 16);
	printf("用W A S D控制方向,Q是加速,E是减速\n");
	SetPos(40, 20);
	printf("加速获得更高分数");
	SetPos(40, 25);
	system("pause");
	system("cls");


	//打印地图
	PrintMap();
	//蛇初始化
	SnakeInit(ppM);
	//食物初始化
	CreateFood(ppM);
}



void PrintSnake(SnakeNode* ps)
{
	assert(ps);
	SnakeNode* cur = ps;
	wchar_t node = NODE;
	while (cur)
	{
		SetPos(cur->data.x, cur->data.y);
		wprintf(L"%c", node);
		cur = cur->next;
	}
}

//初始化蛇
void SnakeInit(Maintenance** ppM)
{
	assert(ppM);
	//初始化蛇的位置坐标
	SnakeNodePos pos[SnakeLength] = { {16,8},{18,8},{20,8},{22,8},{24,8} };
	if (*ppM == NULL)
		*ppM = (Maintenance*)malloc(sizeof(Maintenance));
	if (*ppM == NULL)
	{
		perror("SnakeInit malloc");
		return;
	}
	(*ppM)->ps = NULL;
	for (int i = 0; i < SnakeLength; i++)
		SLTPushBack(&(*ppM)->ps, &pos[i]);//链表的尾插操作
	//打印蛇身
	PrintSnake((*ppM)->ps);
	//设置初始运动方向
	(*ppM)->direction = LEFT;
	
	(*ppM)->status = OK;
	(*ppM)->pFood = NULL;
	(*ppM)->Score = 0;
	(*ppM)->Add = 20;
	(*ppM)->SleepTime = 200;
}


//判断食物是否生成在蛇体内
bool FoodInSnake(Maintenance** ppM)
{
	assert(ppM);
	SnakeNode* cur = (*ppM)->ps;
	while (cur)
	{
		if (cur->data.x == (*ppM)->pFood->data.x
			&& cur->data.y == (*ppM)->pFood->data.y)
			return true;
		cur = cur->next;
	}
	return false;
}


void PrintHelpInfo(Maintenance** ppM)
{
	assert(ppM);
	SetPos(60, 13);
	printf("用W,A,S,D控制移动,Q加速,E减速,空格暂停 ");
	SetPos(60, 14);
	printf("加速可获得更高分数");
	SetPos(60, 15);
	printf("不能撞墙,咬到自己");
	SetPos(60, 16);
	printf("当前食物分数:%02d",(*ppM)->Add);
	SetPos(60, 17);
	printf("当前总得分:%02d", (*ppM)->Score);
}



//创建食物
void CreateFood(Maintenance** ppM)
{
	assert(ppM);
	if (*ppM == NULL)
		*ppM = (Maintenance*)malloc(sizeof(Maintenance));
	if (*ppM == NULL)
	{
		perror("SnakeInit malloc");
		return;
	}
	int x, y;
	
	
	//生成食物
	(*ppM)->pFood = (SnakeNode*)malloc(sizeof(SnakeNode));
	if ((*ppM)->pFood == NULL)
	{
		perror("Food Malloc");
		return;
	}
	
	
	//生成食物坐标
	srand((size_t)time(NULL));
	do
	{
		x = rand() % (ROW - 5) + 2;
		y = rand() % (COL - 1) + 1;
		(*ppM)->pFood->data.x = x;
		(*ppM)->pFood->data.y = y;
	} while (x % 2 || FoodInSnake(ppM));
	
	
	//初始化食物
	(*ppM)->pFood->next = NULL;

	//打印食物
	wchar_t food = FOOD;
	SetPos(x, y);
	wprintf(L"%c", food);
}


//游戏运行,完成蛇的移动,加速减速,吃到食物的反馈及蛇身的变化
void GameRun(Maintenance** ppM)
{
	assert(ppM);
	PrintHelpInfo(ppM);
	do
	{
		SnakeNode* Next = (SnakeNode*)malloc(sizeof(SnakeNode));
		
		//各个按键的反馈
		if (KEY_PRESS(0x41) && (*ppM)->direction != RIGHT)
			(*ppM)->direction = LEFT;
		else if (KEY_PRESS(0x44) && (*ppM)->direction != LEFT)
			(*ppM)->direction = RIGHT;
		else if (KEY_PRESS(0x57) && (*ppM)->direction != DOWN)
		{
			(*ppM)->direction = UP;
		}
		else if (KEY_PRESS(0x53) && (*ppM)->direction != UP)
			(*ppM)->direction = DOWN;
        else if (KEY_PRESS(VK_SPACE))
		{
			//暂停功能
			Pause(ppM);
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			(*ppM)->status = ESC;
			break;
		}
		
		
		//蛇移动一步
		if((*ppM)->status == OK)
		MoveSnake(ppM, &Next);
	

	} while ((*ppM)->status == OK);
	
	//游戏结束的各个状态
	SetPos(5, COL / 2);
	if ((*ppM)->status == KILL_BY_SELF)
		printf("咬到自己,游戏结束");
	else if ((*ppM)->status == KILL_BY_WALL)
		printf("撞到墙,游戏结束");
	else if ((*ppM)->status == ESC)
		printf("游戏结束");
}

void MoveSnake(Maintenance** ppM, SnakeNode** Next)
{
	assert(ppM);
	if ((*Next) == NULL)
	{
		perror("MoveSnake malloc");
		return;
	}
	
	
	//获取蛇头下一个位置的坐标
	switch ((*ppM)->direction)
	{
	    case UP:
	    {
			(*Next)->data.x = (*ppM)->ps->data.x;
			(*Next)->data.y = (*ppM)->ps->data.y - 1;
			break;
	    }
		case DOWN:
		{
			(*Next)->data.x = (*ppM)->ps->data.x;
			(*Next)->data.y = (*ppM)->ps->data.y + 1;
			break;
		}
		case LEFT:
		{
			(*Next)->data.x = (*ppM)->ps->data.x - 2;
			(*Next)->data.y = (*ppM)->ps->data.y;
			break;
		}
		case RIGHT:
		{
			(*Next)->data.x = (*ppM)->ps->data.x + 2;
			(*Next)->data.y = (*ppM)->ps->data.y;
			break;
		}
	}
	
	//判断是否咬到自己
	if (KillBySelf(ppM, Next))
	{
		(*ppM)->status = KILL_BY_SELF;
		return;
	}


	//判断撞墙
	if (KillByWall(Next))
	{
		(*ppM)->status = KILL_BY_WALL;
		return;
	}

	//判断是否吃到食物
	if ((*Next)->data.x == (*ppM)->pFood->data.x
		&& (*Next)->data.y == (*ppM)->pFood->data.y)
	{
		EatFood(ppM, Next);
		(*ppM)->Score += (*ppM)->Add;
		SetPos(60, 17);
		printf("当前总得分:%02d", (*ppM)->Score);
		free((*ppM)->pFood);
		CreateFood(ppM);
	}
	else
	{
		NotEatFood(ppM, Next);
	}
	

	//判断加速减速
	if (KEY_PRESS(0x51) && (*ppM)->status == OK && (*ppM)->SleepTime > 10)
	{
		SetPos(60, 16);
		(*ppM)->Add += 2;
		printf("当前食物分数:%02d", (*ppM)->Add);
		(*ppM)->SleepTime -= 10;
	}
	if (KEY_PRESS(0x45) && (*ppM)->status == OK && (*ppM)->Add > 2)
	{
		SetPos(60, 16);
		(*ppM)->Add -= 2;
		printf("当前食物分数:%02d", (*ppM)->Add);
		(*ppM)->SleepTime += 10;
	}
	Sleep((*ppM)->SleepTime);
}
 



void EatFood(Maintenance** ppM, SnakeNode** Next)
{
	assert(ppM);
	SnakeNode* cur = (*ppM)->ps;
	
	
	//头插入Next
	SetPos((*Next)->data.x, (*Next)->data.y);
	wchar_t node = NODE;
	wprintf(L"%lc", node);
	(*Next)->next = (*ppM)->ps;
	(*ppM)->ps = (*Next);
}


void NotEatFood(Maintenance** ppM, SnakeNode** Next)
{
	assert(ppM && *ppM);
	
	SnakeNode* cur = (*ppM)->ps;
	
	//找尾
	while (cur->next->next)
	{
		cur = cur->next;
	}
	SnakeNode* ptail = cur->next;
	
	//找到尾,把尾的坐标处打印为空格
	SetPos(ptail->data.x, ptail->data.y);
	printf("  ");
	free(ptail);
	//倒数第二个成为新的尾
	cur->next = NULL;
	
	//Next成为新的头
	SetPos((*Next)->data.x, (*Next)->data.y);
	wchar_t node = NODE;
	wprintf(L"%lc", node);
	(*Next)->next = (*ppM)->ps;
	(*ppM)->ps = (*Next);
}


bool KillBySelf(Maintenance** ppM, SnakeNode** Next)
{
	SnakeNode* cur = (*ppM)->ps->next;
	while (cur)
	{
		if (cur->data.x == (*Next)->data.x
			&& cur->data.y == (*Next)->data.y)
			return true;
		cur = cur->next;
	}
	return false;
}


bool KillByWall(SnakeNode** Next)
{
	SnakeNode* cur = (*Next);
	if (cur->data.x == 0 || cur->data.x == ROW - 2
		|| cur->data.y == 0 || cur->data.y == COL)
		return true;
	return false;
}


void Pause(Maintenance** ppM)
{
	assert(ppM);
	while (true)
	{
		Sleep(1);
		if (KEY_PRESS(VK_SPACE) && (*ppM)->status == OK)
			break;
		if (KEY_PRESS(VK_ESCAPE))
		{
			(*ppM)->status = ESC;
			break;
		}
	}
}



void GameEnd(Maintenance** ppM)
{
	assert(ppM);
	SnakeNode* cur = (*ppM)->ps;
	while (cur)
	{
		SnakeNode* next = cur->next;
		free(cur);
		cur = next;
	}
	(*ppM)->ps = NULL;
	(*ppM)->direction = LEFT;

	(*ppM)->status = OK;
	(*ppM)->pFood = NULL;
	(*ppM)->Score = 0;
	(*ppM)->Add = 20;
	(*ppM)->SleepTime = 200;
}

测试test.c文件

#include"SList.h"
void test()
{
	Maintenance* maintence = NULL;
	char ch = 0;
	do
	{
		//游戏开始前的准备工作
		GameStart(&maintence);
		//游戏开始
		GameRun(&maintence);
		//游戏结束
		GameEnd(&maintence);
		system("pause");
		SetPos(5, COL / 2 + 1);
		printf("再来一局?Y/N");
		
		//读取字符是否为y或Y
		ch = getchar();
		getchar();
		SetPos(0, COL + 1);
	} while (ch == 'y' || ch == 'Y');
}




int main()
{
	test();
	
	return 0;
}

你可能感兴趣的:(C语言基础语法,c语言,数据结构,链表)