一-思路组织
1-棋盘构成
三子棋的布局为3*3的棋盘格
我们用二维数组充当棋盘
2-棋盘打印
在数组之间用空格和’|‘号和‘---’来填充模拟视觉上的棋盘效果
例如第一行用空格加数组内容加’|‘号打印:
“’空格‘’数组‘’空格‘’|’‘空格’‘数组’‘空格’‘|’‘空格’‘数组’‘空格’
第二行为‘---’‘|’‘---’‘|’---‘
整体上效果如图:
3-电脑随机下子
电脑随机下子的过程我们用随机数生成函数rand();来生成x与y的坐标。
2-玩家下子
玩家下子的过程要主要C语言中第一个位置下标是0。
5-局势判断
判断过程要要检测三竖和三横,与从左上到右下是否有三个相同的子连续
二-代码实现
1-棋盘构成
宏定义棋盘的大小为3*3
分配一个char类型的二维数组
初始化棋盘内容为空格符号,过程为利用两个for循环一个一个的往数组里面填充空格符号
2-棋盘打印
棋盘打印的过程稍微繁琐一点
首先我们来打印第一行,过程分为三个部分
前两个为 空格+数组+空格+竖杠,最后一个部分为 空格+数组+空格,因为最后一个部分少一个竖杠我们这里要注意,并且还要注意打印一行要加上分行。(仔细观察其中的两个if语句)
第二行就是将先前的 空格+数组+空格 替换成三个小横杠号
也分为三个部分 - - - | 与- - - | 与 - - - 和上面的打印过程几乎相同,也是最后一个部分少打印一个竖杠号。 (仔细观察其中两个if语句)
那么现在请看这张图:
我们能发现 整个棋盘可以分为三个部分
【1.0】与【0.0】这两行可以组成一个部分
【1.1】与【0.1】组成一个部分
【1.2】组成一个部分
仔细对比单行打印过程,整体打印中的【0.0】与【0.1】和单行打印中的竖杠号,都没有在最后一部分中打印出来。(请仔细观察下面这个部分中的if语句)
因此代码实现为
3-电脑随机下子
为了生成随机数,我们需要利用时间戳来作为随机输生成函数的起点
当然,我们还需要引用两个头文件
在电脑下子的时候要将生成的随机数模上棋盘的大小,且要判断棋盘位置是否为空格,如果不是重新生成一次随机数坐标。
代码如下
4-玩家下子
这里要注意的一点就是在C语言中实际的第一个位置以下标0开始,因此我们在判断棋盘位置的时候要将玩家输入的坐标-1;
当然我们也需要判断玩家坐标输入的合法性,在坐标位置有棋子的时候和坐标位置超出棋盘范围的时候,跳转回去并且再次提醒玩家重新输入。
5-局势判断
局势判断是最简单,但是最为繁琐的部分。
因为我们需要判断三横三纵与两个斜着的方向是否有三子连同。
代码设计如下
首先我们要设计这个判断胜负的函数在几种情况下的放回值,如下图所示
接下来是繁琐的代码实现,请仔细阅读
三-代码参考
现在我们已经将三子棋的简单人机对弈的每个部分都介绍完了
考虑到本人的粗心大意的本性,因此在下面附上的参考代码或许有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;
}