先说下背景吧,写下这篇博客时,博主大一在读,C语言初学者,寒假无事,便计划写几个由C语言实现的小游戏以提升编程能力。在这篇博客里分享的是可人机对战的五子棋游戏。
棋类游戏要实现初级的机器智能,其核心思想便是: 感知(SENSE)-> 思考(THINK)-> 行动(ACT)。所以,本文将尽量以这个顺序介绍实现过程。
(1) 前期准备:
此程序中,机器方将以“当前最佳下法”来主导行动,即设计一套评价体系,针对每一个可落子处,综合评分,最终选最高分处落子。所以,我定义了“位置”的结构,包含横纵坐标和得分数三个参数。进而,定义一个位置结构体数组,用于记录每次评分时的可选位置,以及记录该数组最后一个有效元素下标的变量,用于更方便存入取出。另外,诸如初始化棋盘,打印棋盘,初始化位置结构体数组等自定义函数,也一并贴在此处。

#include<stdio.h>
#include<windows.h>#define TRUE 1
#define FALSE 0struct position {int x;int y;int score;
};char chess_board[16][16];
struct position positions[50];
int position_order;void initialize_board(void)
{int i, j;for (i = 0;i < 16;i++) {for (j = 0;j < 16;j++) {chess_board[i][j] = ' ';}}
}
void initial_positions(void)
{positions[0].x = positions[0].y = positions[0].score = 0;for (position_order = 1;position_order < 50;position_order++) {positions[position_order] = positions[0];}position_order = 0;
}
void print_board(void)
{int i, j;/*print letters*/printf("  ");for (j = 0;j < 16;j++) {printf("  %c", 'A' + j);}putchar('\n');/*print chess board*/for (i = 0;i < 33;i++) {if (i % 2 == 1) {printf("%2d", (i + 1) / 2);}else {printf("  ");}for (j = 0;j < 33;j++) {/*The first row*/if (i == 0 && j == 0) {printf("┌  ");}else if (i == 0 && j==32) {printf("┐");}else if (i == 0 && j % 2 == 0) {printf("┬  ");}/*The last row*/if (i == 32 && j == 0) {printf("└  ");}else if (i == 32 && j == 32) {printf("┘");}else if (i == 32 && j % 2 == 0) {printf("┴  ");}/*The other rows*/if (i != 0 && i != 32) {if (i % 2 == 0) {if (j == 0) {printf("├  ");}else if (j == 32) {printf("┤");}else if (j % 2 == 0) {printf("┼  ");}}else if(j % 2 == 0) {printf("  %c", chess_board[(i - 1) / 2][(j + 1) / 2]);}}}putchar('\n');}
}
int is_full(void) {int i, j;for (i = 0;i < 16;i++) {for (j = 0;j < 16;j++) {if (chess_board[i][j] == ' ')return(FALSE);}}return(TRUE);
}
void is_win(int x, int y, char cp)
{int i, num=0;/*row*/for (i = 0;chess_board[y][x + i] == cp;i++, num++);for (i = -1;chess_board[y][x + i] == cp;i--, num++);if (num >= 5) {system("cls");print_board();printf("%c win!", cp);system("pause");}else {num = 0;}for (i = 0;chess_board[y + i][x] == cp;i++, num++);for (i = -1;chess_board[y + i][x] == cp;i--, num++);if (num >= 5){system("cls");print_board();printf("%c win!", cp);system("pause");}else {num = 0;}for (i = 0;chess_board[y + i][x + i] == cp;i++, num++);for (i = -1;chess_board[y + i][x + i] == cp;i--, num++);if (num >= 5) {system("cls");print_board();printf("%c win!", cp);system("pause");}else {num = 0;}for (i = 0;chess_board[y + i][x - i] == cp;i++, num++);for (i = -1;chess_board[y + i][x - i] == cp;i--, num++);if (num >= 5) {system("cls");print_board();printf("%c win!", cp);system("pause");}else {num = 0;}
}
void scan(void)
{char c;int i;do {printf("输入落子行列:");scanf_s("%d%c", &i, &c);if (!(chess_board[i - 1][c - 'A'] == ' '))continue;chess_board[i - 1][c - 'A'] = '*';break;} while (TRUE);is_win(c - 'A', i - 1, '*');
}
        贴一张实际效果图:
