人工智能实现简单的五子棋程序

我这个程序在QQ五子棋上实验了一下,结果是黑棋先手全胜,白棋后手的胜率却惨不忍睹,原因有下:
1、五子棋的先手是有优势的,所以职业比赛里都会有禁手来实现公平
2、水平有限,对局面的评估只有一小部分

主要思路就是博弈树,程序向前看一步,对对手可能的走法进行评估,评估的策略主要有两条:
1、扫描整个棋盘,判断出现了多少个三子成线并且两端未堵的情况,对方每出现一种该情况,评估大幅下降,因为这几乎是必堵的,反之评估大幅上升,程序追求的就是这种情况;
2、判断出现了多少个两子成线的情况,这种情况的评估值比上一种要少
此外还有一个几乎是没用的判断,是我从九宫格的评估方案上“拿”过来的,用两种颜色的棋子分别填满棋盘,判断出现了多少个五子成龙的情况,不过很鸡肋

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>
#include <set>
#include <math.h>
#include <algorithm>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
int changex=8,changey=8;
struct Node
{
    int A[16][16];
    int f;
};
void mcopy(int des[16][16],int src[16][16])
{
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            des[i][j]=src[i][j];
}
bool check(int flag,int tmp[16][16])
{
    int fflag;
    if(flag==0)
        fflag=1;
    else
        fflag=0;
    //棋谱1:四子连线,端点都在
    for(int i=1; i<=15; ++i) //水平方向
        for(int j=1; j<=10; ++j)
        {
            if(tmp[i][j]!=fflag&&tmp[i][j+1]==flag&&tmp[i][j+2]==flag&&tmp[i][j+3]==flag&&tmp[i][j+4]==flag&&tmp[i][j+5]!=fflag)
                return true;
        }
    for(int i=1; i<=10; ++i) //垂直方向
        for(int j=1; j<=15; ++j)
        {
            if(tmp[i][j]!=fflag&&tmp[i+1][j]==flag&&tmp[i+2][j]==flag&&tmp[i+3][j]==flag&&tmp[i+4][j]==flag&&tmp[i+5][j]!=fflag)
                return true;
        }
    for(int i=1; i<=10; ++i) //正斜方向
        for(int j=1; j<=10; ++j)
        {
            if(tmp[i][j]!=fflag&&tmp[i+1][j+1]==flag&&tmp[i+2][j+2]==flag&&tmp[i+3][j+3]==flag&&tmp[i+4][j+4]==flag&&tmp[i+5][j+5]!=fflag)
                return true;
        }
    for(int i=1; i<=10; ++i) //反斜方向
        for(int j=15; j>=6; --j)
        {
            if(tmp[i][j]!=fflag&&tmp[i+1][j-1]==flag&&tmp[i+2][j-2]==flag&&tmp[i+3][j-3]==flag&&tmp[i+4][j-4]==flag&&tmp[i+5][j-5]!=fflag)
                return true;
        }
    //棋谱2:最简单的五子成型
    for(int i=1; i<=15; ++i) //水平方向
        for(int j=1; j<=11; ++j)
        {
            if(tmp[i][j]==flag&&tmp[i][j+1]==flag&&tmp[i][j+2]==flag&&tmp[i][j+3]==flag&&tmp[i][j+4]==flag)
                return true;
        }
    for(int i=1; i<=11; ++i) //垂直方向
        for(int j=1; j<=15; ++j)
        {
            if(tmp[i][j]==flag&&tmp[i+1][j]==flag&&tmp[i+2][j]==flag&&tmp[i+3][j]==flag&&tmp[i+4][j]==flag)
                return true;
        }
    for(int i=1; i<=11; ++i) //正斜方向
        for(int j=1; j<=11; ++j)
        {
            if(tmp[i][j]==flag&&tmp[i+1][j+1]==flag&&tmp[i+2][j+2]==flag&&tmp[i+3][j+3]==flag&&tmp[i+4][j+4]==flag)
                return true;
        }
    for(int i=1; i<=11; ++i) //反斜方向
        for(int j=15; j>=5; --j)
        {
            if(tmp[i][j]==flag&&tmp[i+1][j-1]==flag&&tmp[i+2][j-2]==flag&&tmp[i+3][j-3]==flag&&tmp[i+4][j-4]==flag)
                return true;
        }
    return false;
}
int h(int flag,int A[16][16])
{
    int tmp[16][16];
    mcopy(tmp,A);
    int fflag;
    if(flag==1)
        fflag=0;
    else
        fflag=1;
    int val=0;
    //出现没有堵住的三子,对己方不利的评价要很高
    for(int i=1; i<=15; ++i) //水平方向
        for(int j=1; j<=11; ++j)
        {
            if(tmp[i][j]!=flag&&tmp[i][j+1]==fflag&&tmp[i][j+2]==fflag&&tmp[i][j+3]==fflag&&tmp[i][j+4]!=flag)
                val+= -1000;
            if(tmp[i][j]!=fflag&&tmp[i][j+1]==flag&&tmp[i][j+2]==flag&&tmp[i][j+3]==flag&&tmp[i][j+4]!=fflag)
                val+= 1000;
            /*if(tmp[i][j]!=flag&&tmp[i][j+1]==fflag&&tmp[i][j+2]!=flag&&tmp[i][j+3]==fflag&&tmp[i][j+4]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i][j+1]==flag&&tmp[i][j+2]!=fflag&&tmp[i][j+3]==flag&&tmp[i][j+4]!=fflag)
                val+= 100;*/
        }
    for(int i=1; i<=11; ++i) //垂直方向
        for(int j=1; j<=15; ++j)
        {
            if(tmp[i][j]!=flag&&tmp[i+1][j]==fflag&&tmp[i+2][j]==fflag&&tmp[i+3][j]==fflag&&tmp[i+4][j]!=flag)
                val+= -1000;
            if(tmp[i][j]!=fflag&&tmp[i+1][j]==flag&&tmp[i+2][j]==flag&&tmp[i+3][j]==flag&&tmp[i+4][j]!=fflag)
                val+= 1000;
            /*if(tmp[i][j]!=flag&&tmp[i+1][j]==fflag&&tmp[i+2][j]!=flag&&tmp[i+3][j]==fflag&&tmp[i+4][j]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i+1][j]==flag&&tmp[i+2][j]!=fflag&&tmp[i+3][j]==flag&&tmp[i+4][j]!=fflag)
                val+= 100;*/
        }
    for(int i=1; i<=11; ++i) //正斜方向
        for(int j=1; j<=11; ++j)
        {
            if(tmp[i][j]!=flag&&tmp[i+1][j+1]==fflag&&tmp[i+2][j+2]==fflag&&tmp[i+3][j+3]==fflag&&tmp[i+4][j+4]!=flag)
                val+= -1000;
            if(tmp[i][j]!=fflag&&tmp[i+1][j+1]==flag&&tmp[i+2][j+2]==flag&&tmp[i+3][j+3]==flag&&tmp[i+4][j+4]!=fflag)
                val+= 1000;
            /*if(tmp[i][j]!=flag&&tmp[i+1][j+1]==fflag&&tmp[i+2][j+2]!=flag&&tmp[i+3][j+3]==fflag&&tmp[i+4][j+4]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i+1][j+1]==flag&&tmp[i+2][j+2]!=fflag&&tmp[i+3][j+3]==flag&&tmp[i+4][j+4]!=fflag)
                val+= 100;*/
        }
    for(int i=1; i<=11; ++i) //反斜方向
        for(int j=15; j>=5; --j)
        {
            if(tmp[i][j]!=flag&&tmp[i+1][j-1]==fflag&&tmp[i+2][j-2]==fflag&&tmp[i+3][j-3]==fflag&&tmp[i+4][j-4]!=flag)
                val+= -1000;
            if(tmp[i][j]!=fflag&&tmp[i+1][j-1]==flag&&tmp[i+2][j-2]==flag&&tmp[i+3][j-3]==flag&&tmp[i+4][j-4]!=fflag)
                val+= 1000;
            /*if(tmp[i][j]!=flag&&tmp[i+1][j-1]==fflag&&tmp[i+2][j-2]!=flag&&tmp[i+3][j-3]==fflag&&tmp[i+4][j-4]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i+1][j-1]==flag&&tmp[i+2][j-2]!=fflag&&tmp[i+3][j-3]==flag&&tmp[i+4][j-4]!=fflag)
                val+= 100;*/
        }
    //出现未堵住的两子,评价次之
    for(int i=1; i<=15; ++i) //水平方向
        for(int j=1; j<=12; ++j)
        {
            if(tmp[i][j]!=flag&&tmp[i][j+1]==fflag&&tmp[i][j+2]==fflag&&tmp[i][j+3]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i][j+1]==flag&&tmp[i][j+2]==flag&&tmp[i][j+3]!=fflag)
                val+= 100;
        }
    for(int i=1; i<=12; ++i) //垂直方向
        for(int j=1; j<=15; ++j)
        {
            if(tmp[i][j]!=flag&&tmp[i+1][j]==fflag&&tmp[i+2][j]==fflag&&tmp[i+3][j]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i+1][j]==flag&&tmp[i+2][j]==flag&&tmp[i+3][j]!=fflag)
                val+= 100;
        }
    for(int i=1; i<=12; ++i) //正斜方向
        for(int j=1; j<=12; ++j)
        {
            if(tmp[i][j]!=flag&&tmp[i+1][j+1]==fflag&&tmp[i+2][j+2]==fflag&&tmp[i+3][j+3]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i+1][j+1]==flag&&tmp[i+2][j+2]==flag&&tmp[i+3][j+3]!=fflag)
                val+= 100;
        }
    for(int i=1; i<=12; ++i) //反斜方向
        for(int j=15; j>=4; --j)
        {
            if(tmp[i][j]!=flag&&tmp[i+1][j-1]==fflag&&tmp[i+2][j-2]==fflag&&tmp[i+3][j-3]!=flag)
                val+= -100;
            if(tmp[i][j]!=fflag&&tmp[i+1][j-1]==flag&&tmp[i+2][j-2]==flag&&tmp[i+3][j-3]!=fflag)
                val+= 100;
        }
    /*if(val!=0)
    {
        return val;
    }*/
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(tmp[i][j]==8)
                tmp[i][j]=flag;
    int sum1=0;
    for(int i=1; i<=15; ++i) //水平方向
        for(int j=1; j<=11; ++j)
            if(tmp[i][j]==flag&&tmp[i][j+1]==flag&&tmp[i][j+2]==flag&&tmp[i][j+3]==flag&&tmp[i][j+4]==flag)
                sum1++;
    for(int i=1; i<=11; ++i) //垂直方向
        for(int j=1; j<=15; ++j)
            if(tmp[i][j]==flag&&tmp[i+1][j]==flag&&tmp[i+2][j]==flag&&tmp[i+3][j]==flag&&tmp[i+4][j]==flag)
                sum1++;
    for(int i=1; i<=11; ++i) //正斜方向
        for(int j=1; j<=11; ++j)
            if(tmp[i][j]==flag&&tmp[i+1][j+1]==flag&&tmp[i+2][j+2]==flag&&tmp[i+3][j+3]==flag&&tmp[i+4][j+4]==flag)
                sum1++;
    for(int i=1; i<=11; ++i) //反斜方向
        for(int j=15; j>=5; --j)
            if(tmp[i][j]==flag&&tmp[i+1][j-1]==flag&&tmp[i+2][j-2]==flag&&tmp[i+3][j-3]==flag&&tmp[i+4][j-4]==flag)
                sum1++;
    mcopy(tmp,A);
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(tmp[i][j]==8)
                tmp[i][j]=fflag;
    int sum0=0;
    for(int i=1; i<=15; ++i) //水平方向
        for(int j=1; j<=11; ++j)
            if(tmp[i][j]==fflag&&tmp[i][j+1]==fflag&&tmp[i][j+2]==fflag&&tmp[i][j+3]==fflag&&tmp[i][j+4]==fflag)
                sum0++;
    for(int i=1; i<=11; ++i) //垂直方向
        for(int j=1; j<=15; ++j)
            if(tmp[i][j]==fflag&&tmp[i+1][j]==fflag&&tmp[i+2][j]==fflag&&tmp[i+3][j]==fflag&&tmp[i+4][j]==fflag)
                sum0++;
    for(int i=1; i<=11; ++i) //正斜方向
        for(int j=1; j<=11; ++j)
            if(tmp[i][j]==fflag&&tmp[i+1][j+1]==fflag&&tmp[i+2][j+2]==fflag&&tmp[i+3][j+3]==fflag&&tmp[i+4][j+4]==fflag)
                sum0++;
    for(int i=1; i<=11; ++i) //反斜方向
        for(int j=15; j>=5; --j)
            if(tmp[i][j]==fflag&&tmp[i+1][j-1]==fflag&&tmp[i+2][j-2]==fflag&&tmp[i+3][j-3]==fflag&&tmp[i+4][j-4]==fflag)
                sum0++;
    return val+sum1-sum0;
}
int move1(int A[16][16])
{
    int tmp[16][16],num,ans=INF;
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(A[i][j]==8)
            {
                mcopy(tmp,A);
                tmp[i][j]=0; //对手下
                if(check(0,tmp))
                    num=-INF;
                else
                    num=h(1,tmp);
                //cout<<num<<endl;
                ans=min(ans,num);
            }
    return ans;
}
int move0(int A[16][16])
{
    int tmp[16][16],num,ans=INF;
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(A[i][j]==8)
            {
                mcopy(tmp,A);
                tmp[i][j]=1; //对手下
                if(check(1,tmp))
                    num=-INF;
                else
                    num=h(0,tmp);
                //cout<<num<<endl;
                ans=min(ans,num);
            }
    return ans;
}
void solve1(Node &node)
{
    int tmp[16][16],f,ans=-INF,anst[16][16]= {};
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(node.A[i][j]==8)
            {
                mcopy(tmp,node.A);
                tmp[i][j]=1; //自己下
                if(check(1,tmp))
                {
                    f=move1(tmp);
                    if(f!=-INF)
                        f=INF;
                }
                else
                    f=move1(tmp);
                //cout<<f<<endl;
                if(f>ans)
                {
                    //cout<<f<<endl;
                    ans=f;
                    mcopy(anst,tmp);
                }
            }
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(node.A[i][j]!=anst[i][j])
            {
                changex=i;
                changey=j;
                break;
            }
    mcopy(node.A,anst);
    node.f=ans;
    cout<<ans<<endl;
}
void solve0(Node &node)
{
    int tmp[16][16],f,ans=-INF,anst[16][16]= {};
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(node.A[i][j]==8)
            {
                mcopy(tmp,node.A);
                tmp[i][j]=0; //自己下
                if(check(0,tmp))
                {
                    f=move0(tmp);
                    if(f!=-INF)
                        f=INF;
                }
                else
                    f=move0(tmp);
                if(f>ans)
                {
                    //cout<<f<<endl;
                    ans=f;
                    mcopy(anst,tmp);
                }
            }
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            if(node.A[i][j]!=anst[i][j])
            {
                changex=i;
                changey=j;
                break;
            }
    mcopy(node.A,anst);
    node.f=ans;
    cout<<ans<<endl;
}
void show(int A[16][16])
{
    int sum=0;
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            sum+=A[i][j];
    if(sum==0)
        cout<<"无法继续!"<<endl;
    else
    {
        for(int i=0; i<=15; ++i)
        {
            for(int j=0; j<=15; ++j)
            {
                if(i==0)
                {
                    cout<<j<<" ";
                    if(j==0)
                        cout<<" ";
                }
                else if(j==0)
                {
                    cout<<i<<" ";
                    if(i<10)
                        cout<<" ";
                }
                else if((i==4&&j==4)||(i==4&&j==12)||(i==12&&j==4)||(i==12&&j==12)||(i==changex&&j==changey))
                {
                    if(A[i][j]==8)
                        cout<<".<";
                    else
                        cout<<A[i][j]<<"<";
                }
                else
                {
                    if(A[i][j]==8)
                        cout<<". ";
                    else
                        cout<<A[i][j]<<" ";
                }
                if(j>9&&i!=0)
                    cout<<" ";
            }
            cout<<endl;
        }
        cout<<endl;
    }
}
int main()
{
    int flag=1,myturn,first=1;
    int i,j;
    Node node;
    for(int i=1; i<=15; ++i)
        for(int j=1; j<=15; ++j)
            node.A[i][j]=8;
    cout<<"程序执黑棋(1)or白棋(0)?";
    cin>>myturn;
    while(1)
    {
        if(flag)
        {
            if(flag==myturn)
            {
                if(first)//人工智能第一次不会走中间,因为中间范围的评价相同,所以第一步要强制下在中间
                {
                    node.A[8][8]=1;
                    first=0;
                }
                else
                    solve1(node);
            }
            else
            {
                cout<<"对方的选择是?";
                cin>>i>>j;
                node.A[i][j]=1;
            }
            show(node.A);
            flag=0;
        }
        else
        {
            if(flag==myturn)
            {
                if(first)
                {
                    node.A[i-1][j-1]=0;
                    first=0;
                }
                else
                    solve0(node);
            }
            else
            {
                cout<<"对方的选择是?";
                cin>>i>>j;
                node.A[i][j]=0;
            }
            show(node.A);
            flag=1;
        }
    }
    return 0;
}

