人工智能三子棋-人机对弈-人人对弈,谁会是最终赢家?

news2024/11/24 22:25:52

在这里插入图片描述

✅作者简介:大家好我是原始豌豆,感谢支持。
🆔本文由 原始豌豆 原创 CSDN首发🐒 如需转载还请通知⚠
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​
📣系列专栏:C语言项目实践

前言

    相信大家都玩过三子棋或者五子棋游戏,但你会通过编程完美实现吗,本篇将带领你真正学会AI智能三子棋,让程序拥有人工智能,可以实现堵棋,赢棋,自由落子的功能。玩家VS电脑,玩家VS玩家。谁会是最终赢家?

文章目录

一、项目背景与目标
二、最终成果展示
三、模块化编程
四、整体结构组成
五、代码及思路讲解
     (1)控制台属性设置函数
     (2)颜色控制函数
     (3)背景音乐函数
     (4)光标函数
     (5)三子棋字幕动画函数
     (6)主菜单界面player vs computer 动画展示函数
     (7)主菜单函数
     (8)初始化棋盘函数
     (9)打印棋盘函数
     (10)玩家下棋函数
     (11)玩家下棋提示框函数
     (12)AI人工智能下棋函数
     (13)AI人工智能三连赢棋函数
     (14)AI人工智能堵棋函数
     (15)先手后手函数
     (16)遍历棋盘函数
     (17)电脑下棋提示框函数
     (18)玩家VS玩家下棋函数
     (19)人机对战中玩家赢或电脑赢或平局的提示框函数
     (20)游戏主体函数
     (21)游戏说明函数
     (22)开发人员菜单函数
     (23)退出游戏函数
     (24)主函数

六.全部源码
七.项目总结

一、项目背景与目标

  三子棋,又称为井字棋,是一种古老而简单的两人对弈游戏。在3x3的棋盘上,两位玩家轮流在空格上放置自己的标记(通常为X和#),先连成三子一线(横、竖或斜)的玩家获胜。本项目旨在使用C语言实现一个能够与人类玩家进行对战并具有一定智能水平的三子棋AI。在人机对战中这个AI会尝试找到一个胜利的模式或阻止对手胜利的模式。如果没有找到,它将会选择其余可以干扰到玩家的位置下棋。除此之外该程序还具备人人对战的功能。

二、最终成果展示

话不多说,先给大家看下做好后的最终效果!!!

三子棋游戏最终效果图GIF展示(带背景音乐)

在这里插入图片描述

三、模块化编程

实现智能三子棋代码逻辑前,我们来说说什么是模块化编程吧?

什么是模块化编程?
  传统方式编程:所有的函数均放在主函数main.c里,一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路。
   模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include
"XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。

四、整体结构组成

游戏代码主要由四部分组成。
beautify.c
   该文件内主要存放三子棋游戏的控制台设置函数,颜色控制函数,音乐播放函数,光标定位函数,游戏开场动画函数,菜单界面动画函数,主菜单界面,打印玩家赢或电脑赢或平局的提示框函数,游戏规则说明函数,开发人员菜单函数,打印玩家下棋对应的提示框函数,打印电脑下棋坐标提示框的函数,打印退出游戏动态效果
game.c
    该文件内主要存放初始化棋盘函数 ,打印棋盘函数 ,玩家下棋函数,电脑下棋函数,分别调用AI智能三连赢棋函数,AI智能堵棋函数,按比重权值范围随机落子,实现人工智能下棋,判断棋盘是否为空函数,AI人工智能三连赢棋函数,AI人工智能三连赢棋函数,玩家VS玩家下棋函数 ,判断输赢函数
test.c
   该文件内主要存放人机对战中选择玩家先手或电脑先手的函数,游戏主体函数,主函数

sanziqi.h 头文件
   声明游戏内容中所包含的所有函数

在这里插入图片描述

五、代码及思路讲解

(1)控制台属性设置函数

  智能三子棋是基于C语言编写的控制台应用程序,我们首先要把控制台的各种参数设置好,控制台显示的宽度和高度,控制台缓冲区的大小和控制台标题的设置。

//控制台属性设置
void ConsoleSet()
{
	system("mode con cols=141 lines=50");//窗口宽度和高度
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//使用GetStdHandle函数来获取标准输出句柄(STD_OUTPUT_HANDLE),通常用于控制台应用程序。
	COORD bufferSize = { 141, 500 }; // 缓冲区大小为141*500字符  缓冲区的大小以字符宽和高为单位。//设置的控制台缓冲区大小要超过控制台大小
	SetConsoleScreenBufferSize(hOut, bufferSize);
	system("title 智能三子棋");//界面美化
}

(2)颜色控制函数

  智能三子棋游戏里使用了很多不同颜色的动态效果展示和界面,所以我们先编写一个color函数用来控制程序的各个界面的颜色,让程序运行看上去更加美观、鲜明。

//颜色控制函数
void color(short x)
{
	if (x >= 0 && x <= 15)
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);
	else
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}

(3)背景音乐函数

   我们的游戏是带有背景音乐的,所以这里我们创建一个背景音乐函数BackgroundMusic,通过调用windows api来实现音乐的播放和音效的实现,“AI-sanziqi.wav”,引号内部的内容为音频文件的名称(音频文件必须是wav格式的,音频文件放在代码对应的release文件底下程序才能有声音),在这里给
BackgroundMusic函数设置了参数x,通过参数用来控制音乐的打开或关闭。传参数e时音乐停止,音频文件我会放到本文的最后。

void BackgroundMusic(char x) //参数传s代表start,开始播放音乐,参数传e代表end,终止播放音乐
{
	if (x == 's')
	{
		PlaySound(TEXT("AI-sanziqi.wav"), NULL, SND_FILENAME | SND_LOOP | SND_ASYNC);//播放音频文件,循环播放,异步播放  
	}
	if (x == 'e')
	{
		PlaySound(NULL, NULL, SND_PURGE); // 停止所有声音 
	}
}

(4)光标函数

   从本文最开始的最终效果展示中可以看到,部分图案和文字的的打印定位是通过光标位置到指定坐标,然后开始打印的,所以我们编写一个光标控制函数。

//定位光标位置到指定坐标
void goto_xy(int x, int y)
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);;
	COORD pos = { x,y };
	SetConsoleCursorPosition(handle, pos);
}

(5)三子棋字幕动画函数

   游戏开场动画,用字符数组存储并通过循环搭配Sleep函数和光标函数实现显示动画和动画清除的效果,本函数有两个参数,参数传p打印图案,传c清屏图案。

