三子棋(C 语言)

目录

  • 一、游戏设计的整体思路
  • 二、各个步骤的代码实现
    • 1. 菜单及循环选择的实现
    • 2. 棋盘的初始化和显示
    • 3. 轮流下棋及结果判断实现
    • 4. 结果判断实现
  • 三、所有代码
  • 四、总结

一、游戏设计的整体思路

(1)提供一个菜单让玩家选择人机对战、玩家对战或者退出游戏,且一局结束以后还可以继续选择直到退出。
(2)使用一个 3*3 二维数组来表示一个棋盘,存放双方的落子情况并进行显示。且显示时使用一些符号进行分隔让棋盘更加合理。
(3)初始棋盘应为空,然后提示先手方下棋,要对先手方落子位置进行判断(是否合法,是否该位置已经落子),最后先手方下棋完毕后应检查棋局状态(获胜、平局或继续);同理后手方下棋。
(4)若平局或者任意一方获胜则结束对局,显示结果。

本次代码使用多文件形式,一共有三个文件,一个头文件包含各种声明,一个 .c 文件进行测试,一个 .c 文件实现函数功能。
在这里插入图片描述

二、各个步骤的代码实现

1. 菜单及循环选择的实现

(1)主函数代码的实现

// 头文件
#include <stdio.h>
#include "Tic_tac_toe.h"int main()
{// 所需变量int select = 0;// 选择do{// 菜单menu();// 选择scanf("%d", &select);// 判断switch (select){case 1 :  // 人机对战PVE();break;case 2 :PVP();  // 玩家对战break;case 0 :printf("游戏结束!\n");break;}} while (select);return 0;
}

(2)头文件中的声明

// 菜单
void menu();

(3)函数实现

// 菜单
void menu()
{printf("**************************************\n");printf("**********      1. PVE      **********\n");printf("**********      2. PVP      **********\n");printf("**********      0. exit     **********\n");printf("**************************************\n");
}

(4)运行效果如下
在这里插入图片描述

2. 棋盘的初始化和显示

棋盘的初始化和显示分别设计为两个函数 —— InitBoard()、PrintBoard()。首先,需要在主函数创建棋盘,且大小使用符号常量,方便替换。然后把棋盘的信息传入人机对战和玩家对战函数中,并在头文件和实现文件中进行相应的声明和定义。

(1)主函数创建棋盘

int main()
{// 所需变量int select = 0;char board[ROW][COL] = { 0 };  // 创建棋盘//...
}

(2)头文件进行相应声明

// 初始化棋盘
void InitBoard(char board[][COL], int row, int col);// 打印棋盘
void PrintBoard(char board[][COL], int row, int col);// 人机对战模式
void PVE(char board[][COL], int row, int col);// 玩家对战模式
void PVP(char board[][COL], int row, int col);

(3)实现文件进行定义

// 初始化棋盘
void InitBoard(char board[][COL], int row, int col)
{int i;for (i = 0; i < row; ++i){int j;for (j = 0; j < col; ++j)board[i][j] = ' ';}
}// 打印棋盘
void PrintBoard(char board[][COL], int row, int col)
{// 打印棋盘时,需要适当添加符号分隔int i;for (i = 0; i < row; ++i){int j;for (j = 0; j < col; ++j)printf(" ---");// 下一行printf("\n");for (j = 0; j < col; ++j)printf("| %c ", board[i][j]);// 补齐改行分隔printf("|");// 下一行printf("\n");}// 补齐最后一行分隔for (i = 0; i < col; ++i)printf(" ---");printf("\n");
}// 人机对战模式
void PVE(char board[][COL], int row, int col)
{printf("\n人机对战:\n");// 初始化棋盘InitBoard(board, row, col);// 显示棋盘PrintBoard(board, row, col);
}// 玩家对战模式
void PVP(char board[][COL], int row, int col)
{printf("\n玩家对战:\n");// 初始化棋盘InitBoard(board, row, col);// 显示棋盘PrintBoard(board, row, col);
}

运行效果如下:
在这里插入图片描述

3. 轮流下棋及结果判断实现

