一-思路组织

1-棋盘构成

三子棋的布局为3*3的棋盘格

我们用二维数组充当棋盘

2-棋盘打印

在数组之间用空格和’|‘号和‘---’来填充模拟视觉上的棋盘效果

例如第一行用空格加数组内容加’|‘号打印:

“’空格‘’数组‘’空格‘’|’‘空格’‘数组’‘空格’‘|’‘空格’‘数组’‘空格’

第二行为‘---’‘|’‘---’‘|’---‘

整体上效果如图:

C语言实现简单的三子棋人机对弈_C语言

3-电脑随机下子

电脑随机下子的过程我们用随机数生成函数rand();来生成x与y的坐标。

2-玩家下子

玩家下子的过程要主要C语言中第一个位置下标是0。

5-局势判断

判断过程要要检测三竖和三横,与从左上到右下是否有三个相同的子连续

二-代码实现

1-棋盘构成

宏定义棋盘的大小为3*3

C语言实现简单的三子棋人机对弈_C语言_02

分配一个char类型的二维数组

C语言实现简单的三子棋人机对弈_三子棋_03

初始化棋盘内容为空格符号,过程为利用两个for循环一个一个的往数组里面填充空格符号

C语言实现简单的三子棋人机对弈_C语言_04

2-棋盘打印

棋盘打印的过程稍微繁琐一点

首先我们来打印第一行,过程分为三个部分

前两个为 空格+数组+空格+竖杠,最后一个部分为 空格+数组+空格,因为最后一个部分少一个竖杠我们这里要注意,并且还要注意打印一行要加上分行。(仔细观察其中的两个if语句)

C语言实现简单的三子棋人机对弈_C语言_05

第二行就是将先前的 空格+数组+空格 替换成三个小横杠号

也分为三个部分 - - - | 与- - - | 与 - - -   和上面的打印过程几乎相同,也是最后一个部分少打印一个竖杠号。 (仔细观察其中两个if语句)

C语言实现简单的三子棋人机对弈_C语言_06

那么现在请看这张图:

C语言实现简单的三子棋人机对弈_三子棋_07

我们能发现  整个棋盘可以分为三个部分

【1.0】与【0.0】这两行可以组成一个部分

【1.1】与【0.1】组成一个部分

【1.2】组成一个部分

仔细对比单行打印过程,整体打印中的【0.0】与【0.1】和单行打印中的竖杠号,都没有在最后一部分中打印出来。(请仔细观察下面这个部分中的if语句)

因此代码实现为

C语言实现简单的三子棋人机对弈_C语言_08

3-电脑随机下子

为了生成随机数,我们需要利用时间戳来作为随机输生成函数的起点

C语言实现简单的三子棋人机对弈_三子棋_09

当然,我们还需要引用两个头文件

C语言实现简单的三子棋人机对弈_C语言_10

在电脑下子的时候要将生成的随机数模上棋盘的大小,且要判断棋盘位置是否为空格,如果不是重新生成一次随机数坐标。

代码如下

C语言实现简单的三子棋人机对弈_三子棋_11


4-玩家下子

这里要注意的一点就是在C语言中实际的第一个位置以下标0开始,因此我们在判断棋盘位置的时候要将玩家输入的坐标-1;

当然我们也需要判断玩家坐标输入的合法性,在坐标位置有棋子的时候和坐标位置超出棋盘范围的时候,跳转回去并且再次提醒玩家重新输入。

C语言实现简单的三子棋人机对弈_三子棋_12

5-局势判断

局势判断是最简单,但是最为繁琐的部分。

因为我们需要判断三横三纵与两个斜着的方向是否有三子连同。

代码设计如下

首先我们要设计这个判断胜负的函数在几种情况下的放回值,如下图所示

C语言实现简单的三子棋人机对弈_三子棋_13

接下来是繁琐的代码实现,请仔细阅读

C语言实现简单的三子棋人机对弈_三子棋_14

三-代码参考

现在我们已经将三子棋的简单人机对弈的每个部分都介绍完了

考虑到本人的粗心大意的本性,因此在下面附上的参考代码或许有bug存在于其中,请包含。

#include<stdio.h>
#include<time.h>   // 时间戳
#include<stdlib.h> // rand()与srand()
#include<Windows.h>

#define BOARD 3
#define ROW BOARD
#define COL BOARD  // 设置棋盘为 3*3 大小 

#define play_board '*' // 设置玩家下的棋子为 *
#define computer_board '#' // 设置电脑下的棋子为 #

void Menu(void) {
	printf("*------------*\n");
	printf("|  0.out     |\n");
	printf("|  1.play    |\n");
	printf("*------------*\n");
}

void init_board(char board[ROW][COL]) { // 将棋盘填充为空格 
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW; i += 1) {
		for (j = 0; j < COL; j += 1) {
			board[i][j] = ' ';
		}
	}
}

