三子棋(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-原生态-文件上传-版本缺陷&配置缺陷 文件上传安全指的是攻击者通过利用上传实现后门的写入连接后门进行权限控制的安全问题…

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…

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

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

工程文件参考——STM32+HAL+SPI主从机通讯

文章目录 前言CubeMX设置SPI设置NSS设置 SPI从机代码SPI主机代码 前言 关于如何简单的写一个稳定的SPI主从机通讯&#xff0c;思路很简单 1、SPI高速传输的时候很容易出现错位之类的问题&#xff0c;CRC的校验首先是必要的。在STM32中SPI使用DMA通讯可以自动执行CRC的校验&…

Linux——Harbor(容器镜像 管理项目)

镜像拉取存在一定的问题&#xff0c;出现原因在于&#xff1a; 使用官方公共仓库中的镜像。 拉取的镜像&#xff0c;主要保存在一下仓库中&#xff1a; docker.io //Docker hub 最大的官方维护的公共镜像仓库&#xff0c;一般都会提供所有项目的最新版镜像&#xff0c;镜像…

springboot 整合 快手 移动应用 授权 发布视频 小黄车

前言&#xff1a; 因快手文档混乱&#xff0c;官方社区技术交流仍有很多未解之谜&#xff0c;下面3种文档的定义先区分。 代码中的JSON相关工具均用hutool工具包 1.快手 移动双端 原生SDK 文档https://mp.kuaishou.com/platformDocs/develop/mobile-app/ios.html 2.快手 Api 开…

物联网智能项目(含案例说明)

物联网&#xff08;Internet of Things&#xff0c;简称IoT&#xff09;智能项目是指利用物联网技术将各种物理设备、传感器、软件、网络等连接起来&#xff0c;实现设备之间的互联互通&#xff0c;并通过数据采集、传输、处理和分析&#xff0c;实现智能化管理和控制的项目。以…

Qt入门教程:创建我的第一个小程序

本章教程&#xff0c;主要介绍如何编写一个简单的QT小程序。主要是介绍创建项目的过程。 一、打开QT软件编辑器 这里使用的是QT5.14.2版本的&#xff0c;安装教程参考以往教程&#xff1a;https://blog.csdn.net/qq_19309473/article/details/142907096 二、创建项目 到这里&am…

【Docker】03-自制镜像

1. 自制镜像 2. Dockerfile # 基础镜像 FROM openjdk:11.0-jre-buster # 设定时区 ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 拷贝jar包 COPY docker-demo.jar /app.jar # 入口 ENTRYPOINT ["ja…

Flutter应用解析(一)

1、创建项目 1.1 新建 1.2 选择Flutter SDK的位置 1.3 项目名称 英文单词加下划线起名规范&#xff0c;其他默认即可。 1.4 点击运行 发生报错显示我们的JAVA版本不符合 1.5 更改版本设置 1.6 再次启动项目 2、分析页面代码 以下是lib/main.dart的源代码&#xff08;为了阅…

【机器学习】朴素贝叶斯算法|商品评论情感分析案例介绍及代码实现

文章目录 朴素贝叶斯算法朴素贝叶斯算法介绍概率数学基础复习朴素贝叶斯算法-利用概率值进行分类的一种机器学习算法贝叶斯公式朴素贝叶斯算法拉普拉斯平滑系数 朴素贝叶斯API案例分析流程数据集代码实现运行结果 朴素贝叶斯算法 朴素贝叶斯算法介绍 概率数学基础复习 条件概…