这里假设人机对战时,玩家使用符号 ‘#’,机器人使用符号 ‘*’;玩家对战时,一号玩家使用符号 ‘#’,二号玩家使用符号 ‘*’。玩家下棋和机器人下棋分别使用两个函数 —— PMove() 和 EMove()。PMove() 函数需要使用一个额外的变量判断是 1 号玩家下棋,还是 2 号玩家下棋。

判断结果函数 is_win() 需要判断每行是否相同、每列是否相同,倘若其中有一个实现,则有一方获胜,倘若没有则需要使用函数 is_full() 判断棋盘是否已满,若满则平局,否则继续。is_full() 函数也可以直接实现在 is_win() 函数内部。

(1)头文件函数声明

// 玩家下棋
void PMove(char board[][COL], int row, int col, int who);// 机器人下棋
void EMove(char board[][COL], int row, int col);// 判断是否获胜
void is_win(char board[][COL], int row, int col);

(2)实现文件函数定义

// 玩家下棋
void PMove(char board[][COL], int row, int col, char who)
{// 所需变量int x, y;do{// 输入坐标printf("玩家下棋(输入坐标中间用空格隔开):\n");scanf("%d %d", &x, &y);// 判断合法性if ((x < 0 || x > row || y < 0 || y > col) || board[x - 1][y - 1] != ' '){printf("坐标非法,请重新输入!\n");}else{board[x - 1][y - 1] = who;break;}} while (1);
}// 机器人下棋
void EMove(char board[][COL], int row, int col)
{// 机器人是随机下棋的,这里就要获取随机数了// 需要包含头文件 stdlib.h 和 time.h // 设置随机数种子srand((unsigned)time(0));printf("电脑下棋:\n");while (1){int x = rand() % row;  // 0 - row - 1int y = rand() % col;  // 0 - col - 1if (board[x][y] == ' '){board[x][y] = '*';break;}}
}// 判断是否获胜
char is_win(char board[][COL], int row, int col)
{// 判断每行int r = 1;int i;for (i = 0; i < row; ++i){// 重置判断符号r = 1;// 若首字符为空则下一行if (board[i][0] == ' ')continue;// 判断改行int j;for (j = 1; j < col; ++j){r *= (board[i][j - 1] == board[i][j]);// 判断if (!r)break;}if (r)return board[i][0];}// 判断每列int c = 1;int j;for (j = 0; j < col; ++j){// 重置判断符号c = 1;// 首字符为空则下一列if (board[0][j] == ' ')continue;// 判断该列int i;for (i = 1; i < row; ++i){c *= (board[i - 1][j] == board[i][j]);// 判断if (!c)break;}if (c)return board[0][j];}// 判断对角线int d = 1;if (board[0][0] != ' '){for (i = 1; i < row; ++i){d *= (board[i - 1][i - 1] == board[i][i]);// 判断if (!d)break;}if (d)return board[0][0];}if (board[0][col] != ' '){d = 1;for (i = 1; i < row; ++i){d *= (board[i - 1][col - i + 1] == board[i][col - i]);// 判断if (!d)break;}if (d)return board[0][col];}// 判断棋盘是否已满int full = 1;for (i = 0; i < row; ++i){for (j = 0; j < col; ++j){if (board[i][j] == ' '){// 未满return 'C';}}}// 已满return 'D';}

上述代码中 is_win() 函数中所使用的是通用判断方法,即使改变了符号常量 ROW 和 COL 的值也可以是判断。(如果实在理解不了,可以只当 3*3 的特殊情况来实现判断,当然,也可能是我的代码写的不好)。

(3)程序运行结果:
这里值展示人机对战的三种结果:

玩家获胜:
在这里插入图片描述

电脑获胜:
在这里插入图片描述

平均:
在这里插入图片描述

4. 结果判断实现

该代码直接放在了函数 PMove() 和函数 EMove() 中,也可以单独写一个函数。

(1)实现文件函数定义

// 人机对战模式
void PVE(char board[][COL], int row, int col)
{printf("\n人机对战:\n");// 初始化棋盘InitBoard(board, row, col);// 显示棋盘PrintBoard(board, row, col);// 开始下棋int ret = 0;do{// 玩家下棋PMove(board, row, col, '#');// 显示棋盘PrintBoard(board, row, col);// 判断ret = is_win(board, row, col);if (ret != 'C')break;// 机器人下棋EMove(board, row, col);// 显示棋盘PrintBoard(board, row, col);// 判断ret = is_win(board, row, col);if (ret != 'C')break;} while (1);// 判断最终结果if (ret == '#')printf("玩家获胜!\n");else if (ret == '*')printf("电脑获胜!\n");elseprintf("平局!\n");
}// 玩家对战模式
void PVP(char board[][COL], int row, int col)
{printf("\n玩家对战:\n");// 初始化棋盘InitBoard(board, row, col);// 显示棋盘PrintBoard(board, row, col);// 开始下棋int ret = 0;do{// 玩家 1 号下棋PMove(board, row, col, '#');// 显示棋盘PrintBoard(board, col, row);// 判断ret = is_win(board, row, col);if (ret != 'C')break;// 玩家 2 号下棋PMove(board, row, col, '*');// 显示棋盘PrintBoard(board, col, row);// 判断ret = is_win(board, row, col);if (ret != 'C')break;} while (1);// 判断最终结果if (ret == '#')printf("玩家 1 号获胜!\n");else if (ret == '*')printf("玩家 2 号获胜!\n");elseprintf("平局!\n");
}

(2)演示结果
在上一次演示结果中已经体现。

三、所有代码

分三个文件,分别是头文件 Tic_tac_toe.h,测试文件 test.c,和函数实现文件 Tic_tac_toe.c。

(1)Tic_tac_toe.h

// 常量声明
#define ROW 3
#define COL 3// 函数声明// 菜单
void menu();// 初始化棋盘
void InitBoard(char board[][COL], int row, int col);// 打印棋盘
void PrintBoard(char board[][COL], int row, int col);// 玩家下棋
void PMove(char board[][COL], int row, int col, char who);// 机器人下棋
void EMove(char board[][COL], int row, int col);// 判断是否获胜
char is_win(char board[][COL], int row, int col);// 人机对战模式
void PVE(char board[][COL], int row, int col);// 玩家对战模式
void PVP(char board[][COL], int row, int col);

(2)test.c

// 头文件
#include <stdio.h>
#include "Tic_tac_toe.h"int main()
{// 所需变量int select = 0;char board[ROW][COL] = { 0 };// 选择do{// 菜单menu();// 选择scanf("%d", &select);// 判断switch (select){case 1 :  // 人机对战PVE(board, ROW, COL);break;case 2 :PVP(board, ROW, COL);  // 玩家对战break;case 0 :printf("游戏结束!\n");break;default :printf("选择错误请重新选择!\n");break;}} while (select);return 0;
}

(3)Tic_tac_toe.c

// 头文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "Tic_tac_toe.h"// 函数定义// 菜单
void menu()
{printf("**************************************\n");printf("**********      1. PVE      **********\n");printf("**********      2. PVP      **********\n");printf("**********      0. exit     **********\n");printf("**************************************\n");
}// 初始化棋盘
void InitBoard(char board[][COL], int row, int col)
{int i;for (i = 0; i < row; ++i){int j;for (j = 0; j < col; ++j)board[i][j] = ' ';}
}// 打印棋盘
void PrintBoard(char board[][COL], int row, int col)
{// 打印棋盘时,需要适当添加符号分隔int i;for (i = 0; i < row; ++i){int j;for (j = 0; j < col; ++j)printf(" ---");// 下一行printf("\n");for (j = 0; j < col; ++j)printf("| %c ", board[i][j]);// 补齐改行分隔printf("|");// 下一行printf("\n");}// 补齐最后一行分隔for (i = 0; i < col; ++i)printf(" ---");printf("\n");
}// 玩家下棋
void PMove(char board[][COL], int row, int col, char who)
{// 所需变量int x, y;do{// 输入坐标printf("玩家下棋(输入坐标中间用空格隔开):\n");scanf("%d %d", &x, &y);// 判断合法性if ((x < 0 || x > row || y < 0 || y > col) || board[x - 1][y - 1] != ' '){printf("坐标非法,请重新输入!\n");}else{board[x - 1][y - 1] = who;break;}} while (1);
}// 机器人下棋
void EMove(char board[][COL], int row, int col)
{// 机器人是随机下棋的,这里就要获取随机数了// 需要包含头文件 stdlib.h 和 time.h // 设置随机数种子srand((unsigned)time(0));printf("电脑下棋:\n");while (1){int x = rand() % row;  // 0 - row - 1int y = rand() % col;  // 0 - col - 1if (board[x][y] == ' '){board[x][y] = '*';break;}}
}// 判断是否获胜
char is_win(char board[][COL], int row, int col)
{// 判断每行int r = 1;int i;for (i = 0; i < row; ++i){// 重置判断符号r = 1;// 若首字符为空则下一行if (board[i][0] == ' ')continue;// 判断改行int j;for (j = 1; j < col; ++j){r *= (board[i][j - 1] == board[i][j]);// 判断if (!r)break;}if (r)return board[i][0];}// 判断每列int c = 1;int j;for (j = 0; j < col; ++j){// 重置判断符号c = 1;// 首字符为空则下一列if (board[0][j] == ' ')continue;// 判断该列int i;for (i = 1; i < row; ++i){c *= (board[i - 1][j] == board[i][j]);// 判断if (!c)break;}if (c)return board[0][j];}// 判断对角线int d = 1;if (board[0][0] != ' '){for (i = 1; i < row; ++i){d *= (board[i - 1][i - 1] == board[i][i]);// 判断if (!d)break;}if (d)return board[0][0];}if (board[0][col] != ' '){d = 1;for (i = 1; i < row; ++i){d *= (board[i - 1][col - i + 1] == board[i][col - i]);// 判断if (!d)break;}if (d)return board[0][col];}// 判断棋盘是否已满int full = 1;for (i = 0; i < row; ++i){for (j = 0; j < col; ++j){if (board[i][j] == ' '){// 未满return 'C';}}}// 已满return 'D';}// 人机对战模式
void PVE(char board[][COL], int row, int col)
{printf("\n人机对战:\n");// 初始化棋盘InitBoard(board, row, col);// 显示棋盘PrintBoard(board, row, col);// 开始下棋int ret = 0;do{// 玩家下棋PMove(board, row, col, '#');// 显示棋盘PrintBoard(board, row, col);// 判断ret = is_win(board, row, col);if (ret != 'C')break;// 机器人下棋EMove(board, row, col);// 显示棋盘PrintBoard(board, row, col);// 判断ret = is_win(board, row, col);if (ret != 'C')break;} while (1);// 判断最终结果if (ret == '#')printf("玩家获胜!\n");else if (ret == '*')printf("电脑获胜!\n");elseprintf("平局!\n");
}// 玩家对战模式
void PVP(char board[][COL], int row, int col)
{printf("\n玩家对战:\n");// 初始化棋盘InitBoard(board, row, col);// 显示棋盘PrintBoard(board, row, col);// 开始下棋int ret = 0;do{// 玩家 1 号下棋PMove(board, row, col, '#');// 显示棋盘PrintBoard(board, col, row);// 判断ret = is_win(board, row, col);if (ret != 'C')break;// 玩家 2 号下棋PMove(board, row, col, '*');// 显示棋盘PrintBoard(board, col, row);// 判断ret = is_win(board, row, col);if (ret != 'C')break;} while (1);// 判断最终结果if (ret == '#')printf("玩家 1 号获胜!\n");else if (ret == '*')printf("玩家 2 号获胜!\n");elseprintf("平局!\n");
}

四、总结

本次三子棋的代码编写总体来说还可以,基本上都实现了上述功能,其中的某些代码本人均是根据符号常量来编写的,也就是即使符号常量改变,本代码依旧可以实现。当然,本人水平有限,有的地方看不懂可能是本人代码写的太烂,这里先道个歉。

当然代码还有很多可以提升的地方,比如优化棋盘,可以使用鼠标来下棋,提升电脑的水平等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/55114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第二十七篇:传输层讲解,TCP系列一

一、传输层的功能 ① 分割与重组数据 传输层也要做数据分割&#xff0c;所以必然也需要做数据重组。 ② 按端口号寻址 IP只能定位数据哪台主机&#xff0c;无法判断数据报文应该交给哪个应用&#xff0c;传输层给每个应用都设置了一个编号&#xff0c;这个编号就是端口&…

Midjourney官宣网页版免费用!前谷歌大佬祭出AI生图2.0,全网惊艳实测

Midjourney一度稳居AI生图的第一梯队&#xff0c;甚至是很多人心中的Top1。但是Ideogram 2.0的发布&#xff0c;抢夺了Midjourney的荣光&#xff0c;不仅一举拉高了图像生成质量&#xff0c;还打起了价格战。 曾经在AI图像生成领域无可匹敌的领导者Midjourney&#xff0c;终于…

3分钟学会下载 blender

1. blender简介 Blender是一款开源的3D创作套件&#xff0c;它由Blender Foundation维护&#xff0c;并得到了全球志愿者和专业开发者的支持。Blender广泛应用于3D模型的制作、动画、渲染、视频编辑、游戏创建、模拟、 composting以及3D打印等多个领域。 功能特点&#xff1a…

欧盟通过《网络弹性法案》保障联网产品安全

欧盟理事会通过了《网络弹性法案》&#xff08;CRA&#xff09;&#xff0c;这是一项新法律&#xff0c;旨在使含有数字组件的消费产品更加安全使用。 CRA要求 CRA 概述了欧盟范围内针对数字产品的网络安全标准&#xff0c;即直接或间接连接到其他设备或网络的产品。 此类别…

Qt-QDockWidget浮动窗口相关操作(49)

目录 描述 使用 描述 在 Qt 中&#xff0c;浮动窗⼝也称之为铆接部件。浮动窗⼝是通过 QDockWidget类 来实现浮动的功能。浮动窗口⼀般是位于核心部件的周围&#xff0c;可以有多个。 使用 创建我们可以参考下面的语法格式 使用起来也很简单&#xff0c;不过只能创建一个 Q…

DAY47WEB 攻防-PHP 应用文件上传函数缺陷条件竞争二次渲染黑白名单JS 绕过

1、PHP-原生态-文件上传-检测后缀&黑白名单2、PHP-原生态-文件上传-检测信息&类型内容3、PHP-原生态-文件上传-函数缺陷&逻辑缺陷4、PHP-原生态-文件上传-版本缺陷&配置缺陷 文件上传安全指的是攻击者通过利用上传实现后门的写入连接后门进行权限控制的安全问题…

ChatGPT与R语言融合技术在生态环境数据统计分析、绘图、模型中的实践与进阶应用

在短短一年多的时间里&#xff0c;GPT已经在多个领域展现出其独特的价值&#xff0c;特别是在数据统计分析领域。GPT的介入为数据处理、模型构建和结果解释带来了前所未有的便利。与此同时&#xff0c;R语言凭借其开源、自由、免费的特性&#xff0c;成为了统计分析和数据可视化…

LaTeX 对新段落会自动缩进。如果你不希望某些段落有缩进,可以使用以下几种方法来取消缩进:

默认情况下&#xff0c;LaTeX 对新段落会自动缩进。如果你不希望某些段落有缩进&#xff0c;可以使用以下几种方法来取消缩进&#xff1a; 全局取消段落缩进&#xff1a; 在导言区添加如下命令可以取消整个文档中的段落缩进&#xff1a; \setlength{\parindent}{0pt}局部取消段…

05 django管理系统 - 部门管理 - 修改部门

04我们已经实现了新增部门的功能&#xff0c;下面开始修改部门模块的实现。 按道理来说&#xff0c;应该是做成弹框样式的&#xff0c;通过ajax悄咪咪的发数据&#xff0c;然后更新前端数据&#xff0c;但是考虑到实际情况&#xff0c;先用页面跳转的方式实现&#xff0c;后面…

TCP/IP相关

1、关于三次握手、四次挥手和TCP的11种状态&#xff1a; 记住这张图就行了&#xff1a; 2、关于慢启动、拥塞避免、超时重传、快速重传、快速恢复 记住这张图就行了&#xff1a; 一些名词解释&#xff1a; MSS&#xff1a;Maximum Segment Size&#xff0c;最大报文长度 RT…

JDBC存在什么问题?MyBatis是如何解决的?

文章目录 引言JDBC核心组件1、DriverManager2、Connection3、Statement4、ResultSet5、SQLException JDBC存在的问题1、繁琐的代码编写2、手动管理资源3、SQL语句的硬编码4、缺乏对象映射5、事务管理复杂6、缺乏缓存支持 MyBatis如何解决这些问题&#xff1f;1、简化代码编写2、…

鸿蒙开发之ArkUI 界面篇 三十四 容器组件Tabs 自定义TabBar

如果需要修改Tabs的图标和文字之间的距离我们该怎么办呢&#xff1f;好在tabBar是联合类型&#xff0c;提供了自定义tabBar&#xff0c;这里就可以显示特殊图标或者是文字图片&#xff0c;如下图&#xff1a; 这里定义了myBuilder的函数&#xff0c;用了 来修饰&#xff0c;没有…

MongoDB中排序、索引和聚合的详细说明

以下是关于MongoDB中排序、索引和聚合的详细说明&#xff1a; 排序&#xff08;Sort&#xff09; 基本语法 使用db.collection.find().sort({field: direction})方法对查询结果进行排序。其中field是要排序的字段&#xff0c;direction可以是1&#xff08;表示升序&#xff0…

结合大语言模型的机械臂抓取操作学习

一、 大语言模型的机械臂抓取操作关键步骤 介绍如何基于大语言模型实现机械臂在PyBullet环境中的抓取操作&#xff0c;涵盖机器人运动学、坐标系转换、抓取候选位姿生成、开放词汇检测以及大语言模型代码生成等模块。 1. 机器人正逆运动学基本概念 正运动学: 已知机器人的关节…

19 Shell Script awk命令

Shell Script awk命令 一、awk 一&#xff09;awk介绍 ​ awk是一个强大的文本分析工具&#xff0c;相对于grep的查找&#xff0c;sed的编辑&#xff0c;awk在其对数据分析并生成报告时&#xff0c;显得尤为强大。简单来说awk就是把文件逐行的读入&#xff0c;以空格为默认分…

《深度学习》【项目】自然语言处理——情感分析 <上>

目录 一、项目介绍 1、项目任务 2、评论信息内容 3、待思考问题 1&#xff09;目标 2&#xff09;输入字词格式 3&#xff09;每一次传入的词/字的个数是否就是评论的长度 4&#xff09;一条评论如果超过32个词/字怎么处理&#xff1f; 5&#xff09;一条评论如果…

【论文阅读】SRCNN

学习资料 论文题目&#xff1a;Learning a Deep Convolutional Network for Image Super-Resolution&#xff08;学习深度卷积网络用于图像超分辨率&#xff09;论文地址&#xff1a;link.springer.com/content/pdf/10.1007/978-3-319-10593-2_13.pdf代码&#xff1a;作者提出的…

白平衡之乘积通道法

免责声明:本文所提供的信息和内容仅供参考。作者对本文内容的准确性、完整性、及时性或适用性不作任何明示或暗示的保证。在任何情况下,作者不对因使用本文内容而导致的任何直接或间接损失承担责任,包括但不限于数据丢失、业务中断或其他经济损失。 读者在使用本文信息时,应…

zabbix报警设置

文章目录 I 通过脚本媒介,调用钉钉电话API。配置脚本设置脚本媒介II 配置发送邮件的信息案例:配置163邮箱配置接收媒介创建动作知识扩展DING 2.0 发钉钉钉机器人自定义webhookzabbix执行远程命令I 通过脚本媒介,调用钉钉电话API。 zabbix可以通过脚本调用钉钉电话API,需要…

oneAPI学习-使用oneAPI 实现矩阵乘法并分析性能瓶颈

oneAPI学习-使用oneAPI 实现矩阵乘法并分析性能瓶颈 一.相关链接二.oneAPI介绍三.矩阵乘法简介四.环境准备五.获取设备列表六.基础版实现代码解释 七.局部内存实现代码解释 八.性能分析1.运行性能分析2.常见分析类型3.分析结果解读4.优化建议5.清理分析数据 oneAPI学习-使用one…