1. 玩家电脑轮流先手
2. 优化了电脑下棋的算法,实现智能落子
3. 优化游戏界面,提高游戏体验
game.h文件:内容包括文件包含,宏定义,全局数据类型的声明,外部链接函数的声明
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#define MENUBLANK "\t\t\t "
#define BOARDBLANK "\t\t\t"
enum state{
DRAW = 1,
WIN,
LOSE,
CONTINUE
};
//1. iswin函数的返回值与judgment中相应游戏状态的下标设置相同,方便使用
//2. 枚举类型state,为代表游戏状态的数字定义了名称,方便理解代码。
void ShowBoard(const char board[3][3], const int score, const char* judgment);
void PlayerMove(char board[3][3]);
enum state IsWin(const char board[3][3]);
void ComputerMove(char board[3][3]);
处理玩家选项,决定是否开始游戏或是退出程序。
int main(){
enum option input = 0;
do{
//2.打印菜单
Menu();
//3.处理用户选项
fflush(stdin);//清除缓冲区中残留的非法输入***
scanf("%d", &input);
switch (input)
{
case PLAY:
//3.玩游戏:控制游戏流程
PlayGame();
system("pause");
break;
case EXIT:
printf("退出游戏!\n");
system("pause");
break;
default:
printf("输入错误,请重新输入!\n");
system("pause");
break;
}
} while (input);
return 0;
}
打印菜单以供玩家选择
void Menu(){
system("cls");
printf("\n\n\n\n\n\n");
printf("\t\t\t\tTic Tac Toe");
printf("\n\n\n");
printf(MENUBLANK"****************************\n");
printf(MENUBLANK" 1.PLAY \n");
printf(MENUBLANK"****************************\n");
printf(MENUBLANK" 0.EXIT \n");
printf(MENUBLANK"****************************\n");
//MENUBLANK替换为空白字符使菜单显示在窗口中央
printf("\n\n\n");
}
通过调用游戏具体实现的函数,控制游戏的整个流程,是设计思路的直接体现
void PlayGame(){
char board[3][3];//棋盘:0代表空,#代表玩家,*代表电脑
char* judgment[5] = { "Game in Progress",
"It's a draw",
"You Win",
"You Lose",
"New game starting..."};//游戏状态
int jud = 0;//游戏状态标记
int score = 0;//游戏得分
enum state ret = 0;//输赢结果
int op = 1;//人机轮换先手标记
while (1)
{
//初始化棋盘和状态标记
memset(board, 0, 3 * 3);
jud = 0;
while (1)
{
//*打印棋盘
ShowBoard(board, score, judgment[jud]);
//人机轮换先手下棋
if (op==1)
{
PlayerMove(board);//玩家下
}
else
{
ComputerMove(board);//电脑下
}
//*判断输赢
if ((ret = IsWin(board)) != CONTINUE)
break;
//*打印棋盘
ShowBoard(board, score, judgment[jud]);
if (op == -1)
{
PlayerMove(board);
}
else
{
ComputerMove(board);
}
//*判断输赢
if ((ret = IsWin(board)) != CONTINUE)
break;
//*打印棋盘
ShowBoard(board, score, judgment[jud]);
}
switch (ret)
{
case DRAW:
jud = DRAW;
score += 50;
break;
case WIN:
jud = WIN;
score += 100;
break;
case LOSE:
jud = LOSE;
score -= 100;
break;
}
ShowBoard(board, score, judgment[jud]);
Sleep(2000);
jud = 4;
ShowBoard(board, score, judgment[jud]);
Sleep(2000);
op *= -1;//交换先后手
}
}
负责打印棋盘,当前得分和游戏状态(赢或输等)
void ShowBoard(const char board[3][3], const int score, const char* judgment){
//打印内容:棋盘,分数,游戏状态
system("cls");
printf("\n\n\n\n");
printf("\t\t\t\tTic Tac Toe");
int i;
printf("\n\n\n\n\n");
for (i = 0; i < 3; i++)
{
printf(BOARDBLANK);
printf(" %c | %c | %c ", board[i][0], board[i][1], board[i][2]);
//*打印游戏状态
if (i == 0){
printf("\t\t");
printf(judgment);
}
//*打印分数
if (i == 1){
printf("\t\t");
printf("Score: %d", score);
}
printf("\n");
if (i<2)
printf(BOARDBLANK"---|---|---\n");
}
printf("\n\n\n");
}
负责玩家下棋的具体实现(对应设计思路图中“玩家下棋”)
void PlayerMove(char board[3][3]){
int x = 0;
int y = 0;
printf("玩家下:\n");
while (1)
{
//*输入坐标
printf("请输入坐标:");
fflush(stdin);
scanf("%d %d", &x, &y);
//*检查坐标:
if (x >= 1 && x <= 3 && y >= 1 && y <= 3)//1.在不在范围
{
if (board[x - 1][y - 1] == 0)//2.棋位是否为空
{
//*落子
board[x - 1][y - 1] = '#';
break;
}
else
{
printf("棋位已占!\n");
}
}
else
{
printf("非法坐标!\n");
}
}
}
判断输赢情况
enum state IsWin(const char board[3][3]){
int i,j;
char ch;
//*判断是否平局
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
if (board[i][j] == 0)
break;
}
if (board[i][j] == 0)
break;
}
if (i == 3){
return DRAW;
}
//*判断行
for ( i = 0; i < 3; i++)
{
ch = board[i][0];
for ( j = 1; j < 3; j++)
{
if (ch != board[i][j])
break;
}
if (j == 3 && ch != 0){
return ch=='#'? WIN : LOSE;
}
}
//*判断列
for (i = 0; i < 3; i++)
{
ch = board[0][i];
for (j = 1; j < 3; j++)
{
if (ch != board[j][i])
break;
}
if (j == 3 && ch != 0){
return ch == '#' ? WIN : LOSE;
}
}
//*判断右下
ch = board[0][0];
for ( i = 1; i < 3; i++)
{
if (ch != board[i][i])
break;
}
if (i == 3 && ch != 0){
return ch == '#' ? WIN : LOSE;
}
//*判断左下
ch = board[0][2];
for (i = 1; i < 3; i++)
{
if (ch != board[i][2-i])
break;
}
if (i == 3 && ch != 0){
return ch == '#' ? WIN : LOSE;
}
//*游戏继续
return CONTINUE;
}
负责电脑下棋的具体实现(对应设计思路图中“电脑下棋”)
void ComputerMove(char board[3][3]){
printf("电脑下:\n");
int rank[3][3] = {7,7,7,7,7,7,7,7,7};
int i, j;
//*为每个空位评定等级
for ( i = 0; i < 3; i++)
{
for ( j = 0; j < 3; j++)
{
if (board[i][j] == 0)
{
rank[i][j] = ChackBoard(board,i,j);
}
}
}
//*找出最高等级落子
int top = 7;
int tpx = 0;
int tpy = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
if (rank[i][j] < top){
top = rank[i][j];
tpx = i;
tpy = j;
}
}
}
Sleep(1000);
board[tpx][tpy] = '*';
}
根据等级评定规则为给定的棋位评定优先级
int ChackBoard(const char board[3][3], const int x, const int y){
//评定等级:
//1.追三 2.堵二 3.对角 4.边角 5.中心 6.普通 7.棋位已占
int ret = 6;
char line[3] = { 0 };
int i = 0;
int tmp = 0;
//*中心
if (x == 1 && y == 1)
{
ret = 5;
}
//*边角
if ((x == 2 || x == 0) && (y == 2 || y == 0))
{
ret = 4;
}
//*对角
if (board[2 - x][2 - y] == '*')
{
ret = 3;
}
//*行
for ( i = 0; i < 3; i++)
{
line[i] = board[x][i];
}
tmp=ChackLine(line);
ret = MIN(ret, tmp);
//*列
for (i = 0; i < 3; i++)
{
line[i] = board[i][y];
}
tmp = ChackLine(line);
ret = MIN(ret, tmp);
//*右下
if (x == y)
{
for (i = 0; i < 3; i++)
{
line[i] = board[i][i];
}
tmp = ChackLine(line);
ret = MIN(ret, tmp);
}
//*左下
if (x + y == 2)
{
for (i = 0; i < 3; i++)
{
line[i] = board[i][2-i];
}
tmp = ChackLine(line);
ret = MIN(ret, tmp);
}
return ret;
}
检查给定的棋位在行或列或对角线上的落子情况
int ChackLine(const char line[3]){
int i = 0;
int count = 0;
for (i = 0; i < 3; i++)
{
if (line[i] == '*')
{
count++;
}
else if (line[i] == '#')
{
count--;
}
}
//*追三
if (count == 2)
{
return 1;
}
//*堵二
else if (count == -2)
{
return 2;
}
//*普通
else
{
return 6;
}
}
1. 每局游戏开始前都需要清空棋盘,并将游戏状态标记设置为0。(初始化)
2. op:人机轮换先手标记,当op==1时玩家先手,当op==-1是电脑先手,每局游戏结束后op*=-1实现每局轮换。
一局游戏结束后评定分数,打印棋盘:
1. 等级评定规则(7级等级最低,1级等级最高。选择等级最高的棋位下棋):
7级:已占棋位; 6级:一般棋位; 5级:中心棋位(2 2); 4级:四个脚上的棋位(1 1或3 1);
3级:位于角落且对角有自己的棋子; 2级:对手已经在一条直线上已经下了两个棋子;
1级:自己已经在一条直线上已经下了两个棋子;
2. 将rank中所有等级初始化为7级,且只有未占的棋位才能评定等级,就成功将已占棋位筛除在外。
这里只解释1,2级的评定方法:
1. 评定方法:先将棋位所在的一整行或一整列或整条对角线收集到line数组中。
再交给ChackLine函数评定1级或2级。结果当然应该取最高等级。
2. 右下对角线上的棋位横纵坐标相同,如果x==y表示存在右下对角线。
3. 左下对角线上的棋位横纵坐标相加等于2,如果x+y==2表示存在左下对角线。
经过简单的优化,电脑确实变得不好对付了:
说实话,平局容易,胜局难。我也是尝试了很多次才找到了几种获胜的办法,你也可以下载来试一试。
最后将完整源码分享给大家,希望大家能与我在交流中共同学习,共同进步:
https://gitee.com/zty857016148/C_OS_Project/tree/master/TicTacToehttps://gitee.com/zty857016148/C_OS_Project/tree/master/TicTacToe
如果大家还有什么问题欢迎提问,我抽空一定回答!
若是各位大佬发现了代码中的错误或是遗漏之处还请海涵,最好在评论区交流助我一臂之力!
下一篇:【初级C语言】详解扫雷游戏(含难度选择,雷位标记,递归展开等功能)https://blog.csdn.net/zty857016148/article/details/126754920