//游戏开场动画,三子棋字幕显示(函数内部不带颜色)
void sanziqi(char print_or_cls)//参数传p打印图案,传c清屏图案
{
	char str[] =
		"######################################################################\n\
#                      #                      #     ##   ###   ###   #\n\
#                      #    ##                #     ##   ###   ###   #\n\
#   ###############    #    ##############    #     ##  ####   ####  #\n\
#   ###############    #    ##############    #     ##  ###########  #\n\
#                      #            ####      #  ##################  #\n\
#                      #          #####       #  #######  ##   ###   #\n\
#                      #         ####         #  #######  ########   #\n\
#                      #         ###          #    ###    ########   #\n\
#     ##        ##     # ###################  #    #####  ##   ###   #\n\
#     ############     # ###################  #   ######  ########   #\n\
#     ############     # ###################  #   ####### ########   #\n\
#                      #          ##          #  #######  ##   ###   #\n\
#                      #          ##          # ### ## ## ##   ##### #\n\
#                      #          ##          #  ## ## ############# #\n\
#                      #          ##          #     ## ############# #\n\
#  ##             ###  #          ##          #     ##    ### ####   #\n\
#  ##################  #       #####          #     ##   ###  #####  #\n\
#  ##################  #       #####          #     ## ####     #### #\n\
#                      #       ####           #     ##  ##       ##  #\n\
#                      #                      #                      #\n\
#                      #                      #                      #\n\
######################################################################";

	if (print_or_cls == 'p')	//参数传p代表打印图案printf
		for (int i = 0; i < strlen(str); i++)
		{
			putchar(str[i]);
			if (str[i] == '\n')
			{
				Sleep(50);
			}
		}
	else if (print_or_cls == 'c')  //参数传c代表清除图案cls
	{
		goto_xy(0, 0);   //将坐标传到(0,0),再依次按行打印空格,以达到清屏效果
		for (int i = 0; i < strlen(str); i++)
		{
			putchar(' ');
			if (str[i] == '\n')
			{
				Sleep(50);
			}
		}
	}
}

效果展示
在这里插入图片描述

(6)主菜单界面player vs computer 动画展示函数

   从本文最开始的最终效果展示中可以看到,主菜单界面的左侧是有player vs computer 这个动态效果的展示的,所以我们编写一个player vs computer动画展示函数,用字符数组存储图案,并通过循环搭配Sleep函数和光标函数实现显示动画和动画清除的效果,我将图案分成了三部分,并设置了红,绿,蓝三种颜色对一部分每一部图案。

//游戏主菜单界面左侧player vs computer 动画展示(函数内部自带颜色,红,绿,蓝)
void player_vs_computer()
{
	int line = 0;//控制player vs computer在goto_xy函数操纵下的的行数位置
	char ch1[] =
		"        _  \n"
		" _ __  | |   __ _   _   _    ___   _ __  \n"
		"| '_ \\ | |  / _` | | | | |  / _ \\ | '__|\n"
		"| |_)| | | | (_| | | |_| | |  __/ | |   \n"
		"|.__./ |_|  \\__,_|  \\__, |  \\___| | _|\n"
		"|_|                 |___/ \n";

	char ch2[] =
		" __   __  ___ \n"
		" \\ \\ /  // __|\n"
		"  \\ V /  \\__ \\\n"
		"   \\_/   |___/\n";

	char ch3[] =
		"                                        _\n"
		"  ___   ___   _ __ ___   _ __   _   _  | |_    ___   _ __ \n"
		"/ __|  / _ \\ | '_ ` _ \\ | '_ \\ | | | | | __|  / _ \\ | '__|\n"
		"| (__ | (_) || | | | | || |_) || |_| | | |_  |  __/ |  | \n"
		"\\___ | \\___/ |_| |_| |_|| .__ / \\__,_|  \\___| \\___| | _|\n"
		"                        |_| \n";
	color(4);//红色
	goto_xy(5, 0);
	for (int i = 0; i < strlen(ch1); i++)
	{
		printf("%c", ch1[i]);
		if (ch1[i] == '\n')
		{
			Sleep(30);
			goto_xy(5, ++line);
		}
	}
	color(10);//绿色
	goto_xy(15, ++line);
	for (int i = 0; i < strlen(ch2); i++)
	{
		printf("%c", ch2[i]);
		if (ch2[i] == '\n')
		{
			Sleep(30);
			goto_xy(15, ++line);
		}
	}
	color(3);//蓝色
	goto_xy(0, ++line);
	for (int i = 0; i < strlen(ch3); i++)
	{
		printf("%c", ch3[i]);
		if (ch3[i] == '\n')
		{
			Sleep(30);
			goto_xy(0, ++line);
		}
	}
}

效果展示
在这里插入图片描述

(7)主菜单函数

  我们既然做游戏,就得有一个菜单,供玩家选择开始游戏或退出游戏等功能。我们先做一个Menu菜单函数并写上每个功能,使用时在主函数里调用就可以展示给玩家了。
  菜单函数也可以设计的很精美,可以参考我的设计样式.最后的goto_xy(75, 16);是为了将输入处的光标定位在括号里面

//游戏主菜单界面菜单界面(函数内部不带颜色)
void Menu()
{
	goto_xy(60, 5);
	printf("________________________\n");  Sleep(70);
	goto_xy(60, 6);
	printf("|       1.开始游戏      |\n");  Sleep(70);
	goto_xy(60, 7);
	printf("|_______________________|\n"); Sleep(70);
	goto_xy(60, 8);
	printf("|       2.人人对战      |\n");  Sleep(70);
	goto_xy(60, 9);
	printf("|_______________________|\n"); Sleep(70);
	goto_xy(60, 10);
	printf("|       3.游戏说明      |\n"); Sleep(70);
	goto_xy(60, 11);
	printf("|_______________________|\n"); Sleep(70);
	goto_xy(60, 12);
	printf("|       4.开发人员      |\n"); Sleep(70);
	goto_xy(60, 13);
	printf("|_______________________|\n"); Sleep(70);
	goto_xy(60, 14);
	printf("|       5.退出游戏      |\n"); Sleep(70);
	goto_xy(60, 15);
	printf("|_______________________|\n"); Sleep(70);
	goto_xy(60, 16);
	printf("请输入选项->【   】");
	goto_xy(75, 16);//将输入处的光标定位在括号里面
}

效果展示
在这里插入图片描述

(8)初始化棋盘函数

  菜单函数写好以后,游戏拥有的基本功能框架也就定位好了,接下来我们要做的就是实现菜单函数里所希望实现的所有功能,先来实现人机对战。
  创建棋盘并进行初始化,因为我们是要把符号输入到二维数组中,结合我们构造出来的图案形成三子棋的模样,所以我们需要创建一个二维数组并进行初始化(因为棋盘最开始为空白的,所以二维数组初始化的内容为空格),三子棋的棋盘一共三行三列,共有九个空可以下棋,这用一个三行三列的数组来存放每个空的数据,为了代码的灵活性,这里不要把行和列的值写死,用define定义的常量来写,方便以后代码的更新。

//初始化棋盘函数  
void InitBoard(char Board[ROW][COL], int row, int col)   // 二维数组作为参数传递给函数时,必须指明数组的列数
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Board[i][j] = ' ';//将棋盘的每个位置初始化为空格
		}
	}
}

(9)打印棋盘函数

  我们创建并初始化棋盘后,需要打印出三子棋的棋盘供玩家进行观察和下棋,我们同样不要把行和列写死,而是采用define定义的常量ROW和COL,这样玩家如果不想玩3*3格的三子棋,或者想把三子棋改为五子棋,只需在头文件中改变define定义的常量值,棋盘的大小也会随之改变。