(2)感知:
        这一步中,将遍历棋盘中所有可落子位置,从横,纵,右斜和左斜四个方向,以棋子状态进行评分,总后将有效位置的总分记录在数组中。
         下面先贴出的是评分函数,其接受一个大小为5的字符数组(记录在选定位置周围截取的包含该位置的”一行“棋子状态),一个描述选定位置在该行中的位置的参数x。内部由若干分支构成,对应每种棋子状态,返回对该状态的评分。

void reverse(char row[],int len)            //由于一行棋子具有对称性,故x=3,x=4的状态可翻转归结为x=0,x=1的状态
{char temp;int i,j;for (i = 0, j = len - 1;i <= j;i++, j--) {temp = row[i];row[i] = row[j];row[j] = temp;}
}
int score(char row[], int x)                //'O'代表白棋,'*'代表黑棋,人执黑,机器执白
{                                           //每个if分支后的注释,'_'代表空格(即可落子处),'O'为白棋,'*'为黑棋if (x > 2) {                        //'?'表示已可给分,该位置状态不必获取reverse(row,5);             //三者的排布代表截取行的棋子状态
        x = 4 - x;                  //紧接着的return,返回的便是对该位置的评分}switch (x) {case 0: {if (row[1] == 'O' && (row[2] == 'O' || row[2] == ' ') && (row[3] == 'O' || row[3] == ' ') && (row[4] == 'O' || row[4] == ' ')) {if (row[x + 2] == ' ') {        //_O_??return(15);}else if (row[x + 3] == ' ') {   //_OO_?return(50);}else if (row[x + 4] == ' ') {   //_OOO_return(90);}else {                           //_OOOOreturn(1000);}}else if (row[1] == '*' && (row[2] == '*' || row[2] == ' ') && (row[3] == '*' || row[3] == ' ') && (row[4] == '*' || row[4] == ' ')) {if (row[x + 2] == ' ') {        //_*_??return(5);}else if (row[x + 3] == ' ') {    //_**_?return(30);}else if (row[x + 4] == ' ') {   //_***_return(70);}else {                           //_****return(500);}}};break;case 1: {if ((row[0] == 'O' || row[0] == ' ') && (row[2] == 'O' || row[2] == ' ') && (row[3] == 'O' || row[3] == ' ') && (row[4] == 'O' || row[4] == ' ')) {if (row[0] == 'O') {if (row[2] == ' ') {       //O_ _??return(15);}else if (row[3] == ' ') {   //O_O_??return(50);}else if (row[4] == ' ') {   //O_OO_return(90);}else {                       //O_OOOreturn(1000);}}else if (row[2] == ' ') {     //_ _ _??return(0);}else if (row[3] == ' ') {       //_ _O_?return(15);}else if (row[4] == ' ') {       //_ _OO_return(50);}else {                          //_ _OOOreturn(80);}}else if ((row[0] == '*' || row[0] == ' ') && (row[2] == '*' || row[2] == ' ') && (row[3] == '*' || row[3] == ' ') && (row[4] == '*' || row[4] == ' ')) {if (row[0] == '*') {if (row[2] == ' ') {       //*_ _??return(5);}else if (row[3] == ' ') {    //*_*_?return(30);}else if (row[4] == ' ') {    //*_**_return(70);}else {                       //*_***return(500);}}else if (row[2] == ' ') {      //_ _ _??return(0);}else if (row[3] == ' ') {       //_ _*_?return(5);}else if (row[4] == ' ') {        //_ _**_return(30);}else {                          //_ _***return(60);}}}break;case 2: {if ((row[0] == 'O' || row[0] == ' ') && (row[1] == 'O' || row[2] == ' ') && (row[3] == 'O' || row[3] == ' ') && (row[4] == 'O' || row[4] == ' ')) {if (row[1] == 'O') {if (row[3] == 'O') {if (row[0] == 'O') {if (row[4] == 'O') {//OO_OOreturn(1000);}else {return(90);      //OO_O_}}else {if (row[4] == 'O') {//_O_OOreturn(90);}else {                //_O_O_return(50);}}}else {if (row[0] == 'O') { //OO_ _?return(40);}else {                  //_O_ _?return(15);}}}else {if (row[3] == 'O') {if (row[4] == 'O') {    //_ _ _OOreturn(40);}else {                 //_ _ _O_return(15);}}}}else if ((row[0] == '*' || row[0] == ' ') && (row[1] == '*' || row[2] == ' ') && (row[3] == '*' || row[3] == ' ') && (row[4] == '*' || row[4] == ' ')) {if (row[1] == '*') {if (row[3] == '*') {if (row[0] == '*') {if (row[4] == '*') {//**_**return(500);}else {              //**_*_return(70);}}else {if (row[4] == '*') {//_*_**return(70);}else {             //_*_*_return(30);}}}else {if (row[0] == '*') { //**_ _ _return(20);}else {                 //_*_ _?return(5);}}}else {if (row[3] == '*') {     //_ _ _**if (row[4] == '*') {return(20);}else {                 //_ _ _*_return(5);}}}}}break;}return(0);
}

        接下来呢,就是感知中的另一个主要部分,其功能是对某一位置,从四个方向,截取评分函数所需的一行五位置数组。

