(简易版)c语言人机对战五子棋

概要

琐碎的地方就不提了,人机对战五子棋的核心在于如何分析棋局,使程序落子最优化。当然我只是简易版本,没有棋盘界面化,没有禁手,策略也不能算是精良,只能勉强当作一个参考供大家学习。

Ps:AI有时候会犯傻,可能是分值设置的原因,请谅解??

如何分析棋局

这里只是概括,具体可以看之后挂出来的网址。

  1. 确定有效空位(即AI下棋的空位选项):落子处周围米字型区域(8个点),每次落子都要更新记录空位的集合
  2. 空位的评分:这里需要对每种棋局进行打分(四子、三子、两子、一子都有各自的情况),最后累加得到该空位的局势得分,具体评分内容可以看这里的网址(https://www.write-bug.com/article/1372.html) [转载自write-bug.com]
  3. 求最高分的空位:即为程序最优落子点

下面po上代码

fivechess.c

#include <stdio.h>
#include <stdlib.h>

#define CHESS_MAX 24                //行列坐标

struct local                        //空位信息
{
    int local_carrier;              //存储四位坐标信息
    int score;                      //空位的得分信息
}empty1[CHESS_MAX*CHESS_MAX];

int judge[CHESS_MAX][CHESS_MAX] = {{0}};      //用于判断该空位是否在空位结构体中 0:不在 1:在 排除重复性计入结构体
int board[CHESS_MAX][CHESS_MAX] = {{0}};      //棋盘数组
int number_empty = 0;                         //空位数
int x, y;                                     //临时记录坐标

//根据棋型计算空位得分,count1为相连棋子数,
//leftStatus、rightStatus为1或2,1代表为空,2为墙或者对方棋子
int getScoreBySituation(int count1, int left1, int right1);//这个需要对照评分表

//横向扫描计算得分 cur表示AI或者人类
int getXScore(int x, int y, int cur);

//纵向扫描计算得分
int getYScore(int x, int y, int cur);

//正斜向扫描计算得分
int getSkewScore1(int x, int y, int cur);

//反斜向扫描计算得分
int getSkewScore2(int x, int y, int cur);

//上面五个函数在get_score.c中定义

//显示棋盘
void show_chess_board(int (*a)[CHESS_MAX][CHESS_MAX]);

//用户下棋 返回落子信息
int get_inputchess();

//落子处为中心的米字型区域记录空位
void add_empty(int x, int y);

//双方在空位处下子 要在judge数组中更新状态
void update_judge_empty(int x, int y);

//更新各个空位的总得分
void update_score_empty();

//返回得分最高
int return_high();

int win(int a);
//以上为函数


//将空位存入数组中
void add_empty(int x, int y)
{
    int i, j;
    for (i = x - 1; i <= x + 1; i++)
    {
        for (j = y - 1; j <= y + 1; j++)
        {
            if(i == x && j == y)
                break;
            if(board[i][j] == 0 && judge[i][j] == 0)//该位置没有下过并且不在空位结构体中
            {
                empty1[number_empty].local_carrier = i*100+j;
                number_empty++;
                judge[i][j] = 1;
            }
        }
    }
}


//人类或者AI下一次棋后更新空位分数
void update_score_empty()
{
    int i, grade;
    for(i = 0; i < number_empty; i++)
    {
        grade = empty1[i].local_carrier;
        int X = grade / 100, Y = grade % 100;
        if(board[X][Y] != 0)//空位被下过就不再计算得分
        {
            empty1[i].score = 0;
            continue;
        }
        else
        {
            empty1[i].score = getXScore(X,Y,1) + getXScore(X,Y,2)
            + getYScore(X,Y,1) + getYScore(X,Y,2) + getSkewScore1(X,Y,1)
            + getSkewScore1(X,Y,2) + getSkewScore2(X,Y,1) + getSkewScore2(X,Y,2);
        }
    }
}

int win(int a)
{                                           //判断胜负
	int x,y;//x means line,y means column
	//judge line
	for(y=0;y<17;y++)//last four column no need to judge
	{
		for(x=0;x<20;x++)
		{
            if(a==board[x][y]&&a==board[x+1][y]&&a==board[x+2][y]&&a==board[x+3][y]&&a==board[x+4][y])
                return a;//win
		}
	}
	//judge vitical
	for(y=0;y<20;y++)
	{
        for(x=0;x<17;x++)//last four line no need to judge
        {
            if(a==board[x][y]&&a==board[x][y+1]&&a==board[x][y+2]&&a==board[x][y+3]&&a==board[x][y+4])
                return a;
        }
	}
	// judge "\"
	for(y=0;y<20;y++)
	{
        for(x=0;x<17;x++)//last four line no need to judge
        {
            if(a==board[x][y]&&a==board[x+1][y+1]&&a==board[x+2][y+2]&&a==board[x+3][y+3]&&a==board[x+4][y+4])
                return a;
        }
	}
	// judge "/"
	for(y=19;y>3;y--)
	{
        for(x=0;x<17;x++)//last four line no need to judge
        {
            if(a==board[x][y]&&a==board[x+1][y-1]&&a==board[x+2][y-2]&&a==board[x+3][y-3]&&a==board[x+4][y+4])
                return a;
        }
	}
	return 0;
}

//返回得分最高
int return_high()
{
    int i, grade_msg;
    int max = 0, max_location;
    for (i = 0; i < number_empty; i++)
    {
        grade_msg = empty1[i].local_carrier;
        if(judge[grade_msg/100][grade_msg%100] == 1 && board[grade_msg/100][grade_msg%100] == 0)//在空位结构体中 且未下过
        {
            if(empty1[i].score > max)//若得分都相同,就返回第一个找到的空位
            {
                max = empty1[i].score;
                max_location = empty1[i].local_carrier;
            }
        }
    }
    return max_location;
}

//人类落子
int get_inputchess()
{
    int a, circle_x = 0, circle_y = 0;
    char local_x, local_y;
    do//记录x坐标
    {
        printf("please input the chess of line:");
        scanf("%c", &local_x);
        getchar();//换行符去掉
        switch(local_x)
        {
		case '0':
			x = 0;
		case '1':
			x = 1;
			break;
		case '2':
			x = 2;
			break;
		case '3':
			x = 3;
			break;
		case '4':
			x = 4;
			break;
		case '5':
			x = 5;
			break;
		case '6':
			x = 6;
			break;
		case '7':
			x = 7;
			break;
		case '8':
			x = 8;
			break;
		case '9':
			x = 9;
			break;
		case 'a':
			x = 10;
			break;
		case 'b':
			x = 11;
			break;
		case 'c':
			x = 12;
			break;
		case 'd':
			x = 13;
			break;
		case 'e':
			x = 14;
			break;
		case 'f':
			x = 15;
			break;
		case 'g':
			x = 16;
			break;
		case 'h':
			x = 17;
			break;
		case 'i':
			x = 18;
			break;
		case 'j':
			x = 19;
			break;
		case 'k':
			x = 20;
			break;
		case 'l':
			x = 21;
			break;
		case 'm':
			x = 22;
			break;
		case 'n':
			x = 23;
			break;
        default:
            printf("wrong input%c\n",local_x);
            circle_x = 1;
            break;
        }
    }
    while(circle_x != 0);
    printf("please input the chess of row:"); //y的处理同上
	do
	{
		scanf("%c", &local_y); //获取列坐标
		getchar();
		switch (local_y)
		{
		case '0':
			y = 0;
		case '1':
			y = 1;
			break;
		case '2':
			y = 2;
			break;
		case '3':
			y = 3;
			break;
		case '4':
			y = 4;
			break;
		case '5':
			y = 5;
			break;
		case '6':
			y = 6;
			break;
		case '7':
			y = 7;
			break;
		case '8':
			y = 8;
			break;
		case '9':
			y = 9;
			break;
		case 'a':
			y = 10;
			break;
		case 'b':
			y = 11;
			break;
		case 'c':
			y = 12;
			break;
		case 'd':
			y = 13;
			break;
		case 'e':
			y = 14;
			break;
		case 'f':
			y = 15;
			break;
		case 'g':
			y = 16;
			break;
		case 'h':
			y = 17;
			break;
		case 'i':
			y = 18;
			break;
		case 'j':
			y = 19;
			break;
		case 'k':
			y = 20;
			break;
		case 'l':
			y = 21;
			break;
		case 'm':
			y = 22;
			break;
		case 'n':
			y = 23;
			break;
		default:
			printf("wrong input %c\n", local_y);
			circle_y = 1;
			break;
		}
	}
	while (circle_y == 1);
	a = x*100+y;//坐标信息
	board[x][y] = 1;
	return a;
}

//显示棋盘
void show_chess_board(int (*a)[CHESS_MAX][CHESS_MAX])
{
	int i, j, k = 0; //k用来控制显示空格,换行
	printf("\n\n");
	printf(" 0123 4567 89ab cdef ghij klmn\n"); //打印出列坐标

	for (i = 0; i < CHESS_MAX; i++) //i为行
	{

		for (j = 0; j < CHESS_MAX; j++) //j为列

		{

			//实现每四个棋子就出现一个空格,方便查找位置去下子
			if (k > 0 && (k % 4 == 0))
				printf(" ");

			//每24个棋子输出,就换行显示
			if (k > 0 && (k % CHESS_MAX == 0))
				printf("\n");

			//显示每行的开头的数字
			if (j == 0)
			{
				if (i <= 9)
					printf("%d", i); //开始9行用数字显示
				else
				{
					switch (i)
					{
					case 10:
						printf("%c", 'a');
						break;
					case 11:
						printf("%c", 'b'); //1-9不够显示行坐标,9后用a-k表示
						break;
					case 12:
						printf("%c", 'c');
						break;
					case 13:
						printf("%c", 'd');
						break;
					case 14:
						printf("%c", 'e');
						break;
					case 15:
						printf("%c", 'f');
						break;
					case 16:
						printf("%c", 'g');
						break;
					case 17:
						printf("%c", 'h');
						break;
					case 18:
						printf("%c", 'i');
						break;
					case 19:
						printf("%c", 'j');
						break;
					case 20:
						printf("%c", 'k');
						break;
					case 21:
						printf("%c", 'l');
						break;
					case 22:
						printf("%c", 'm');
						break;
					case 23:
						printf("%c", 'n');
						break;
					}
				}
			}

			int l = (*a)[i][j];
			if (l == 0)
				printf("-"); //此时代表未落子
			else if (l == 1)
				printf("O"); // 人类落子
			else if (l == 2)
				printf("X"); // AI落子

			k++; //每落一个子后k++来控制空格与换行
		}
	}

	printf("\n"); //打印棋盘完后再输出一个换行,让用户看得更清楚
}

get_score.c(计算空位处分数)

#include <stdio.h>
#include <stdlib.h>

#define CHESS_MAX 24

//根据棋型计算空位得分,count1为相连棋子数,leftStatus、rightStatus为1或2,1代表为空,2为墙或者对方棋子
int getScoreBySituation(int count1, int left1, int right1);//这个需要对照评分表

//横向扫描计算得分 cur表示AI或者人类
int getXScore(int x, int y, int cur);

//纵向扫描计算得分
int getYScore(int x, int y, int cur);

//正斜向扫描计算得分
int getSkewScore1(int x, int y, int cur);

//反斜向扫描计算得分
int getSkewScore2(int x, int y, int cur);

extern int board[CHESS_MAX][CHESS_MAX];	    //棋盘数组
int left = 0, right = 0;                    //空位左右两边的情况
int count_left, count_right;                //左右连子数
int temp, temp_1, temp_2;                   //记录空位坐标的临时变量


int getScoreBySituation(int count1, int left1, int right1)//评分表参照网站中的设定(不固定)
{
    if(count1 == 4)
        return 200000;
    if(count1 == 3)
    {
        if(left1 == 1 && right1 == 1)
            return 50000;
        else if( (left1 == 1 && right1 == 2) || (left1 == 2 && right1 == 1) )
            return 3000;
        else
            return 1000;
    }
    if(count1 == 2)
    {
        if(left1 == 1 && right1 == 1)
            return 3000;
        else if( (left1 == 1 && right1 == 2) || (left1 == 2 && right1 == 1) )
            return 1000;
        else
            return 500;
    }
    if(count1 == 1)
    {
        if(left1 == 1 && right1 == 1)
            return 500;
        else if( (left1 == 1 && right1 == 2) || (left1 == 2 && right1 == 1) )
            return 200;
        else
            return 100;
    }
    if(count1 == 0)
    {
        if(left1 == 1 && right1 == 1)
            return 100;
        else if( (left1 == 1 && right1 == 2) || (left1 == 2 && right1 == 1) )
            return 50;
        else
            return 30;
    }
    return 0;
}


int getXScore(int x, int y, int cur)//对同一个位置要调用两次函数(AI和人类)
{
    temp = y;
    count_left = count_right = 0;
    while(1)//左边移动
    {
        --y;
        if(y == -1 || board[x][y] != cur)//对方棋子或者墙
        {
            left = 2;
            break;
        }
        else if(board[x][y] == cur)//自己棋子
        {
            ++count_left;
            continue;
        }
        if(board[x][y] == 0)//空格
        {
            left = 1;
            break;
        }
    }
    while(1)//右边移动
    {
        ++temp;
        if(y == CHESS_MAX || board[x][temp] != cur)//对方棋子或者墙
        {
            right = 2;
            break;;
        }
        else if(board[x][temp] == cur)//自己棋子
        {
            ++count_right;
            continue;
        }
        if(board[x][temp] == 0)//空格
        {
            right = 1;
            break;
        }
    }
    return getScoreBySituation(count_left + count_right, left, right);
}

int getYScore(int x, int y, int cur)
{
    temp = x;
    count_left = count_right = 0;
    while(1)//向下移动
    {
        ++x;
        if(x == CHESS_MAX || board[x][y] != cur)//对方棋子或者墙
        {
            left = 2;
            break;
        }
        else if(board[x][y] == cur)//自己棋子
        {
            ++count_left;
            continue;
        }
        if(board[x][y] == 0)//空格
        {
            left = 1;
            break;
        }
    }
    while(1)//向上移动
    {
        --temp;
        if(temp == -1 || board[temp][y] != cur)//对方棋子或者墙
        {
            right = 2;
            break;;
        }
        else if(board[temp][y] == cur)//自己棋子
        {
            ++count_right;
            continue;
        }
        if(board[temp][y] == 0)//空格
        {
            right = 1;
            break;
        }
    }
        return getScoreBySituation(count_left + count_right, left, right);
}

int getSkewScore1(int x, int y, int cur)
{
    temp_1 = x, temp_2 = y;
    count_left = count_right = 0;
    while(1)//斜向左下移动
    {
        ++x;
        --y;
        if(x == CHESS_MAX || y == -1 || board[x][y] != cur)//对方棋子或者墙
        {
            left = 2;
            break;
        }
        else if(board[x][y] == cur)//自己棋子
        {
            ++count_left;
            continue;
        }
        if(board[x][y] == 0)//空格
        {
            left = 1;
            break;
        }
    }
    while(1)//斜向右下移动
    {
        --temp_1;
        ++temp_2;
        if(temp_1 == -1 || temp_2 == CHESS_MAX ||board[temp_1][temp_2] != cur)//对方棋子或者墙
        {
            right = 2;
            break;
        }
        else if(board[temp_1][temp_2] == cur)//自己棋子
        {
            ++count_right;
            continue;
        }
        if(board[temp_1][temp_2] == 0)//空格
        {
            right = 1;
            break;
        }
    }
    return getScoreBySituation(count_left + count_right, left, right);
}

int getSkewScore2(int x, int y, int cur)
{
    temp_1 = x, temp_2 = y;
    count_left = count_right = 0;
    while(1)//斜向左上移动
    {
        --x;
        --y;
        if(x == -1 || y == -1 || board[x][y] != cur)//对方棋子或者墙
        {
            left = 2;
            break;
        }
        else if(board[x][y] == cur)//自己棋子
        {
            ++count_left;
            continue;
        }
        if(board[x][y] == 0)//空格
        {
            left = 1;
            break;
        }
    }
    while(1)//斜向右下移动
    {
        ++temp_1;
        ++temp_2;
        if(temp_1 == CHESS_MAX || temp_2 == CHESS_MAX ||board[temp_1][temp_2] != cur)//对方棋子或者墙
        {
            right = 2;
            break;
        }
        else if(board[temp_1][temp_2] == cur)//自己棋子
        {
            ++count_right;
            continue;
        }
        if(board[temp_1][temp_2] == 0)//空格
        {
            right = 1;
            break;
        }
    }
    return getScoreBySituation(count_left + count_right, left, right);
}

main.c

int main()
{
    printf("如果打算开始请输入1\n");
    int msg;
    scanf("%d", &msg);
    getchar();

    if(msg == 1)
    {
        board[12][12] = 2;//AI先落子
        show_chess_board(&board);
        add_empty(12,12);
        while(1)
        {
            //人类后落子
            int Chess_msg = get_inputchess();
            show_chess_board(&board);
            if(win(1))
            {
                printf("人类胜利\n");
                break;
            }

            printf("请按任意键继续\n");
            getchar();

            //落子后机器分析
            add_empty(Chess_msg / 100, Chess_msg % 100);//记录空位

            update_score_empty();//更新空位得分

            //AI落子
            int max_location = return_high();
            board[max_location/100][max_location%100] = 2;
            show_chess_board(&board);
            if(win(2))
            {
                printf("AI胜利");
                break;
            }

            // 落子后机器分析
            add_empty(max_location / 100, max_location % 100);

            update_score_empty();//更新空位得分

        }
    }
    return 0;
}

演示图片:
在这里插入图片描述
感谢sky老师的指导

做到有些粗糙,有不对的地方欢迎在评论区留言哟?~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值