c# 五子棋 游戏完整源码博弈树AI

五子棋博弈搜索实验报告
一 实验内容
五子棋对弈程序
二 算法、数据结构与函数设计
算法:
由于五子棋比一字棋难,规则多,又有禁手限制,无法采用简单的搜索和加分方式。
为了分清各种情况的轻重缓急,我们采用一次性打分的策略:
对棋盘上的每个空位根据情况的轻重缓急,越重越争分越高,反之越低,然后根据打分找出分值最高的的位置下子。
打分事实根据有:是否赢棋(最高),对方是否要赢棋,是否禁手,可形成四连的个数,和形成三连(包括活三)的个数,对方可可形成四连的个数,和形成三连(包括活三)的个数,等等。
本算法的好处在于,既有攻又有守,而且攻守较严。
数据结构:
根据算法的一般要求以及实际问题需要,为了方便操作,我们用二维数组(int)表示棋盘:
chessBoard [15, 15]
其中1表示黑子,-1表示白子,0表示无子。这样定义方便轮转棋子的颜色。
为了方便悔棋,我们定义了一个专门用于悔棋的内部类:
public class StepPoint//下棋点,用于回溯
{
public int x;
public int y;
public int color;// 其中1表示黑子,-1表示白子,0表示无子。
};
public Stack backStack = new Stack();//悔棋用的。
判断一局棋是不是结束用一个bool变量:bool end;
函数:
根据算法的要求和实际问题的需要,我们定义了以下几个函数:
//求color色棋在(x,y)点的权重
private int pointWeight(int color, int x, int y)

    //寻找最好的10个下棋点,如有多个最大值,则采用随机方式给出。找不到就返回false
    private bool searchBestPoint(int color,ref int x, ref int y)

    //(x,y)处放color色棋后是否能在(x1,y1)和(x2,y2)之间形成活count且不被另一色棋时不被破坏
    private bool breakLiveCount(int color, int count, int x, int y, int x1, int y1, int x2, int y2)

    //求两点(x1,y1),(x2,y2)间最长的color色连的棋的数目
    private int linkCount(int color, int x1, int y1, int x2, int y2)

    //判断两点(x1,y1),(x2,y2)间是否存在活的count(0~5)
    private bool liveCount(int color, int count, int x1, int y1, int x2, int y2)

    //判断点(x,y)放入黑棋后是否形成长连(6连)
    private bool hasLongLink(int x, int y)

	此外,还有其他界面函数等等。

三 打分(权重)算法设计与实现
在本算法中,最重要的就是打分了。也就是int pointWeight(int color, int x, int y)函数的实现。由于黑棋有禁手而白棋无禁手,故两种棋的打分策略略有不同,当电脑为黑子时,多攻少守,当电脑为白子时,多守少攻。具体如下:

int[] colorLink = new int[6];//下棋方棋连数从0~5(由下标值表示)
int[] colorLive = new int[6];//下棋方活连棋数从0~5(由下标值表示)
int[] fcolorLink = new int[6];//对手方棋连数从0~5(由下标值表示)
int[] fcolorLive = new int[6];//对手方活棋连数从0~5(由下标值表示)