//打印棋盘函数  (亮黄色棋盘)
int DisplayBoard(char Board[ROW][COL], int row, int col)
{
	color(14);//黄色棋盘
	int k = 0;  //goto_xy函数的列坐标
	goto_xy(50, ++k);  //棋盘打印的初始坐标
	for (int i = 1; i <= col; i++)//打印棋盘列序号
	{
		printf("   %d", i);
	}
	goto_xy(52, ++k);
	for (int i = 0; i < col; i++)
	{
		printf("____");
	}
	for (int i = 0; i < row; i++)//控制行数
	{
		goto_xy(50, ++k);//k不断自增以对应棋盘不同的大小,起到换行后居中的效果
		printf("%d|", i + 1);//打印棋盘行序号和最左侧边框
		for (int j = 0; j < col; j++)
		{
			printf(" %c |", Board[i][j]);//将"   |"看作一组,根据col打印
		}
		goto_xy(50, ++k);//k不断自增以对应棋盘不同的大小,起到换行后居中的效果
		printf(" |");//打印棋盘无序号行和最左侧边框
		for (int j = 0; j < col; j++)
		{
			printf("---|");//将"---|"看作一组,根据col打印
		}
	}
	printf("\n\n");
}

三子棋棋盘ROW和COL都为3效果图:
在这里插入图片描述
例如将ROW和COL都改成9:效果图
在这里插入图片描述

(10)玩家下棋函数

  在玩家下棋这部分中,我们定义玩家通过输入坐标来进行下棋。需要注意的是:数组所定义的下标是从0开始的,而玩家认知的坐标是从1开始的(例如:玩家想输入第一行第一列的坐标是,输入的是(1,1),而不是(0,0),这里需要将玩家输入的横、纵坐标减1,转换成数组的坐标。

   在玩家输入完坐标后,我们还需要判断该坐标是否合法,如果玩家输入的坐标超过棋盘界限,或者该坐标已经下过棋了,那么需要重新输入坐标。所以我们采用while循环结构,直到玩家下棋成功,跳出循环。
这里玩家下的棋,用 X表示。定义PlayerMove用来实现玩家下棋的这部分。

//玩家下棋函数  (玩家下棋显示红色提示框)
void PlayerMove(char Board[ROW][COL], int row, int col)
{
	color(4);//玩家下棋显示红色提示框
	int i = 0, j = 0;//定义了两个整数变量i和j,用于存储玩家输入的坐标。
	int ch = 0;
	PrintPlayerMove(1);
	while (1)//开始一个无限循环,这样程序会持续等待玩家的输入,直到玩家输入有效的坐标并下棋break。
	{
		scanf("%d%d", &i, &j);
		while ((ch = getchar()) != '\n' && ch != EOF);//对玩家输入数据进行处理,防止字符造成死循环。
		if ((i >= 1 && i <= row) && (j >= 1 && j <= col))//如果坐标在棋盘范围内(即1到行数和1到列数之间),则执行以下代码:
		{
			if (Board[i - 1][j - 1] == ' ')//检查该坐标位置是否为空。如果为空,表示这个位置可以被下棋
			{
				Board[i - 1][j - 1] = 'X';
				break;//成功下棋后跳出循环
			}
			else
			{
				PrintPlayerMove(2);
			}
		}
		else//如果坐标不在棋盘范围内,重新输入。
		{
			PrintPlayerMove(3);
		}
	}
}

(11)玩家下棋提示框函数

   玩家下棋回合得有提示框提示玩家到下棋回合了,或者玩家坐标输入错误,或者坐标不合法都会有对应的提示框,我们单独做一个玩家下棋提示框函数来显示这些,并通过不同的参数判断该打印哪条提示框信息。上面写的玩家下棋函数就可以调用玩家提示框函数,因为后续还会有人人对战模式,所以我们这里直接把人人对战的提示框也写好。

//打印玩家下棋对应的提示框函数  (函数内部不带颜色)
void PrintPlayerMove(int n)
{
	if (n == 1)
	{
		printf(" __________________________________\n");
		printf("|       玩家下棋回合, 棋子‘X’    |\n");
		printf("|__________________________________|\n");
		printf(" 输入坐标:");
	}
	else if (n == 2)
	{
		printf(" __________________________________\n");
		printf("|      坐标被占用, 请重新输入      |\n");
		printf("|__________________________________|\n");
		printf(" 输入坐标:");//如果该位置已被占据,程序会提示玩家重新输入:
	}
	else if (n == 3)//如果坐标不在棋盘范围内,重新输入。
	{
		printf(" __________________________________\n");
		printf("|      坐标不合法,请重新输入       |\n");
		printf("|__________________________________|\n");
		printf(" 输入坐标:");
	}
	else if (n == 4)
	{
		printf(" __________________________________\n");
		printf("|       玩家一下棋回合, 棋子‘X’  |\n");
		printf("|__________________________________|\n");
		printf(" 玩家一输入坐标:");
	}
	else if (n == 5)
	{
		printf(" __________________________________\n");
		printf("|       玩家二下棋回合, 棋子‘#’  |\n");
		printf("|__________________________________|\n");
		printf(" 玩家二输入坐标:");
	}

}

效果展示图
在这里插入图片描述

(12)AI人工智能下棋函数
看了CSDN大部分文章写的三子棋,都是没有智能的,玩家轻松就能击败电脑,那么如何赋予电脑智能呢?让电脑可以击败玩家或立于不败之地呢?其实并不复杂。

   电脑实现AI人工智能下棋分为三个部分:赢棋、堵棋、按权值自由落子。

   这里的优先级是赢棋>堵棋>自由落子,在写代码的时候要遵循:能赢优先赢,不能赢则优先堵,以上情况都不符合的时候自由落子。(自由落子不等于随机下棋,找到以自身坐标为中心,周围8个坐标有玩家棋子的位置进行落子,可以对玩家形成一定的干扰)

定义三个函数:

   智能三连赢棋函数:AI_WinChess(如果函数执行并落子,返回’s’;否则返回’f’)

   智能堵棋函数: AI_BlockingChess(如果函数执行并落子,返回1;否则返回0)

   自由落子函数:ComputerMove(构建整体框架,根据前两个函数的返回值来判断是否落子)

//电脑下棋函数,分别调用AI智能三连赢棋函数,AI智能堵棋函数,按比重权值范围随机落子,实现人工智能下棋
void ComputerMove(char Board[ROW][COL], int row, int col)
{
	int x, y;//定义了两个整数变量x和y,用于存储电脑下棋的坐标。

	//1.智能三连赢棋函数优先级最高,//如果智能三连赢棋函数没有成功找到可以三连的棋子则返回f,然后进入内层运行智能堵棋函数
	if (AI_WinChess(Board, ROW, COL) == 'f')
	{
		//智能堵棋函数优先级第二,//如果智能堵棋函数没有成功找到可以堵棋的棋子则返回0,然后进入内层进行随机落子
		if (AI_BlockingChess(Board, ROW, COL) == 0)
		{
			//分支判断电脑是否是先手且第一步下棋,是则在中心位置下棋,
			if (TraverseBoard(Board, ROW, COL) == 'e')
			{
				color(15);
				Board[row / 2][col / 2] = '#';
				AI_Coordinate(row / 2, col / 2);
			}
			else if (TraverseBoard(Board, ROW, COL) == 'n')//否则则找到以自身为中心,其余八个坐标内有玩家棋子的位置进行随机下棋
			{

				while (1)
				{
					color(11);//随机下棋湖蓝色
					x = rand() % row;//在循环内,使用rand()函数%row和col,生成一个坐标值不会越界的随机的坐标(x, y)
					y = rand() % col;
					if (Board[x][y] == ' ' && (Board[x - 1][y - 1] == 'X' || Board[x - 1][y] == 'X' || Board[x - 1][y - 1] == 'X' || Board[x][y - 1] == 'X' || Board[x][y + 1] == 'X' || Board[x + 1][y - 1] == 'X' || Board[x + 1][y] == 'X' || Board[x + 1][y + 1] == 'X'))
					{
						Board[x][y] = '#';
						AI_Coordinate(x, y);
						break;
					}
				}
			}
		}
	}
}

(13)AI人工智能三连赢棋函数

对玩家的的落子进行分析,实现智能赢棋 (优先级最高) ,电脑下棋用‘#’表示,该函数在AI人工智能下棋函数中被调用。

1 判断所有行里是否有某行出现 #  #  空 ,  #  空  # ,空  #  #,三种情况进行智能赢棋,
2.判断所有列里是否有某列出现
  #   #   空
  #   空  #
  空  #   #
3.判断一条角线(\)里是否有出现
#     #     空
  #    空     #
    空     #     #
4.判断另一条角线(/)里是否有出现
   #     #     空
  #     空       #
空     #      #

// AI人工智能三连赢棋函数(对玩家的的落子进行分析,实现智能赢棋  (优先级最高) (亮白色提示框)
char AI_WinChess(char Board[ROW][COL], int row, int col)// 返回值s代表success电脑三连赢棋成功,返回值f代表failure电脑未找到能够三连的的棋子

{
	int i = 0;
	int j = 0;
	color(15);
	//1.判断所有行里是否有某行出现 ##空 #空# 空##,三种情况进行智能赢棋
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col - 2; j++)
		{
			if (Board[i][j] == Board[i][j + 1] && Board[i][j] == '#' && Board[i][j + 2] == ' ')
			{
				Board[i][j + 2] = '#';
				AI_Coordinate(i, j + 2);//将下棋坐标传入AI_Coordinate函数
				return 's';
			}
			if (Board[i][j] == Board[i][j + 2] && Board[i][j] == '#' && Board[i][j + 1] == ' ')
			{
				Board[i][j + 1] = '#';
				AI_Coordinate(i, j + 1);//将下棋坐标传入AI_Coordinate函数
				return 's';
			}
			if (Board[i][j + 1] == Board[i][j + 2] && Board[i][j + 1] == '#' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数
				return 's';
			}
		}
	}

	//2.判断所有列里是否有某列出现  
	// #   #   空
	// #   空   #
	// 空   #   #
	//以上三种情况,进行智能三连赢棋
	for (j = 0; j < col; j++)
	{
		for (i = 0; i < row - 2; i++)
		{
			if (Board[i][j] == Board[i + 1][j] && Board[i][j] == '#' && Board[i + 2][j] == ' ')
			{
				Board[i + 2][j] = '#';
				AI_Coordinate(i + 2, j);//将下棋坐标传入AI_Coordinate函数
				return 's';
			}
			if (Board[i][j] == Board[i + 2][j] && Board[i][j] == '#' && Board[i + 1][j] == ' ')
			{
				Board[i + 1][j] = '#';
				AI_Coordinate(i + 1, j);//将下棋坐标传入AI_Coordinate函数
				return 's';
			}
			if (Board[i + 1][j] == Board[i + 2][j] && Board[i + 1][j] == '#' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数
				return 's';
			}
		}
	}


	//3.判断一条角线(\)里是否有出现
	//#           #              空
	//  #           空              #
	//    空           #               #
	//以上三种情况,进行智能赢棋
	for (i = 0; i < row - 2; i++)
	{
		for (j = 0; j < col - 2; j++)
		{
			if (Board[i][j] == Board[i + 1][j + 1] && Board[i][j] == '#' && Board[i + 2][j + 2] == ' ')
			{
				Board[i + 2][j + 2] = '#';
				AI_Coordinate(i + 2, j + 2);
				return 's';
			}
			if (Board[i][j] == Board[i + 2][j + 2] && Board[i][j] == '#' && Board[i + 1][j + 1] == ' ')
			{
				Board[i + 1][j + 1] = '#';
				AI_Coordinate(i + 1, j + 1);
				return 's';
			}
			if (Board[i + 1][j + 1] == Board[i + 2][j + 2] && Board[i + 1][j + 1] == '#' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);
				return 's';
			}
		}
	}


	//4.判断一条角线(/)里是否有出现
	//       #           #           空
	//     #           空           #
	//  空           #            #
	//以上三种情况,进行智能赢棋
	for (i = 0; i < row - 2; i++)
	{
		for (j = col - 1; j > 1; j--)
		{
			if (Board[i][j] == Board[i + 1][j - 1] && Board[i][j] == '#' && Board[i + 2][j - 2] == ' ')
			{
				Board[i + 2][j - 2] = '#';
				AI_Coordinate(i + 2, j - 2);
				return 's';//返回这个棋子的字符
			}
			if (Board[i][j] == Board[i + 2][j - 2] && Board[i][j] == '#' && Board[i + 1][j - 1] == ' ')
			{
				Board[i + 1][j - 1] = '#';
				AI_Coordinate(i + 1, j - 1);
				return 's';//返回这个棋子的字符
			}
			if (Board[i + 1][j - 1] == Board[i + 2][j - 2] && Board[i + 1][j - 1] == '#' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);
				return 's';//返回这个棋子的字符
			}
		}
	}

	return 'f';
}

