本文讲述了使用确定性神经网络SDK开发神经网络五子棋的过程,通过控制台项目、MFC对话框项目分别展示了详细的操作和代码,并附带了一个可用于自主创业的实用案例。另外,此处所述的五子棋可以轻易地扩展成其它的连子棋应用。
1.控制台版本的神经网络五子棋
(1)创建项目
运行Visual Studio 2015(其它版本的VS也可以使用),选择菜单“文件-->新建-->项目”,在对话框的左侧选择“已安装-->模板-->Visual C++-->Win32”,在右侧选择“Win32控制台应用程序”,并选定项目保存的位置和项目名称(此处项目名为test,保存在桌面),如下图所示:
点击“确定”按钮,在弹出的对话框的左侧选择“应用程序设置”,右侧选择“空项目”,并取消“安全开发生命周期(SDL)检查”的选择,详细如下图所示:
点击“完成”按钮,至此完成了控制台项目的创建。
(2)新建程序文件
在解决方案资源管理器中右击test项目,在右键菜单中选择“添加-->新建项”,在弹出的对话框左侧选择“已安装-->Visual C++-->代码”,在右侧选择“C++文件”,在下方输入源代码文件的名称(本例为test.cpp),如下图所示:
点击“添加”按钮,然后即可开始编写代码。
(3)导入SDK
打开test.cpp所在的目录,将SDK文件(AIWZQDll.dll、AIWZQDll.lib、Inter.h)拷贝到该目录,如下图:
然后,在解决方案资源管理器中右击test项目,在右键菜单中选择“添加-->现有项” ,在弹出的对话框中选中Inter.h并点击“添加”按钮即可。
(4)编写代码
打开test.cpp文件,输入如下两句代码来引入SDK:
#include "Inter.h"
#pragma comment(lib, "AIWZQDll.lib")
然后添加main()函数,在该函数开头调用SDK的Login()函数如下:
void main()
{
if (!Login("user", "password"))
{
printf("登录失败\n");
getchar();
return;
}
}
其中:user和password是调用确定性神经网络所使用的用户名和密码,可以在官网www.gnxxkj.com下载客户端软件进行用户注册,并将注册的用户名和密码填至此处。
然后调用InitFromModelFile()函数加载神经网络模型:
if (!InitFromModelFile("model.mod"))
{
printf("初始化失败\n");
getchar();
return;
}
如果没有神经网络模型,也可以使用“棋盘大小”、“几连子获胜”这样的参数来调用InitWithoutModel()函数:
if (!InitWithoutModelFile(15, 15, 5))
{
printf("初始化失败\n");
getchar();
return;
}
此处表示从空模型开始,创建15x15大小棋盘上的五子棋游戏,若要创建六子棋则将函数的第三个参数从5改为6即可。注:若使用已有模型,在模型中已经包含了棋盘大小和几连子获胜这样的信息。
接下来调用StartNewGame()函数来开始一局游戏:
if (!StartNewGame())
{
printf("游戏开始失败\n");
getchar();
return;
}
该函数将初始化内存和游戏基础数据。之后,就可以循环调用SetPieceByAI()函数来落子了,每一次落子后都调用IsGameOver()函数来判断游戏是否结束:
while (!IsGameOver())
{
if (!SetPieceByAI())
{
printf("积分不足或网络问题,请确保网络畅通且积分充足(若是积分不足,充值后可继续本次对局\n");
printf("当前积分为:%d\n", GetPoint());
break;
}
Show();
getchar(); //等待用户按键盘落下一子
}
其中:SetPieceByAI()将调用神经网络的预测功能预测下一次落子位置,并在相应位置落子,然后通过Show()函数显示出来,通过getchar()函数来实现用户按一次键盘就落一子的操作。此处的Show()函数简单定义如下:
void Show()
{
int nBoardWidth = 0; //棋盘宽度
int nBoardHeight = 0; //棋盘高度
TBOARD* pData = GetBoardData(&nBoardWidth, &nBoardHeight);
if (pData == NULL || nBoardWidth < 1 || nBoardHeight < 1)
{
printf("无效数据\n");
return;
}
printf("=============================================================================\n");
for (int h = 0; h < nBoardHeight; h++)
{
for (int w = 0; w < nBoardWidth; w++)
{
switch (pData[h*nBoardWidth + w])
{
case -1: //黑子
printf("X ");
break;
case 1: //白子
printf("O ");
break;
case 0: //空白,该位置尚未落子
printf("- ");
break;
default: //无效数据
break;
}
}
printf("\n");
}
printf("===============================================================================\n");
}
该函数调用SDK函数GetBoardData()来获取当前棋局数据,并在控制台上显示出来。该Show()函数将在对话框案例中进行重写更换。
一旦IsGameOver()函数返回真,则游戏结束,此时可以调用GetWinner()函数来检测获胜者:
switch (GetWinner())
{
case -1:
printf("黑棋获胜\n");
break;
case 1:
printf("白棋获胜\n");
break;
case 0:
printf("平局\n");
break;
}
getchar();
至此,游戏主体已经结束。另外,也可以在游戏结束时调用SaveModel()函数来保存游戏中的神经网络模型,该模型在每次游戏结束后会重新训练和更新,所以会随着游戏次数的增多而不断进化;还可以调用SaveSteps()函数来保存本次对弈的数据,即本局游戏每步落子的位置、谁落的子(黑/白):
SaveModel("model.mod");
SaveSteps("data.txt");
(5)测试
运行项目,在控制台界面中不断按键盘来显示下一次落子位置,直至结束:
(6)完整代码如下:
#include "stdio.h"
#include "Inter.h"
#pragma comment(lib, "AIWZQDll.lib")
void Show()
{
int nBoardWidth = 0; //棋盘宽度
int nBoardHeight = 0; //棋盘高度
TBOARD* pData = GetBoardData(&nBoardWidth, &nBoardHeight);
if (pData == NULL || nBoardWidth < 1 || nBoardHeight < 1)
{
printf("无效数据\n");
return;
}
printf("=============================================================================\n");
for (int h = 0; h < nBoardHeight; h++)
{
for (int w = 0; w < nBoardWidth; w++)
{
switch (pData[h*nBoardWidth + w])
{
case -1: //黑子
printf("X ");
break;
case 1: //白子
printf("O ");
break;
case 0: //空白,该位置尚未落子
printf("- ");
break;
default: //无效数据
break;
}
}
printf("\n");
}
printf("===============================================================================\n");
}
void main()
{
if (!Login("user", "password"))
{
printf("登录失败\n");
getchar();
return;
}
if (!InitWithoutModelFile(15, 15, 5))
{
printf("初始化失败\n");
getchar();
return;
}
//使用模型文件初始化
//if (!InitFromModelFile("model.mod"))
//{
// printf("初始化失败\n");
// getchar();
// return;
//}
if (!StartNewGame())
{
printf("游戏开始失败\n");
getchar();
return;
}
while (!IsGameOver())
{
if (!SetPieceByAI())
{
printf("积分不足或网络问题,请确保网络畅通且积分充足(若是积分不足,充值后可继续本次对局\n");
printf("当前积分为:%d\n", GetPoint());
break;
}
Show();
getchar(); //等待用户按键盘落下一子
}
switch (GetWinner())
{
case -1:
printf("黑棋获胜\n");
break;
case 1:
printf("白棋获胜\n");
break;
case 0:
printf("平局\n");
break;
}
getchar();
SaveModel("model.mod");
SaveSteps("data.txt");
}
(7)完整的Inter.h文件如下:
typedef signed char TBOARD;
//以下函数在程序运行开始的时候调用,仅调用一次
extern "C" __declspec(dllexport) bool Login(char* strLoginName, char* strPassword); //登录
extern "C" __declspec(dllexport) bool InitFromModelFile(char* strModelFileName); //使用模型文件初始化
extern "C" __declspec(dllexport) bool InitWithoutModelFile(int nBoardWidth, int nBoardHeight, int nWinLen); //无模型文件时初始化
//以下函数在每局游戏开始的时候调用,每局游戏调用一次
extern "C" __declspec(dllexport) bool StartNewGame(); //开始游戏,并重置相关数据
//以下函数在每步落子的时候调用,每步落子调用一次
extern "C" __declspec(dllexport) bool SetPieceWithCoord(int nX, int nY); //根据坐标落子
//extern "C" __declspec(dllexport) bool SetPieceWithGUI(CStatic* pCtrlBoard, int nCursorXInCtrl, int nCursorYInCtrl); //根据界面组件及屏幕鼠标位置落子
extern "C" __declspec(dllexport) bool SetPieceByAI(void); //AI落子,该函数返回失败表示所登录的用户积分不足
//extern "C" __declspec(dllexport) bool SetPieceByAIAndShow(CStatic* pCtrlBoard); //AI落子并在界面上显示
extern "C" __declspec(dllexport) bool IsGameOver(); //游戏是否已经结束
extern "C" __declspec(dllexport) int GetWinner(); //获胜者
//extern "C" __declspec(dllexport) bool DrawBoard(CStatic* pCtrlBoard); //绘制棋盘
//extern "C" __declspec(dllexport) bool DrawPieces(CStatic* pCtrlBoard); //绘制所有棋子
//以下函数在需要的时候调用,非必须调用
extern "C" __declspec(dllexport) TBOARD* GetBoardData(int* pnBoardWidth, int* pnBoardHeight); //获得当前棋局数组的内存区首地址(及矩阵的宽和高,如果需要的话)
extern "C" __declspec(dllexport) int GetPoint();
extern "C" __declspec(dllexport) bool SaveSteps(char* strDataFileName); //保存棋局数据
extern "C" __declspec(dllexport) bool SaveModel(char* strModelFileName); //保存模型数据
//以下函数用于“五子棋水平等级评估”
extern "C" __declspec(dllexport) bool SetLayers4Pred(int nLayers); //设置用于预测的层数,与之同时,将禁止神经网络的学习进化,仅使用指定的模型进行预测,可保证公平性
该文件在控制台项目下使用和编译时需要注释掉出错的几行(出错的行是供GUI项目使用的,有GUI下的CStatic类型)。
2.MFC对话框版本的神经网络五子棋
原本打算将几个版本的案例一起写的,但是写完控制台版本的案例之后发现字数太多了,所以其它版本的五子棋将放在后续单独来写,敬请关注。
3.文中所提的SDK可在如下地址下载:
官网下载:www.gnxxkj.com
github下载:https://github.com/wangdechang119
gitlab下载:https://gitlab.com/wangdechang119
gitee下载:https://gitee.com/wangdechang119