if (color == 1)//如果是下棋方是黑子,有禁手
{
      if ( hasLongLink(x, y))//对不起,长连
           return -2;//禁手
      else if (colorLink[5] > 0)//自己可以形成5
           return 150000;
      else if (colorLive[3] > 1 || colorLive[4] + colorLink[4] > 1)//四四,活三三,都是禁手
            return -1;
      else if (fcolorLink[5] > 0)//破坏对方的5
            return 140000;
      else if (colorLive[4] == 1)//可以形成一个活四
            return 130000;
      else if (colorLink[4] == 1 && colorLive[3] == 1)//可以形成一个四三
            return 120000;
      else if (colorLink[4] == 1 && colorLink[3] > 0)
            return 110000;
      //防守式战略
      else if (fcolorLive[4] > 0 || fcolorLink[4] > 1)
            return 100000;
      else if (fcolorLink[4] == 1 && tempLive3 == 1)
            return 90000;
      else if (fcolorLive[3] > 1)
            return 80000;
      else if (fcolorLink[4] == 1 && fcolorLink[3] > 0)
            return 70000;
      else
      {
            totalWeight = (colorLink[4] + colorLive[3]) * 6250 + (colorLink[3] + colorLive[2] + fcolorLink[4] + fcolorLive[3]) * 1250+ (colorLink[2] + fcolorLink[3] + fcolorLive[2]) * 250 + colorLive[1] * 50 + (colorLink[1] + fcolorLink[2] + fcolorLive[1]) * 10 + fcolorLink[1] * 2;
            return totalWeight;
      }
}
else//如果是下棋方是白子,考虑黑子的禁手
{
      bool isBlackForbiden = (tempLive3 > 1 || colorLive[4] > 1 || hasLongLink(x, y));//黑棋有禁手
      bool isBlackLongLink = hasLongLink(x, y);//黑手有长连禁手
      if (colorLink[5] > 0)//自已可以形成5
            return 150000;
      else if (fcolorLink[5] > 0 && !isBlackLongLink)//对方可以形成5,且没有长连禁手限制
            return 140000;
      //进攻与防守相结合的战略
      else if (colorLive[4] > 0 || fcolorLink[4] > 1)//自己可以形成活四,或者对方可以形成两个四连
            return 130000;
      else if (colorLink[4] == 1 && fcolorLive[3] > 0)
            return 120000;
      else if (fcolorLive[4] == 1 && !isBlackForbiden || colorLink[4] > 1 && !isBlackForbiden)
            return 110000;
      else if (colorLink[4] == 1 && fcolorLink[3] > 0)
            return 100000;
      else if (fcolorLink[4] > 0 && tempLive3 == 1 && !isBlackForbiden)
             return 90000;
      else if (colorLive[3] > 1)
             return 80000;
       else if (fcolorLink[4] > 0 && colorLink[3] > 0 && !isBlackForbiden)
             return 70000;
       else
       {
             totalWeight = (colorLink[4] + colorLive[3]) * 6250 + (colorLink[3] + colorLive[2] + fcolorLink[4] + fcolorLive[3]) * 1250+ (colorLink[2] + fcolorLink[3] + fcolorLive[2]) * 250 + colorLive[1] * 50 + (colorLink[1] + fcolorLink[2] + fcolorLive[1]) * 10 + fcolorLink[1] * 2;
             return totalWeight;
}

四 测试方法及测试结果
(电脑先走):
第一轮:

第二轮:

从结果可以看出:人一不小心就会输给电脑,但要赢电脑却要精心策算,步数很多。而且电脑采取最高分随机策略,同一棋盘棋形有可能下出不同的步子,人不能猜测。
(玩家先走):
第三轮:

第四轮:

从结果可以看出:当电脑是白子时,电脑采用多守少攻的策略。
六 实验心得
我们在试验中遇到了不少的问题,但是经过大家的努力和仔细分析调试,终于使实验取得成功。整个过程中我们遇到的主要问题以及吸取到的一些经验教训如下:

  1. 采用打分算法有一个缺点,就是不能进像极大极小和α-β搜索那样进行行更深度的搜索,
    因为该点的权重只对当前棋盘的棋形有效,当对方下了一子后,棋盘棋形变了,该点的权重有可能从原来的最好(也就是最急)变成最差的。
    总体来说,我们的这次实验还是进行的很成功的,这跟团队合作以及个人能力都是分不开的。实验过程令人受益匪浅。
  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
// FiveChess.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "FiveChess.h" #include "MainFrm.h" #include "FiveChessDoc.h" #include "FiveChessView.h" #include "afxsock.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //////////////////////////////////////////////////////////////////////////// // CFiveChessApp BEGIN_MESSAGE_MAP(CFiveChessApp, CWinApp) //{{AFX_MSG_MAP(CFiveChessApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard file based document commands ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) // Standard print setup command ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CFiveChessApp construction CFiveChessApp::CFiveChessApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } ///////////////////////////////////////////////////////////////////////////// // The one and only CFiveChessApp object CFiveChessApp theApp; ///////////////////////////////////////////////////////////////////////////// // CFiveChessApp initialization BOOL CFiveChessApp::InitInstance() { AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif // Change the registry key under which our settings are stored. // TODO: You should modify this string to be something appropriate // such as the name of your company or organization. SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(); // Load standard INI file options (including MRU) // Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views. CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CFiveChessDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CFiveChessView)); AddDocTemplate(pDocTemplate); // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line if (!ProcessShellCommand(cmdInfo)) return FALSE; // The one and only window has been initialized, so show and update it. m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) // No message handlers //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() // App command to run the dialog void CFiveChessApp::OnAppAbout() { CAboutDlg aboutDlg; aboutDlg.DoModal(); } ///////////////////////////////////////////////////////////////////////////// // CFiveChessApp message handlers
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值