(14)AI人工智能堵棋函数

(对玩家的的落子进行分析,实现智能堵棋)(优先级第二),该函数在AI人工智能下棋函数中被调用。
  堵棋的优先度在下新棋之上,在赢棋的优先度之下。

堵棋共有四种堵法:横着堵、竖着堵、左斜堵、右斜堵。
  将堵棋的大致框架写出,并且为了代码的适用性,这里的代码也不要写死,既要适用三行三列的棋盘,也要同样适用于更大的棋盘。(五行五列、十行十列等等)
  当棋盘中不需要堵棋时,返回0;而只要当其中一种情况成立,电脑便落子堵棋,返回1。

//AI人工智能堵棋函数(对玩家的的落子进行分析,实现智能堵棋)(优先级第二) (紫色提示框)
int AI_BlockingChess(char Board[ROW][COL], int row, int col)
{
	color(5);//紫色
	int i = 0;
	int j = 0;
	//判断所有行里是否有某行出现 XX空 X空X 空XX,三种情况进行智能堵棋
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col - 2; j++)
		{
			if (Board[i][j] == Board[i][j + 1] && Board[i][j] == 'X' && Board[i][j + 2] == ' ')
			{
				Board[i][j + 2] = '#';
				AI_Coordinate(i, j + 2);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
			if (Board[i][j] == Board[i][j + 2] && Board[i][j] == 'X' && Board[i][j + 1] == ' ')
			{
				Board[i][j + 1] = '#';
				AI_Coordinate(i, j + 1);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
			if (Board[i][j + 1] == Board[i][j + 2] && Board[i][j + 1] == 'X' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
		}
	}

	//判断所有列里是否有某列出现  
	// X   X   空
	// X   空   X
	// 空   X   X
	//以上三种情况,进行智能堵棋
	for (j = 0; j < col; j++)
	{
		for (i = 0; i < row - 2; i++)
		{
			if (Board[i][j] == Board[i + 1][j] && Board[i][j] == 'X' && Board[i + 2][j] == ' ')
			{
				Board[i + 2][j] = '#';
				AI_Coordinate(i + 2, j);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
			if (Board[i][j] == Board[i + 2][j] && Board[i][j] == 'X' && Board[i + 1][j] == ' ')
			{
				Board[i + 1][j] = '#';
				AI_Coordinate(i + 1, j);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
			if (Board[i + 1][j] == Board[i + 2][j] && Board[i + 1][j] == 'X' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
		}
	}

	//判断一条角线(\)里是否有出现
	//X           X              空
	//  X           空              X
	//    空           X               X
	//以上三种情况,进行智能堵棋
	for (i = 0; i < row - 2; i++)
	{
		for (j = 0; j < col - 2; j++)
		{
			if (Board[i][j] == Board[i + 1][j + 1] && Board[i][j] == 'X' && Board[i + 2][j + 2] == ' ')
			{
				Board[i + 2][j + 2] = '#';
				AI_Coordinate(i + 2, j + 2);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
			if (Board[i][j] == Board[i + 2][j + 2] && Board[i][j] == 'X' && Board[i + 1][j + 1] == ' ')
			{
				Board[i + 1][j + 1] = '#';
				AI_Coordinate(i + 1, j + 1);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
			if (Board[i + 1][j + 1] == Board[i + 2][j + 2] && Board[i + 1][j + 1] == 'X' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数
				return 1;
			}
		}
	}

	//判断一条角线(/)里是否有出现
	//       X           X           空
	//     X           空           X
	//  空           X            X
	//以上三种情况,进行智能堵棋
	for (i = 0; i < row - 2; i++)
	{
		for (j = col - 1; j > 1; j--)
		{
			if (Board[i][j] == Board[i + 1][j - 1] && Board[i][j] == 'X' && Board[i + 2][j - 2] == ' ')
			{
				Board[i + 2][j - 2] = '#';
				AI_Coordinate(i + 2, j - 2);//将下棋坐标传入AI_Coordinate函数
				return 1;//返回这个棋子的字符
			}
			if (Board[i][j] == Board[i + 2][j - 2] && Board[i][j] == 'X' && Board[i + 1][j - 1] == ' ')
			{
				Board[i + 1][j - 1] = '#';
				AI_Coordinate(i + 1, j - 1);//将下棋坐标传入AI_Coordinate函数
				return 1;//返回这个棋子的字符
			}
			if (Board[i + 1][j - 1] == Board[i + 2][j - 2] && Board[i + 1][j - 1] == 'X' && Board[i][j] == ' ')
			{
				Board[i][j] = '#';
				AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数
				return 1;//返回这个棋子的字符
			}
		}
	}

	return 0;
}

(15)先手后手函数

人机对战中选择玩家先手或电脑先手的函数

//人机对战中选择玩家先手或电脑先手的函数
int Xianshou_Houshou()
{
	int control = 0;
	while (1)
	{
		printf(" _____________________________________\n"); Sleep(30);
		printf("|          1.玩家先手(简单)           |\n");  Sleep(30);
		printf("|          2.电脑先手(困难)           |\n");  Sleep(30);
		printf("|_____________________________________|\n"); Sleep(30);
		printf(" 请选择>:");
		scanf("%d", &control);
		while (getchar() != '\n');
		if (control == 1)
			return 1;
		else if (control == 2)
			return 2;
		else
		{
			printf("选择错误,重新选择");
			Sleep(750);
			system("cls");
		}
	}
}

效果图展示
在这里插入图片描述

(16)遍历棋盘函数

  智能三子棋游戏中,我们设置人机对战中玩家可以选择电脑先手下棋或玩家先手下棋,如果电脑先手下棋,棋盘开始时应该是空的,所以我们做一个遍历棋盘函数,根据返回值判断电脑第一步下棋时应该落子的坐标,该函数在AI人工智能下棋函数中被调用。

//遍历棋盘判断棋盘是否为空,电脑先手且棋盘为空的情况下,返回e,非空返回n。
char TraverseBoard(char Board[ROW][COL], int row, int col)
{
	int flag = 0;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (Board[i][j] != ' ')
			{
				flag = 'n';    //nonempty 非空
				return flag;
			}
		}
	}
	return 'e'; //empty 空
}

(17)电脑下棋提示框函数

   玩家下棋回合有玩家提示框,那么电脑下棋也得有电脑下棋提示框,该函数还可以显示电脑下棋的坐标,在智能三连赢棋函数AI_WinChess,智能堵棋函数:AI_BlockingChess, 自由落子函数ComputerMove被调用。

//打印电脑下棋坐标提示框的函数,参数xy为电脑下棋位置 (函数内部不带颜色)
void AI_Coordinate(int x, int y)
{
	printf(" __________________________________\n");
	printf("|  电脑回合,棋子‘#’电脑思考中... |\n");
	printf("|__________________________________|\n");
	Sleep(100);
	printf(" __________________________________\n");
	printf("|       电脑下棋坐标(%d , %d)        |\n", x + 1, y + 1);//将电脑下棋的坐标展示给玩家。展示时横纵坐标+1后展示。
	printf("|__________________________________|\n");
	Sleep(400);
}

效果图展示
在这里插入图片描述
在这里插入图片描述

(18)玩家VS玩家下棋函数

   智能三子棋游戏中除了人机对战还有人人对战的功能,所以应该写一个玩家VS玩家下棋函数 ,来实现人人对战。

//玩家VS玩家下棋函数  (玩家一下棋显示淡绿色提示框)(玩家二下棋显示淡蓝色提示框)
void Player1_VS_Player2(char Board[ROW][COL], int row, int col, int x)
{
	int i = 0, j = 0;//定义了两个整数变量i和j,用于存储玩家输入的坐标。
	int ch = 0;
	if (x == 1)//玩家一
	{
		color(10);//淡绿色
		PrintPlayerMove(4);
	}
	if (x == 2)//玩家二
	{
		color(9);//淡蓝色
		PrintPlayerMove(5);
	}
	while (1)//开始一个无限循环,这样程序会持续等待玩家的输入,直到玩家输入有效的坐标并下棋break。
	{
		scanf("%d%d", &i, &j);
		while ((ch = getchar()) != '\n' && ch != EOF);//对玩家输入数据进行处理,防止字符造成死循环。

		if ((i >= 1 && i <= row) && (j >= 1 && j <= col))//如果坐标在棋盘范围内(即1到行数和1到列数之间),则执行以下代码:
		{
			if (Board[i - 1][j - 1] == ' ')//检查该坐标位置是否为空。如果为空,表示这个位置可以被下棋
			{
				if (x == 1)        //人人对战专用,玩家一回合传参x=1
				{
					Board[i - 1][j - 1] = 'X';
					break;//成功下棋后跳出循环
				}
				else if (x == 2)   //人人对战专用,玩家二回合传参x=2
				{
					Board[i - 1][j - 1] = '#';
					break;//成功下棋后跳出循环
				}
			}

			else
			{
				PrintPlayerMove(2);//坐标被占用提示框
			}
		}
		else//如果坐标不在棋盘范围内,重新输入。
		{
			PrintPlayerMove(3);
		}
	}
}

效果图展示
在这里插入图片描述

在这里插入图片描述
三子棋的输赢判定方式分为五种,一种是横向三个,一种是纵向三个,一种是右下倾斜(\),一种是左下倾斜(/),还有就是是否平局。也就是棋盘下满且双方皆未达成棋子三连的结果。
将框架写好,随后在for循环的内部加入判定部分即可。

//判断输赢函数
char Judgment(char Board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)//判断i行里是否有棋子连成一行的情况出现
	{
		for (j = 0; j < col - 2; j++)
		{
			if (Board[i][j] == Board[i][j + 1] && Board[i][j + 1] == Board[i][j + 2] && Board[i][j] != ' ')
				return Board[i][j];//返回这个棋子的字符
		}
	}
	for (j = 0; j < col; j++)//判断j列里是否有棋子连成一列的情况出现
	{
		for (i = 0; i < row - 2; i++)
		{
			if (Board[i][j] == Board[i + 1][j] && Board[i + 1][j] == Board[i + 2][j] && Board[i][j] != ' ')
				return Board[i][j];//返回这个棋子的字符
		}
	}
	//判断斜线上是否有三个棋子连线的情况出现 (\)
	for (i = 0; i < row - 2; i++)
	{
		for (j = 0; j < col - 2; j++)
		{
			if (Board[i][j] == Board[i + 1][j + 1] && Board[i + 1][j + 1] == Board[i + 2][j + 2] && Board[i][j] != ' ')
				return Board[i][j];//返回这个棋子的字符
		}
	}
	//判断斜线上是否有三个棋子连线的情况出现  (/)
	for (i = 0; i < row - 2; i++)
	{
		for (j = col - 1; j > 1; j--)
		{
			if (Board[i][j] == Board[i + 1][j - 1] && Board[i + 1][j - 1] == Board[i + 2][j - 2] && Board[i][j] != ' ')
				return Board[i][j];//返回这个棋子的字符
		}
	}
	//上述四种情况都未发生的情况下,说明此时无论玩家或电脑双方皆未赢,程序才会运行到此,此时只剩下继续或平局两种情况
	for (i = 0; i < row; i++)//通过遍历棋盘,判断棋盘上是否已经下满了,以此判断继续还是平局
	{
		for (j = 0; j < col; j++)
		{
			if (Board[i][j] == ' ')//如果有空格,说明没下满,游戏继续。
				return 'C';//continue
		}
	}
	return 'Q';//五种情况都未发生,平局。
}

(19)人机对战中玩家赢或电脑赢或平局的提示框函数

 无论是哪一方赢,总得有个结果,所以这里我们做一个提示框函数,作为赢方的结束动画展示。

//打印人机对战中玩家赢或电脑赢或平局的提示框(函数内部不带颜色)
void display_win_or_lost(char x)  //参数传w代表win,打印ch1[],参数传l代表lost,打印ch2[],参数传p代表平局,打印ch3[]
{
	char ch1[] =
		"__   __                          _\n"
		"\\ \\ / /                         (_)\n"
		" \\ V /   ___   _   _  __      __ _  _ __\n"
		"  \\ /  /  _ \\ | | | | \\ \\/ \\ / /| || '_ \\ \n"
		"  | |  | (_) || |_| |  \\ V  V / | || | | |\n"
		"  \\_/   \\___/  \\__,_|   \\_/\\_/  |_||_| |_|\n";

	char ch2[] =
		"__   __                   _               _\n"
		"\\ \\ / /                  | |            | |  \n"
		" \\ V /   ___   _   _     | |  ___   ___ | |_\n"
		"  \\ /  /  _ \\ | | | |    | | / _ \\ / __|| __|\n"
		"  | |  | (_)| | |_| |    | | |(_) |\\__ \\| |_ \n"
		"  \\_/   \\___/  \\__,_|    |_| \\___/ |___/ \\__|\n";

	char ch3[] =
		"                                                                                     \n"
		"   #################                             \n"
		"           #                 ###############     \n"
		"    #      #      #         ###           ##     \n"
		"    ##     #     ##         ##            ##     \n"
		"    ###    #     ##         ################     \n"
		"     ##    #    ##          ##                   \n"
		"     ###   #   ###          ##                   \n"
		"      ##   #  ###           #################    \n"
		"       ##  # ###            ##             ##    \n"
		"       ##  # ##             ##  #########  ##    \n"
		"        #  #                ##  ##     ##  ##    \n"
		"  ###################       ##  #       #  ##    \n"
		"           #                ##  #       #  ##    \n"
		"           #                ##  #       #  ##    \n"
		"           #                ##  ##     ##  ##    \n"
		"           #                ##  #########  ##    \n"
		"           #               ##            ##      \n"
		"           #               ##       ########     \n"
		"           #             ##       #######        \n";

	if (x == 'w')//参数传w代表win,打印ch1[]
	{
		for (int i = 0; i < strlen(ch1) - 1; i++)
		{
			printf("%c", ch1[i]);
			if (ch1[i] == '\n')
			{
				Sleep(50);
			}
		}
	}
	else if (x == 'l')//参数传w代表lost,打印ch2[]
	{
		for (int i = 0; i < strlen(ch2) - 1; i++)
		{
			printf("%c", ch2[i]);
			if (ch1[i] == '\n')
			{
				Sleep(50);
			}
		}
	}
	else if (x == 'p')//参数传p代表平局,打印ch3[]
	{
		for (int i = 0; i < strlen(ch3) - 1; i++)
		{
			printf("%c", ch3[i]);
			if (ch3[i] == '\n')
			{
				Sleep(10);
			}
		}
	}
}

效果图展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(20)游戏主体函数

这块是游戏的重点部分,上面写的所有函数都是为了这里做准备工作,上面所有的逻辑写好以后,我们就可以编写游戏主体函数了,按照一定次序排列,实现整个下棋逻辑。通过不同的参数判断玩家选择是人机对战或人人对战,玩家电脑依次下棋,直到有一方胜出为止,或玩家一,玩家二依次下棋,直到有一方胜出为止。

void Game(int n)//游戏主体函数
{
	char ret = 0;                    //用于接收判断输赢函数的返回值,返回的四种字符对应棋局的四种情况,定义在头文件中
	char Board[ROW][COL] = { 0 };    //定义二维数组
	InitBoard(Board, ROW, COL);      //初始化二维数组
	DisplayBoard(Board, ROW, COL);   //打印棋盘
	while (1)//循环内部为人机双方下棋过程或人人对战过程
	{
		if (n == 1)//人机对战玩家先手
			PlayerMove(Board, ROW, COL);
		if (n == 2)//人机对战电脑先手
			ComputerMove(Board, ROW, COL);
		if (n == 3) //人人对战玩家一
			Player1_VS_Player2(Board, ROW, COL, 1);

		ret = Judgment(Board, ROW, COL); //判断输赢
		if (ret != 'C')                  //等于C棋局继续,不等于C跳出循环,根据ret的值判断输赢
		{
			break;
		}
		system("cls");                   //确认落子后清屏,再打印棋盘
		DisplayBoard(Board, ROW, COL);   //打印棋盘

		if (n == 1)//人机对战电脑后手
			ComputerMove(Board, ROW, COL);
		if (n == 2)   //人机对战玩家后手
			PlayerMove(Board, ROW, COL);
		if (n == 3)  //人人对战玩家二
			Player1_VS_Player2(Board, ROW, COL, 2);

		ret = Judgment(Board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		system("cls");
		DisplayBoard(Board, ROW, COL);//打印棋盘

	}
	if (n == 1 || n == 2)//人机对战的情况下
	{
		if (ret == 'X')
		{
			display_win_or_lost('w');
		}
		else if (ret == '#')
		{
			display_win_or_lost('l');
		}
		else
		{
			color(0XA);
			display_win_or_lost('p');
		}
	}
	else    //人人对战的情况下
	{
		if (ret == 'X')
		{
			printf("\n  玩家一胜利\n");
		}
		else if (ret == '#')
		{
			printf("\n  玩家二胜利\n");
		}
		else
		{
			color(0XA);
			display_win_or_lost('p');
		}
	}
	DisplayBoard(Board, ROW, COL);//打印棋盘
}

(21)游戏说明函数

  菜单函数编写好并且游戏主体函数也编写好后,基本的功能框架算是基本完成了了,接下来我们要做的就是实现菜单函数里所希望实现的其他功能,
先实现菜单函数里的游戏说明功能。

void Introduction()
{
	printf("\n1.游戏分为人机对战和人人对战,双方依次在9宫格棋盘上摆放棋子。\n");
	printf("\n2.双方轮流落子,每次只能在一个格子上放置一个棋子。\n");
	printf("\n3.如果将自己的3个棋子连成一条线,则宣告胜利。连线的方向可以是水平、垂直或对角线。\n");
	printf("\n4.如果在棋盘上形成平局,即双方都没有机会将自己的3个棋子连成一条线,那么游戏结束。\n\n");
}

(22)开发人员菜单函数

  此函数对应主菜单界面的第4个选项开发人员,介绍了版本号,作者信息(我本人),和制作时间,背景音乐等信息,该函数可以控制程序内的音乐打开或关闭。

//开发人员菜单函数(开发人员信息和背景音乐控制)
void kaifa()
{
	int control = 0;
	do
	{
		system("color B0");
		printf(" ________________________________________________________\n"); Sleep(30);
		printf("| 智能三子棋版本v1.1       作者   --->原始豌豆           |\n");  Sleep(30);
		printf("|                                                        |\n");  Sleep(30);
		printf("| 制作时间 2024.2.11        音乐   --->纯音乐            |\n");  Sleep(30);
		printf("|________________________________________________________|\n"); Sleep(30);
		printf("**************1.开启音乐    0.关闭音乐********************\n");
		printf("**************      2.回到主界面     *********************\n");
		printf("请选择>:");
		scanf("%d", &control);
		while (getchar() != '\n');
		switch (control)
		{
		case 1:
			BackgroundMusic('s');
			break;
		case 0:
			BackgroundMusic('e');
			break;
		case 2:
			system("cls");//回到主界面前先清屏
			break;
		default:
			printf("\n          选择错误,请重新输入!");
			system("pause");//界面美化---->控制台停留,实现按任意键继续的效果
			system("cls"); //界面美化---->清屏,防止玩家多次输入错误造成屏幕占满		
			break;
		}
	} while (control != 2);
}

(23)退出游戏函数

  此函数对应主菜单界面的第5个选项退出游戏,在玩家退出游戏时打印由符号组成的退出两个汉字。

//打印退出游戏字幕
void GameOver()
{
	char str[] = "                                                                                           \n"
		"    #      ############                    ##                  \n"
		"   ###    ##############                   ##                  \n"
		"   ###    ###        ###         ##        ##        ##\n"
		"    ###   ##          ##         ##        ##        ##\n"
		"     ##     ##            ##          ##        ##         ##\n"
		"     ###  ##############         ##        ##        ##\n"
		"      ##  ##############         ##        ##        ##\n"
		"          ##          ##         ##        ##        ##\n"
		"          ##          ##         ##        ##        ##\n"
		" ######   ##############         ##        ##        ##\n"
		"  ######  ##############         ###       ##        ##\n"
		"      ##  ######                  #####################\n"
		"      ##  ## ####      ##         #################### \n"
		"      ##  ##  ####    ###        #         ##         #\n"
		"      ##  ##   ####  ####       ###        ##         ##\n"
		"      ##  ##    #######         ###        ##         ##\n"
		"      ##  ##      ####          ###        ##         ##\n"
		"      ##  ##       ###          ###        ##         ##\n"
		"      ##  ##  ###   ###         ###        ##         ##\n"
		"      ##  ## ####    ####       ###        ##         ##\n"
		"     ###  ######      ###       ###        ##         ##\n"
		"    ##### ####         ###      ###        ##         ##\n"
		"   #########                    ###        ##         ##\n"
		" ##### ###################       #######################\n"
		" ####    ##################       #####################\n\n";
	for (int i = 0; i < strlen(str) - 1; i++)
	{
		printf("%c", str[i]);
		if (str[i] == '\n')
		{
			Sleep(10);
		}
	}
}

(24)主函数

上述函数都写好后,在主函数中调用各个函数,即可完成智能三子棋游戏游戏的制作

int main()
{
	int n = 0;
	ConsoleSet();//控制台设置
	BackgroundMusic('s');
	srand((unsigned int)time(NULL));//生成随机数生成起点//time函数参数是指针,返回值是time_t类型,而srand的参数是无符号整型,所以强制类型转换一下。
	system("color 02");
	sanziqi('p');
	Sleep(2000);
	sanziqi('c');
	int input = 0;
	do {
		player_vs_computer();
		color(14);//将菜单界面设置为亮黄色。
		Menu();//打印菜单
		scanf("%d", &input);
		while (getchar() != '\n');
		system("cls");
		switch (input)
		{
		case 1:
			n = Xianshou_Houshou();
			Game(n);   //game
			goto_xy(50, 12);
			system("pause");
			Sleep(1000);
			system("cls");
			break;
		case 2:
			Game(3);
			goto_xy(50, 12);
			system("pause");
			Sleep(1000);
			system("cls");
			break;
		case 3:
			system("color 70");
			Introduction();
			system("pause");
			system("cls");
			system("color 0E");
			break;
		case 4:
			kaifa();
			system("cls");
			system("color 0E");
			break;
		case 5:
			color(8);
			GameOver();
			break;
		default:
			goto_xy(64, 16);//将输入处的光标定位在括号里面
			printf("选择错误,重新选择");
			Sleep(800);
			system("cls");
		}

	} while (input != 5);//当input==5时退出游戏
	return 0;
}

六、全部源码

后续会发出来,敬请期待

七、项目总结

  经过多次测试和调整,我们的AI在三子棋游戏中展现出了较高的智能水平。它能够在电脑先手的大多数情况下战胜人类玩家,并且具有一定的战术意识和预判能力。虽然我们的AI在三子棋游戏中已经取得了一定的成绩,但仍有很大的提升空间。未来,我们可以进一步优化评估函数和搜索算法,提高AI的智能水平和对抗性。同时,也可以尝试将本项目应用到其他类似的棋类游戏中,以检验算法的通用性和可扩展性。

全文完,感谢观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1445407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CTF--Web安全--SQL注入之Post-Union注入

一、手动POST注入实现绕过 账号密码检测 我们利用sqli-labs/Less-11靶场来进行演示&#xff1a; 我们可以看到一个登录页面 打开Less-11的根目录&#xff0c;我们打开页面的源代码(PHP实现)。 用VS-code打开文件&#xff0c;找到验证登录信息的代码行。 此形式的代码存在POST…

从零开始学howtoheap:fastbins的house_of_spirit攻击3

how2heap是由shellphish团队制作的堆利用教程&#xff0c;介绍了多种堆利用技术&#xff0c;后续系列实验我们就通过这个教程来学习。环境可参见从零开始配置pwn环境&#xff1a;从零开始配置pwn环境&#xff1a;从零开始配置pwn环境&#xff1a;优化pwn虚拟机配置支持libc等指…

《Git 简易速速上手小册》第4章:Git 与团队合作(2024 最新版)

文章目录 4.1 协作流程简介4.1.1 基础知识讲解4.1.2 重点案例&#xff1a;为 Python Web 应用添加新功能4.1.3 拓展案例 1&#xff1a;使用 CI/CD 流程自动化测试4.1.4 拓展案例 2&#xff1a;处理 Pull Request 中的反馈 4.2 使用 Pull Requests4.2.1 基础知识讲解4.2.2 重点案…

【Linux系统 04】OpenEuler配置

目录 一、镜像文件下载 二、配置静态IP 三、启动SSH连接 四、免密登录 五、安装常用软件 一、镜像文件下载 官方下载地址&#xff1a;openEuler下载 | 欧拉系统ISO镜像 | openEuler社区官网 选择一个版本&#xff0c;lopenEuler通常有两种版本&#xff1a; 创新版&…

VTK 三维场景的基本要素(相机) vtkCamera 相机的运动

相机的运动 当物体在处于静止位置时&#xff0c;相机可以在物体周围移动&#xff0c;摄取不同角度的图像 移动 移动分为相机的移动&#xff0c;和相机焦点的移动&#xff1b;移动改变了相机相对焦点的位置&#xff0c;离焦点更近或者更远&#xff1b;这样就会改变被渲染的物体…

ZigBee学习——在官方例程实现组网

✨Z-Stack版本&#xff1a;3.0.2 ✨IAR版本&#xff1a;10.10.1 ✨这篇博客是在善学坊BDB组网实验的基础上进行完善&#xff0c;并指出实现的过程中会出现的各种各样的问题&#xff01; 善学坊教程地址&#xff1a; ZigBee3.0 BDB组网实验 文章目录 一、基础工程选择二、可能遇…

耳机壳UV树脂制作耳机壳的工艺流程是什么?

使用耳机壳UV树脂制作耳机壳的工艺流程如下&#xff1a; 获取耳模&#xff1a;首先&#xff0c;需要获取用户的耳模。这通常是通过使用一种柔软的材料注入到用户的耳朵中&#xff0c;然后取出并用来制作耳机的内芯。选择UV树脂&#xff1a;接下来&#xff0c;需要选择合适的UV…

小游戏和GUI编程(6) | 基于 SFML 的井字棋

小游戏和GUI编程(6) | 基于 SFML 的井字棋 0. 简介 使用 SFML 实现井字棋(tic-tac-toe), 规划如下: 了解规则&#xff0c; 使用命令行实现(已经实现了)使用 SFML&#xff0c;提供极简的交互(预计 1 小时)制作 SVG 图像&#xff0c; 美化界面(预计 1 小时) 1. 基于命令行的实…

160基于matlab的负熵和峭度信号的盲分离

基于matlab的负熵和峭度信号的盲分离。基于峭度的FastICA算法的收敛速度要快&#xff0c;迭代次数比基于负熵的FastICA算法少四倍以上。SMSE随信噪比增大两种判据下的FastICA算法都逐渐变小&#xff0c;但是基于峭度的算法的SMSE更小&#xff0c;因此基于峭度的FastICA算法性能…

Spark MLlib

目录 一、Spark MLlib简介 &#xff08;一&#xff09;什么是机器学习 &#xff08;二&#xff09;基于大数据的机器学习 &#xff08;三&#xff09;Spark机器学习库MLlib 二、机器学习流水线 &#xff08;一&#xff09;机器学习流水线概念 &#xff08;二&#xff09…

车载电子电器架构 —— 电子电气系统控制器开发体系

车载电子电器架构 —— 电子电气系统控制器开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费…

idea:如何连接数据库

1、在idea中打开database: 2、点击 ‘’ ---> Data Source ---> MySQL 3、输入自己的账号和密码其他空白处可以不填&#xff0c;用户和密码可以在自己的mysql数据库中查看 4、最后选择自己需要用的数据库&#xff0c;点击运用ok&#xff0c;等待刷新即可 最后&#xff1a…

《CSS 简易速速上手小册》第9章:CSS 最佳实践(2024 最新版)

文章目录 9.1 维护大型项目的 CSS9.1.1 基础知识9.1.2 重点案例&#xff1a;构建一个可复用的 UI 组件库9.1.3 拓展案例 1&#xff1a;优化现有项目的 CSS 结构9.1.4 拓展案例 2&#xff1a;实现主题切换功能 9.2 BEM、OOCSS 和 SMACSS 方法论9.2.1 基础知识9.2.2 重点案例&…

【从Python基础到深度学习】3. Winscp与Ubuntu使用及配置

一、Ubuntu的使用 1.1 开启与关闭 1.2 修改Ubuntu分辨率 选择适合自己电脑大小的分辨率 1.3 Ubuntu终端 1.4 网络测试 终端中输入&#xff1a; ping www.baidu.com ctr C 退出ping命令 1.5 下载软件 连通安装源 sudo apt update 安装 ssh vim sudo apt install ss…

零基础学python之高级编程(2)---面向对象编程组合用法及其继承特性

面向对象编程组合用法及其继承特性 文章目录 面向对象编程组合用法及其继承特性前言一、面向对象编程组合用法1 直接在类中创建实例化对象2 给入参数&#xff0c;在结尾给一个类实例化传参变成对象&#xff0c;然后再在另一个类中传入对象参数 二、面向对象编程----继承1.继承定…

浅谈人工智能之深度学习~

目录 前言&#xff1a;深度学习的进展 一&#xff1a;深度学习的基本原理和算法 二&#xff1a;深度学习的应用实例 三&#xff1a;深度学习的挑战和未来发展方向 四&#xff1a;深度学习与机器学习的关系 五&#xff1a;深度学习与人类的智能交互 悟已往之不谏&#xff0…

2023年智能可穿戴行业市场分析(电商数据查询分析):智能手表销额增长21%,手环明显下滑

近年来&#xff0c;随着技术的进步&#xff0c;智能可穿戴设备在社交网络、医疗保健、导航等诸多领域有着非常广泛的应用&#xff0c;这为大众生活带来了诸多便利。 当前的可穿戴产品形态纷繁多样&#xff0c;主要包括智能手表、智能眼镜、智能手环、健康穿戴和体感控制等等&am…

Unity学习笔记(零基础到就业)|Chapter02:C#基础

Unity学习笔记&#xff08;零基础到就业&#xff09;&#xff5c;Chapter02:C#基础 前言一、复杂数据&#xff08;变量&#xff09;类型part01&#xff1a;枚举数组1.特点2.枚举&#xff08;1&#xff09;基本概念&#xff08;2&#xff09;申明枚举变量&#xff08;3&#xff…

生态位模拟——草稿笔记

文章目录 前言ENM初识一、所需软件安装1.1. 下载ArcGIS软件&#xff1a;1.2. 下载 MaxEnt软件&#xff1a;1.3. 下载ENMtools&#xff1a; 二、数据准备与处理2.1. 物种分布数据2.2. 环境因子数据2.3. 地图数据2.4. 物种分布点去冗余2.4.1. 使用spThin包中的thin函数2.4.2. 或者…

使用耳机壳UV树脂制作私模定制耳塞有什么优点和缺点呢?

使用耳机壳UV树脂制作私模定制耳塞具有以下优点&#xff1a; 个性化定制&#xff1a;UV树脂可以根据用户的耳型进行个性化定制&#xff0c;使耳塞与用户的耳朵形状完美契合&#xff0c;提高舒适度和佩戴稳定性。高强度和耐磨性&#xff1a;UV树脂具有高强度和耐磨性&#xff0…