======================================================================== MICROSOFT FOUNDATION CLASS LIBRARY : fir ======================================================================== AppWizard has created this fir application for you. This application not only demonstrates the basics of using the Microsoft Foundation classes but is also a starting point for writing your application. This file contains a summary of what you will find in each of the files that make up your fir application. fir.dsp This file (the project file) contains information at the project level and is used to build a single project or subproject. Other users can share the project (.dsp) file, but they should export the makefiles locally. fir.h This is the main header file for the application. It includes other project specific headers (including Resource.h) and declares the CFirApp application class. fir.cpp This is the main application source file that contains the application class CFirApp. fir.rc This is a listing of all of the Microsoft Windows resources that the program uses. It includes the icons, bitmaps, and cursors that are stored in the RES subdirectory. This file can be directly edited in Microsoft Visual C++. fir.clw This file contains information used by ClassWizard to edit existing classes or add new classes. ClassWizard also uses this file to store information needed to create and edit message maps and dialog data maps and to create prototype member functions. res\fir.ico This is an icon file, which is used as the application's icon. This icon is included by the main resource file fir.rc. res\fir.rc2 This file contains resources that are not edited by Microsoft Visual C++. You should place all resources not editable by the resource editor in this file. ///////////////////////////////////////////////////////////////////////////// For the main frame window: MainFrm.h, MainFrm.cpp These files contain the frame class CMainFrame, which is derived from CFrameWnd and controls all SDI frame features. ///////////////////////////////////////////////////////////////////////////// AppWizard creates one document type and one view: firDoc.h, firDoc.cpp - the document These files contain your CFirDoc class. Edit these files to add your special document data and to implement file saving and loading (via CFirDoc::Serialize). firView.h, firView.cpp - the view of the document These files contain your CFirView class. CFirView objects are used to view CFirDoc objects. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named fir.pch and a precompiled types file named StdAfx.obj. Resource.h This is the standard header file, which defines new resource IDs. Microsoft Visual C++ reads and updates this file. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" to indicate parts of the source code you should add to or customize. If your application uses MFC in a shared DLL, and your application is in a language other than the operating system's current language, you will need to copy the corresponding localized resources MFC42XXX.DLL from the Microsoft Visual C++ CD-ROM onto the system or system32 directory, and rename it to be MFCLOC.DLL. ("XXX" stands for the language abbreviation. For example, MFC42DEU.DLL contains resources translated to German.) If you don't do this, some of the UI elements of your application will remain in the language of the operating system. /////////////////////////////////////////////////////////////////////////////
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值