int sense_row(int x, int y)        //横向截取,如下列一排示例中,x,y所代表的位置是第五个O
{                                  //[OOOOO]OOOO,O[OOOOO]OOO,OO[OOOOO]OO......如此这般依次截取,其余方向类似int sum = 0, i, j;char row[5];for (i = x - 4;i <= x;i++) {if (!(i >= 0 && i + 4 <= 15)) {continue;}else {for (j = 0;j < 5;j++) {row[j] = chess_board[y][i + j];}sum += score(row, x - i);}}return(sum);
}
int sense_col(int x, int y)
{int sum = 0, i, j;char row[5];for (i = y - 4;i <= y;i++) {if (!(i >= 0 && i + 4 <= 15)) {continue;}else {for (j = 0;j < 5;j++) {row[j] = chess_board[i + j][x];}sum += score(row, y - i);}}return(sum);
}
int sense_right_bias(int x, int y)
{int sum = 0, i, j;char row[5];for (i = -4;i <= 0;i++) {if (!(y + i >= 0 && x + i >= 0 && y + i + 4 <= 15 && x + i + 4 <= 15)) {continue;}else {for (j = 0;j < 5;j++) {row[j] = chess_board[y + i + j][x + i + j];}sum += score(row, -i);}}return(sum);
}
int sense_left_bias(int x, int y)
{int sum = 0, i, j;char row[5];for (i = -4;i <= 0;i++) {if (!(y - i <= 15 && x + i >= 0 && y - i - 4 >= 0 && x + i + 4 <= 15)) {continue;}else {for (j = 0;j < 5;j++) {row[j] = chess_board[y - i - j][x + i + j];}sum += score(row, -i);}}return(sum);
}
void sense(void)            //将四个方向上的评分综合并记录
{int x, y, sum = 0;initial_positions();for (y = 0;y < 16;y++) {for (x = 0;x < 16;x++) {if (chess_board[y][x] != ' ') {continue;}sum += sense_col(x, y);sum += sense_row(x, y);sum += sense_left_bias(x, y);sum += sense_right_bias(x, y);if (sum != 0) {positions[position_order].score = sum;positions[position_order].x = x;positions[position_order].y = y;position_order++;sum = 0;}}}
}
(3)思考与行动:
思考便是在感知并评分后记录下来的位置数组中,寻找最高分位置。行动紧跟其后,在最高分位置落子。博主在实现过程中,遇到有几个相同最高分时,只取第一个最高分,读者可添加随机功能,任选其一落子。代码如下。
void think_act(void)
{int max = 0, max_order, i;for (i = 0;i < position_order;i++) {if (positions[i].score > max) {max = positions[i].score;max_order = i;}}chess_board[positions[max_order].y][positions[max_order].x] = 'O';is_win(positions[max_order].x, positions[max_order].y, 'O');
}
(4)主函数:
void main(void)
{initialize_board();print_board();while (!is_full()) {scan();sense();think_act();system("CLS");print_board();}system("pause");
}
以上便是全部内容,希望可以通过博客与大家学习交流。博主编程水平有限,第一次写博客,必定有许多遗漏之处,思路也是乱糟糟的,希望诸位有何高见,不吝赐教。

