100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > C语言——五子棋人机对战

C语言——五子棋人机对战

时间:2020-04-18 03:33:01

相关推荐

C语言——五子棋人机对战

先说下背景吧,写下这篇博客时,博主大一在读,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");}

以上便是全部内容,希望可以通过博客与大家学习交流。博主编程水平有限,第一次写博客,必定有许多遗漏之处,思路也是乱糟糟的,希望诸位有何高见,不吝赐教。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。