代码非自创 来源:CSDN“suzhucong”的《五子棋人机对战完整代码》博客

网址:https://blog.csdn.net/nameofcsdn/article/details/78640511

以下代码略有改编 (77143号AI为本人自编,没有借鉴任何代码,该人机不会有意下禁手,不过其可能会无意下到禁手棋,请谅解)

#include <stdio.h>
#include<string>
#include<cstring>
#include<cmath>
#include<windows.h>
 
#define N 15
#define same_u_i same(row + dx[u] * i, col + dy[u] * i, p[row][col])//u方向i距离的点是否同色
#define OutOrNotEmpty (!inboard(row + dx[u] * i, col + dy[u] * i) || p[row + dx[u] * i][col + dy[u] * i] != 0) //出了棋盘或者非空格点
 
int p[N + 2][N + 2]; //0空1黑2白  1●2○ -1▲-2△
int s = 0, ais = 1, s0,startm;//s是轮到谁下,s=1,2,s=1是ai下,s=2是玩家,s=s0是黑方下,否则是白方下
bool is_end = false;
int dx[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; //flat技术
int dy[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };//(dx,dy)是8个方向向量
int manu[2][300], manukey = 0;//棋谱
char AIMOD[100];
int aimod;
long long aif[5]={0,4000,10000,1000000,10000000000};
long long wjf[5]={0,1,5001,290000,40000000};
 
int out(int i, int j)//打印棋盘
{
    if (p[i][j] == 1)return printf("-●");
    if (p[i][j] == 2)return printf("-○");
    if (p[i][j] == -1)return printf("-▲");
    if (p[i][j] == -2)return printf("-△");
    if (i == N)
    {
        if (j == 1)return printf("┏-");
        if (j == N)return printf("-┓");
        return printf("-┯-");
    }
    if (i == 1)
    {
        if (j == 1)return printf("┗-");
        if (j == N)return printf("-┛");
        return printf("-┷-");
    }
    if (j == 1)return printf("┠-");
    if (j == N)return printf("-┨");
    return printf("-┼-");
}
 
void DrawBoard()//打印整个游戏界面
{
    system("cls");
    int row = 0, col = 0, keyr = 0, keyc = 0;
    char alpha = 'A';
    printf("\n\n\n     ");
    for (col = 1; col <= N; col++)printf("%c  ", alpha++);
    for (row = N; row >= 1; row--)
    {
        printf("\n   %2d", row);
        for (col = 1; col <= N; col++)
        {
            out(row, col);
            if (p[row][col] < 0)keyr = row, keyc = col;
        }
        printf("%d", row);
    }
    alpha = 'A';
    printf("\n      ");
    for (col = 1; col <= N; col++)printf("%c  ", alpha++);
    printf("\n\n");
    if (s0 == ais)printf("  AI执黑,玩家执白\n");
    else printf("  AI执白,玩家执黑\n");
    alpha = 'A';
    if (keyr)printf("  最后落子位置:%c%d\n", alpha + keyc - 1, keyr);
}


 
void init()//游戏开局初始化
{
	//system("cls");
    system("color f0");
    printf("输入1或者2进行选择\n1,AI执黑先行\n2,玩家执黑先行\n");
    scanf("%d", &s);
    if (s != 1 && s != 2)return init();
    printf("请输入AI版本。\n");
    //scanf("%s",&AIMOD);
    //printf("%s",AIMOD);
    scanf("%d",&aimod);
    if(aimod!=0 && aimod!=77143) return init();
	//if(AIMOD!="ccD" && AIMOD!="Zcym")return init();
    s0 = s;
    startm=s;
    int i, j;
    for (i = 0; i <= N + 1; i++)for (j = 0; j <= N + 1; j++)p[i][j] = 0;//以空格包围棋盘	
    DrawBoard();
    for (j = 0; j < 300; j++)manu[0][j] = manu[1][j] = 0;
}
 
bool inboard(int row, int col)//判断(row,col)是否在棋盘内
{
    if (row <1 || row > N)return false;
    return col >= 1 && col <= N;
}
 
int same(int row, int col, int key)//判断2个棋子是否同色
{
    if (!inboard(row, col))return false;
    return (p[row][col] == key || p[row][col] + key == 0);
}
 
int num(int row, int col, int u)//坐标(row,col),方向向量u,返回该方向有多少连续同色棋子
{
    int i = row + dx[u], j = col + dy[u], sum = 0, ref = p[row][col];
    if (ref == 0)return 0;
    while (same(i, j, ref))sum++, i += dx[u], j += dy[u];
    return sum;
}
 
int live4(int row, int col)//落子成活4的数量
{
    int sum = 0, i, u;
    for (u = 0; u < 4; u++)//4个方向,判断每个方向是否落子就成活4
    {
        int sumk = 1;
        for (i = 1; same_u_i; i++)sumk++;
        if(OutOrNotEmpty)continue;
        for (i = -1; same_u_i; i--)sumk++;
        if(OutOrNotEmpty)continue;
        if (sumk == 4)sum++;
    }
    return sum;
}
 
int cheng5(int row, int col)//成5点的数量
{
    int sum = 0, i, u;
    for (u = 0; u < 8; u++)//8个成五点的方向
    {
        int  sumk = 0;
        bool flag = true;
        for (i = 1; same_u_i || flag; i++)
        {
            if (!same_u_i)//该方向的第一个不同色的点,超出边界或者对方棋子或空格
            {
                if (p[row + dx[u] * i][col + dy[u] * i])sumk -= 10;//该方向的第一个不同色的点是对方棋子,没有成五点
                flag = false;
            }
            sumk++;
        }
        if (!inboard(row + dx[u] * --i, col + dy[u] * i))continue;//该方向的第一个不同色的点是超出边界,没有成五点
        for (i = -1; same_u_i; i--)sumk++;
        if (sumk == 4)sum++;
    }
    return sum;
}
 
int chong4(int row, int col)//冲4的数量
{
    return cheng5(row, col) - live4(row, col) * 2;
}
 
int live3(int row, int col)//落子成活3的数量
{
    int key = p[row][col], sum = 0, i, u,flag=2;
    for (u = 0; u < 4; u++)//三连的活三
    {
        int sumk = 1;
        for (i = 1; same_u_i; i++)sumk++;
        if(OutOrNotEmpty)continue;
        i++;
        if(OutOrNotEmpty)flag--;
        for (i = -1; same_u_i; i--)sumk++;
        if(OutOrNotEmpty)continue;
        i--;
        if(OutOrNotEmpty)flag--;
        if (sumk == 3 && flag>0)sum++;
    }
    for (u = 0; u < 8; u++)//8个方向,每个方向最多1个非三连的活三
    {
        int  sumk = 0;
        bool flag = true;
        for (i = 1; same_u_i || flag; i++)//成活四点的方向
        {
            if (!same_u_i)
            {
                if (flag&&p[row + dx[u] * i][col + dy[u] * i])sumk -= 10;
                flag = false;
            }
            sumk++;
        }
        if(OutOrNotEmpty)continue;;
        if (p[row + dx[u] * --i][col + dy[u] * i] == 0)continue;
        for (i = 1; same_u_i; i++)sumk++;
        if(OutOrNotEmpty)continue;;
        if (sumk == 3)sum++;
    }
    return sum;
}
 
bool overline(int row, int col)//长连禁手
{
    for (int u = 0; u < 4; u++)if (num(row, col, u) + num(row, col, u + 4) > 4)return true;
    return false;
}
 
bool ban(int row, int col)//判断落子后是否成禁手
{
    if (same(row, col, 2))return false;//白方无禁手
    return live3(row, col) > 1 || overline(row, col) || live4(row, col) + chong4(row, col) > 1;
}
 
bool end_(int row, int col)//(row,col)处落子之后是否游戏结束
{
    for (int u = 0; u < 4; u++)if (num(row, col, u) + num(row, col, u + 4) >= 4)is_end = true;
    if (is_end)return true;
    is_end = ban(row, col);
    return is_end;
}
 
void go(int row, int col)//落下一子
{
    if (s == s0)p[row][col] = -1; //标出最新下的棋
    else p[row][col] = -2;
    for (int i = 0; i <= N; i++)for (int j = 0; j <= N; j++) //取消上一个最新棋的标识
    {
        if (i == row && j == col)continue;
        if (p[i][j] < 0)p[i][j] *= -1;
    }
    DrawBoard();
    if (ban(row, col))
    {
        printf("禁手\n");
        if (s0 == 1)printf("玩家胜");
        else printf("AI胜");
        Sleep(10000);
    }
    if (end_(row, col))
    {
        if (s == ais)printf("AI胜");
        else printf("玩家胜");
        Sleep(10000);
    }
    manu[0][manukey] = row, manu[1][manukey++] = col;
}
 
bool ok(int row, int col)//能否落子
{
    return inboard(row, col) && (p[row][col] == 0);
}
 
int point(int row, int col)//非负分值
{
    if (ban(row, col))return 0;//禁手0分
    if (end_(row, col))
    {
        is_end = false;
        return 10000;
    }
    int ret = live4(row, col) * 1000 + (chong4(row, col) + live3(row, col)) * 100, u;
    for (u = 0; u < 8; u++)if (p[row + dx[u]][col + dy[u]])ret++;//无效点0分
    return ret;
}
 
int AI3(int p2)
{
    int keyp = -100000, tempp;
    for (int i = 1; i <= N; i++)for (int j = 1; j <= N; j++)
    {
        if (!ok(i, j))continue;
        p[i][j] = s0;
        tempp = point(i, j);
        if (tempp == 0)
        {
            p[i][j] = 0;
            continue;
        }
        if (tempp == 10000)
        {
            p[i][j] = 0;
            return 10000;
        }
        p[i][j] = 0;
        if (tempp - p2 * 2 > keyp)keyp = tempp - p2 * 2;//第三层取极大
    }
    return keyp;
}
 
int AI2()
{
    int keyp = 100000, tempp;
    for (int i = 1; i <= N; i++)for (int j = 1; j <= N; j++)
    {
        if (!ok(i, j))continue;
        p[i][j] = 3 - s0;
        tempp = point(i, j);
        if (tempp == 0)
        {
            p[i][j] = 0;
            continue;
        }
        if (tempp == 10000)
        {
            p[i][j] = 0;
            return -10000;
        }
        tempp = AI3(tempp);
        p[i][j] = 0;
        if (tempp < keyp)keyp = tempp;//第二层取极小
    }
    return keyp;
}
 
void AI()
{
    DrawBoard();
    printf("  轮到AI下,请稍候: ");
    int keyi,keyj;
	if(aimod==0){
		if (p[8][8] == 0)return go(8, 8);
	    int i, j;
	    int keyp = -100000, tempp;
	    for (i = 1; i <= N; i++)
	    {
	        for (j = 1; j <= N; j++)
	        {
	            if (!ok(i, j))continue;
	            p[i][j] = s0;
	            tempp = point(i, j);
	            if (tempp == 0)
	            {
	                p[i][j] = 0;
	                continue;
	            }//高效剪枝,避开了禁手点和无效点
	            if (tempp == 10000)return go(i, j);
	            tempp = AI2();
	            p[i][j] = 0;
	            if (tempp > keyp)keyp = tempp, keyi = i, keyj = j;//第一层取极大
	        }
	    }
	} 
    else if(aimod==77143){
    	if (p[8][8] == 0)return go(8, 8);
    	long long ansmark=0;
    	for(int ii=1;ii<=N;ii++){
    		for(int jj=1;jj<=N;jj++){
    			if(p[ii][jj]<0) p[ii][jj]=-p[ii][jj];
			}
		}
    	for(int i=1;i<=N;i++){
    		for(int j=1;j<=N;j++){
    			long long ansbu=0;
    			for(int k=0;k<4;k++){
    				if(p[i][j]!=0){
    					
					}
					else{
						int wjqz=0,aiqz=0,klioo_l=4,klioo_r=-4;
						while(i+dx[k]*klioo_l<=0 || i+dx[k]*klioo_l>=16 || j+dy[k]*klioo_l<=0 || j+dy[k]*klioo_l>=16){
							klioo_l--;
						}
						while(i+dx[k]*klioo_r<=0 || i+dx[k]*klioo_r>=16 || j+dy[k]*klioo_r<=0 || j+dy[k]*klioo_r>=16){
							klioo_r++;
						}
						klioo_r+=4;
						for(int r=klioo_l;r>=klioo_l-4 && klioo_l>=klioo_r;r--){
							if(p[i+dx[k]*r][j+dy[k]*r]==startm%2+1) wjqz++;
							else if(p[i+dx[k]*r][j+dy[k]*r]==startm) aiqz++;
						}
						if(aiqz==0) ansbu+=wjf[wjqz];
						if(wjqz==0) ansbu+=aif[aiqz];
						for(int r=klioo_l;r>klioo_r;r--){
							if(p[i+dx[k]*r][j+dy[k]*r]==startm%2+1) wjqz--;
							else if(p[i+dx[k]*r][j+dy[k]*r]==startm) aiqz--;
							r-=5;
							if(p[i+dx[k]*r][j+dy[k]*r]==startm%2+1) wjqz++;
							else if(p[i+dx[k]*r][j+dy[k]*r]==startm) aiqz++;
							r+=5;
							if(aiqz==0) ansbu+=wjf[wjqz];
							if(wjqz==0) ansbu+=aif[aiqz];
						}
					}
				} 
				if(ansbu>ansmark){
					ansmark=ansbu;
					keyi=i;
					keyj=j;
				}
			}
		}
		//printf("\n%d\n",ansmark);
		//scanf("%d",&ansmark);
	}
    return go(keyi, keyj);
}
 
void out_manual()
{
    char alpha = 'A';
    int i;
    printf("\n  黑方落子位置: ");
    for (i = 0; i < manukey; i += 2)printf("  %c%d", alpha + manu[1][i] - 1, manu[0][i]);
    printf("\n  白方落子位置: ");
    for (i = 1; i < manukey; i += 2)printf("  %c%d", alpha + manu[1][i] - 1, manu[0][i]);
    Sleep(5000);
}
 
void player()
{
    DrawBoard();
    printf("  轮到玩家下,请输入坐标(输入=0查看棋谱): ");
    char c = '\n';
    int row = 0, col = 0;
    while (c<'0')scanf("%c%d", &c, &row);
    if (c == '=')
    {
        out_manual();
        return player();
    }
    if (c < 'a')col = c - 'A' + 1;
    else col = c - 'a' + 1;
    if (!ok(row, col))
    {
        printf("此处不能下");
        Sleep(1000);
        return player();
    }
    int i=row,j=col;
    /*
    			printf("%d\n\n",startm);
    			long long ansbu=0;
    			for(int ii=1;ii<=N;ii++){
    				for(int jj=1;jj<=N;jj++){
    					if(p[ii][jj]<0) p[ii][jj]=-p[ii][jj];
    					printf("%d ",p[ii][jj]); 
					}
					printf("\n");
				}
    			for(int k=0;k<4;k++){
    				if(false){
    					
					}
					else{
						int wjqz=0,aiqz=0;
						for(int r=4;r>=0;r--){
							if(p[i+dx[k]*r][j+dy[k]*r]==startm%2+1) wjqz++;
							else if(p[i+dx[k]*r][j+dy[k]*r]==startm) aiqz++;
						}
						printf("%d %d\n",wjqz,aiqz);
						ansbu+=wjf[wjqz];
						ansbu+=aif[aiqz];
						for(int r=4;r>0;r--){
							if(p[i+dx[k]*r][j+dy[k]*r]==startm%2+1) wjqz--;
							else if(p[i+dx[k]*r][j+dy[k]*r]==startm) aiqz--;
							r-=5;
							if(p[i+dx[k]*r][j+dy[k]*r]==startm%2+1) wjqz++;
							else if(p[i+dx[k]*r][j+dy[k]*r]==startm) aiqz++;
							r+=5;
							printf("%d ",r);
							ansbu+=wjf[wjqz];
							ansbu+=aif[aiqz];
							printf("%d %d\n",wjqz,aiqz);
						}
					}
				} 
				printf("%lld\n",ansbu);
				scanf("%lld",&ansbu);
	*/
    go(row, col);
}
 
int main()
{
    init();
    while (!is_end)
    {
        if (s == ais)AI();
        else player();
        s = 3 - s;//换下棋方
    }
    return 0;
}