void print_board(char board[ROW][COL]) { // 实现棋盘打印
	int i = 0;							               //    |   |    // 1.0
	int j = 0;							               // ---|---|--- // 0.0
	for (i = 0; i < ROW; i += 1) {		     //    |   |    // 1.1
		for (j = 0; j < COL; j += 1) {	     // ---|---|--- // 0.1
			printf(" %c ", board[i][j]);       //    |   |    // 1.2
			if (j < COL - 1) {
				printf("|");
			}
			if (COL - 1 == j) {
				printf("\n");
			}
		} 
		// 上面这个部分 负责打印 [1.0],[1.1],[1.2] 这三行
		//============================================
		// 下面这个部分 负责打印 [0.0],[0.1] 这两行
		if (i < ROW - 1) {
			for (j = 0; j < COL; j += 1) {
				printf("---");
				if (j < COL - 1) {
					printf("|");
				}
				if (COL - 1 == j) {
					printf("\n");
				}
			}
		}
	}
}

void computer_play_chess(char board[ROW][COL]) { // 电脑随机下子
	printf("电脑下\n");
	int x = 0;
	int y = 0;
	while (1) {
		x = rand() % COL;
		y = rand() % ROW;
		if (board[y][x] == ' ') { // 判断棋盘随机位置是否为空
			board[y][x] = computer_board;
			break;
		}
	}
}

void play_chess(char board[ROW][COL]) {
	int y = 0;
	int x = 0;
	up:
	printf("请输入x,y坐标>:");
	scanf("%d%d", &x, &y);   // 要注意在C语言中,第一个位置下标为0
	if (board[y - 1][x - 1] != ' ' && 1 <= x && x <= COL && 1 <= y && y <= ROW) {
		printf("请重新输入\n");
		goto up;
	}
	board[y - 1][x - 1] = play_board;
}

char judge(char board[ROW][COL]) {
	int y = 0;
	int x = 0;
	for (y = 0; y < ROW; y += 1) { // 判断三行是否有相同的三个子
		if (board[y][0] != ' ' && board[y][0] == board[y][1] && board[y][1] == board[y][2]) {
			return board[y][0];
		}
	}
	for (x = 0; x < COL; x += 1) { // 判断三竖是否有相同的三个子
		if (board[0][x] != ' ' && board[0][x] == board[1][x] && board[1][x] == board[2][x]) {
			return board[0][x];
		}
	}
	if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
		return board[1][1];  // 判断左上到右下是否有相同的三个子
	}
	if (board[2][0] != ' ' && board[2][0] == board[1][1] && board[1][1] == board[0][2]) {
		return board[1][1];  // 判断左下到右上是否有相同的三个子
	}
	for (y = 0; y < ROW; y += 1) { // 在经过胜负判断后,发现目前没有获胜的一方
		for (x = 0; x < COL; x += 1) { // 检查棋盘是否还有为填满的位置
			if (' ' == board[y][x]) {  // 如果是代表局势还要继续对弈下去
				return '?';
			}
		}
	}
	return '!';    // 在没有获胜的一方时,且棋盘被填满,代表平局
}

void play(void) { // 游玩过程 
	//先打印棋盘,需要一个二维数组,还要先初始化(填充‘空格’) 
	char board[ROW][COL] = { 0 }; // 这是将来的棋盘内容
	init_board(board); // 初始化棋盘
	//print_board(board); // 打印棋盘来看看,写完后注释掉
	// 以上是棋盘的准备
	
	// 以下是下棋过程
	// 玩家下 *, 电脑下 #
	// 设win返回值为 * 代表玩家胜利
	// 设win返回值为 # 代表电脑胜利
	// 设win返回值为 ?代表继续
	// 设win返回值为 !代表平局
	char win = 0;
	while (1) { // 给个死循环 在局势判断不为 '?' 时跳出循环
		// 电脑下   
		computer_play_chess(board);
		// 打印棋盘
		print_board(board);
		system("pause");// 暂停一下
		// 判断局势
		win = judge(board);
		if ('?' != win) {
			break;
		}
		// 玩家下
		play_chess(board);
		// 打印棋盘
		print_board(board);
		system("pause"); // 暂停一下
		// 判断局势
		win = judge(board);
		if ('?' != win) {
			break;
		}
	}
	if ('*' == win) {
		printf("你获胜\n");
	}
	else if ('#' == win) {
		printf("电脑获胜\n");
	}
	else if ('!' == win) {
		printf("平局\n");
	}
}

int main(void) {
	srand((unsigned int)time(NULL)); 
	// 利用时间戳作为随机数生成函数的起点
	int menu = 0;                    // 为后面电脑随机下子,做准备
	do {
		Menu(); // 首先做个菜单
		scanf("%d", &menu);
		if (1 == menu) {
			play();
		}
	} while (menu);
	
	return 0;
}