C语言——五子棋人机对战相关推荐

  1. C语言 AI智能,五子棋 人机对战,人人对战

    C语言五子棋,人机对战,人人对战 chunli@Linux:~$ cat main.c # include<stdio.h> # include<string.h> # inc ...

  2. 五子棋程序设计(C语言、人机对战、禁手)

    五子棋程序设计(C语言.人机对战.禁手) 一.程序需求分析 1.1五子棋简介 五子棋是全国智力运动会竞技项目之一,是一种两人对弈的纯策略型棋类游戏. 五子棋有两种玩法.玩法一:双方分别使用黑白两色的棋 ...

  3. C#实现五子棋人机对战的思路与源码分享

    前言 业余闲暇时,基于.netframework使用C#语言开发了一个五子棋人机对战的游戏,在此做些整理和分享,先上图. winform版 UI虽然很简洁,却花了些功夫,图形元素全动态生成,毕竟不是美 ...

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

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

  5. 五子棋人机对战_10.带人机对战的五子棋程序

    今天我们带来一个带人机对战功能的五子棋程序.程序基于前面文章中的框架搭建,新增人机对战的策略.程序基于规则进行决策,不考虑禁手,玩家执黑子先行.棋盘规模采用15乘15,棋盘规模容易扩展,程序棋力中等, ...

  6. 人工智能 - 五子棋人机对战

    人工智能 - 五子棋人机对战 作者: jig    阅读人次: 6635    文章来源:本站原创    发布时间:2007-7-12     网友评论(8)条 原帖及讨论:http://bbs.bc ...

  7. c语言五子棋人机对弈算法,使用canvas基于AI算法实现人机对战之五子棋

    这是我使用canvas基于AI算法实现的人机对战之五子棋 黑棋是我 下了几局,真心下不过啊!!! 不说了,源码奉上: 人机大战之五子棋 canvas{ display: block; margin:5 ...

  8. c语言五子棋人机对弈算法_从零开始编写C语言五子棋程序1

    从零开始编写C语言五子棋程序 C语言程序是国科大计算机系本科生必修课程,我选修的是武成岗老师的C语言课程.除了课上回答问题,实验课competitive programming的成绩之外,最终的大作业 ...

  9. C语言五子棋--人机对弈--人人对弈

    C语言五子棋: 注:直接可以运行使用的代码,家人们的点赞在哪里 第一次实现可以进行人机对战的五子棋,分享下,难度不大.都是按照模块来写的,封装成了很多个函数,比较细致,新手还是可以看懂的.主要可以实现 ...

最新文章

  1. DataLoader 与 Dataset
  2. linux下仅仅有rman备份集的异机不同文件夹恢复
  3. 138个被撤出EI检索的会议
  4. Python翻译Excel文件
  5. 关于MinGW和MSYS
  6. win7虚拟机详细搭建过程
  7. 高阶篇:4.2.5)DFMEA建议措施及后续完备
  8. Spark写入Hudi报分区列乱码问题java.net.URISyntaxException: Illegal character in path at index 46:
  9. 2008年世界各国最新GDP排名
  10. Metaverse 元宇宙入门-02-Hardware 硬件与元宇宙
  11. K8s学习之yum安装
  12. 区块链/BlockChain+ProofOfWork
  13. 金刚菩提子开裂自动修复此计算机,教你一招修复开裂的金刚菩提
  14. IEEE论文搜索方法
  15. 计算机图形学之纹理的作用
  16. 章节十二:编程思维:如何debug
  17. 计算机教师面试专业技能部分,教师招聘考试面试,专业技能测试考什么?全在这了...
  18. 如何在跳槽前客观地评估自己的身价?
  19. Windows_XP DLL修改
  20. MySQL基本语句(燕十八部分)

热门文章

  1. 实习小白::(转) Cocos2d-x 3.0 开发(八)骨骼动画的动态换肤
  2. 各平台播放器以及浏览器的navigator.userAgent
  3. [python]...
  4. 微信dnf手游服务器上限,DNF手游:服务器跨区规则上线,玩家扎堆进一区,团本互通...
  5. 曾哥传——番外篇(一)曾哥无敌
  6. 极速办公(word)插入的表格如何修改文字方向
  7. @NotNull和@NotBlank的区别
  8. GPS 凯立德 J08 没声音的问题解决
  9. 奥兹国的奇妙术士-对话系统收集数据的方法
  10. Python Day13 面向对象基础【初级】