概要

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

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 linefor(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 viticalfor(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;elsereturn 1000;}if(count1 == 2){if(left1 == 1 && right1 == 1)return 3000;else if( (left1 == 1 && right1 == 2) || (left1 == 2 && right1 == 1) )return 1000;elsereturn 500;}if(count1 == 1){if(left1 == 1 && right1 == 1)return 500;else if( (left1 == 1 && right1 == 2) || (left1 == 2 && right1 == 1) )return 200;elsereturn 100;}if(count1 == 0){if(left1 == 1 && right1 == 1)return 100;else if( (left1 == 1 && right1 == 2) || (left1 == 2 && right1 == 1) )return 50;elsereturn 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老师的指导

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

(简易版)c语言人机对战五子棋相关推荐

  1. java swing人机对战五子棋(含背景音乐)

    一.项目简介 本项目是一套基于java swing的人机对战五子棋系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者. 包含:项目源码.数据库脚本等,该项目附带全部源码可 ...

  2. 介绍一款Android小游戏--交互式人机对战五子棋

    学习Android系统开发之余,编写了一个小游戏--交互式人机对战五子棋,自娱自乐.之所以称之为交互式人机对战五子棋,一是因为在进入人机对战模式这前,你可以任意设置好开局,同时,在对战过程中,你可以看 ...

  3. java五子棋人机对战_实现简单的人机对战五子棋(实践)

    五子棋人机对战实践项目 总的任务和目标 完成一个人机对战的五子棋项目,基本效果如下: 第一部分 Java绘图原理 1.   基本概念 像素,坐标 2.   组件自定义绘图原理 3.   Graphic ...

  4. 用Lex编写的简易版C语言词法分析器(编译原理大作业1)

    本文讲解关于如何用lex工具来编写一个简易版的C语言的词法分析器.我主要通过一个完整的项目例子来进行讲解.当然这篇文章面向已经对lex有所了解但还不会具体运用的读者,如果对lex一无所知,请看我的另一 ...

  5. Java-基于GUI编程的人机对战五子棋

    1.人员分工 组员 职务 负责模块 刘新臻 组长 AI算法设计.程序逻辑设计 陈钦毅 组员 游戏画面框架,监听器.工具类 文才学 组员 棋子设计类.菜单功能 2.前期调查 根据调研我们需要以下功能 功 ...

  6. 基于FPGA(basys3)的双人对战人机对战五子棋(vivado)课程设计项目

    目录 主界面显示与选择模式 双人对战 人机对战 胜利界面显示 部分源码 主界面显示与选择模式 VGA显示器显示图片,显示图片利用Block Memory Generator将图片像素点储存在RAM里面 ...

  7. C++新手项目实践 — 智能人机对战五子棋

    C-project-practice-intelligent-man-machine-gobang-fight C++新手项目实践 - 智能人机五子棋对战(利用Easyx的基础函数) 项目演示视频 智 ...

  8. 完全基于C++ 实现的人机对战五子棋小游戏

    1. 项目目标 掌握C++的核心技术 掌握C++开发项目的方法和流程 掌握AI算法的基础应用 2. 效果演示 3. 创建项目 使用VS2019+easyx图形库开发,也可以使用VS的其他版本. 使用V ...

  9. slider节点透明背景_【Cocos Creator 实战教程(1)】——人机对战五子棋(节点事件相关)...

    一.涉及知识点 场景切换 按钮事件监听 节点事件监听 节点数组 循环中闭包的应用 动态更换sprite图片 定时器 预制资源 二.步骤 2.1 准备工作 首先,我们要新建一个空白工程,并在资源管理器中 ...

  10. 练习:尼姆游戏(聪明版/傻瓜式•人机对战)

    题目   尼姆游戏,这是一个著名的游戏,有很多变种玩法.两个玩家轮流从一堆物品中拿走一部分.在每一步中,玩家可以自由选择拿走多少物品,但是必须拿走一部并且最多只能拿走一半物品,然后轮到下一个玩家.拿走 ...

最新文章

  1. Android程序label居中
  2. 函数的多态性以及虚函数
  3. MySQL数据库-笔记05【查询练习题*25道(附解析)】
  4. web窗体的基本控件
  5. 人工机器:TM、VNM和NTM的内存机制
  6. console查看对象结构
  7. 我和51CTO的缘分【我与51CTO一“七”成长】
  8. 谷歌开源的代码评审规范,值得借鉴!
  9. 软件质量因素 6个_影响软件质量管理的主要因素
  10. ipad版实现横屏竖屏 详解iPad横竖屏切换解决方案
  11. chrome下载速度慢,提高下载速度
  12. linux nfs性能差,linux – 奇怪的nfs性能:1个线程比8个好,8个好于2个!
  13. BP神经网络实现实例1曲线拟合
  14. linux两个邮件服务,Linux系列-Red Hat5平台下的Postfix邮件服务搭建(二)
  15. linux修改默认22端口失败,【原创文章】修改亚马逊AWS EC2 LINUX系统SSH默认22端口失败的原因和解决办法...
  16. JS中的`DOM`增删修
  17. 北京 上海 天津 河北 融资性担保机构经营许可证
  18. ARLocation使用说明
  19. (Python)LeetCode1386:安排电影院座位
  20. 重整山河!2017eSmart,让VR飞一会儿

热门文章

  1. 数据恢复技巧:U盘文件被隐藏怎么恢复?
  2. sis地址发布器_Android Studio 3.6 正式版终于发布了,快来围观
  3. 开始使用Mac OS X
  4. 如何阅读源码,阅读源码的难点和方法分析
  5. 全网最有效软考高项十大管理ITTO记忆:宫殿记忆法、主线记忆法、逻辑记忆法、跟踪记忆法、诗词记忆法
  6. ubuntu中安装flash播放器
  7. 浏览器兼容性问题和解决方案
  8. 计算机化学试题,08计算机化学试卷yuanj.doc
  9. java-工具-开源
  10. WEB 系统架构演变