人工智慧實現簡單的五子棋程式
我這個程式在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;
}
相關文章
- Java簡單實現無介面五子棋Java
- 實現簡單的`Blazor`低程式碼Blazor
- java簡單練習-五子棋Java
- 使用 Tcl 實現簡單的文字識別程式
- 使用 Fantom 實現簡單的文字識別程式
- 使用 Elixir 實現簡單的文字識別程式
- Rust 程式設計,實現簡單的佇列Rust程式設計佇列
- 小程式簡單實現表格佈局
- ArrayList的簡單實現
- 實現簡單的BitMap
- AOP的簡單實現
- 簡單的 HashMap 實現HashMap
- 一個簡單的區塊鏈程式碼實現區塊鏈
- html實現簡單ListViews效果的例項程式碼HTMLView
- java實現一個簡單的爬蟲小程式Java爬蟲
- 用python實現簡單的線上翻譯程式Python
- 順序審批流的簡單程式碼實現
- 簡單的python程式碼實現語音朗讀Python
- [Linux]簡單的shell實現Linux
- java實現簡單的JDBCJavaJDBC
- 簡單的實現vue原理Vue
- 簡單的實現React原理React
- AspectJ簡單實現
- FastClick簡單實現AST
- Promise 簡單實現Promise
- ReadableStream 簡單實現
- Express 簡單實現Express
- 使用 Modula-2 實現簡單的文字識別程式
- 使用 R 語言實現簡單的文字識別程式
- Proteus實現簡單51程式的設計與模擬
- 使用簡單的Java程式碼實現酒店管理系統Java
- axios簡單實現小程式延時loading指示iOS
- Unity實現簡單的物件池Unity物件
- 簡單、好懂的Svelte實現原理
- Promise 基本方法的簡單實現Promise
- netty 實現簡單的rpc呼叫NettyRPC
- Android中SharePreferences的簡單實現Android
- SpringBoot與WebService的簡單實現Spring BootWeb