写在前面(前言)

1. 何为三子棋:

三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。

2. 为什么要和人工智障玩三子棋:

这个问题也困扰了我很久,如果我和朋友一起玩的话,肯定是和棋,但人工智障我有概率能赢呀,计算机的运行是由人预先编制的,很简单,因为我还没学到Alphago那种强人工智能,只能欺负欺负简单的人工智障了。(ps.关于计算机的运行为什么是人预先编制的后期我会出一篇博客,关注我,不迷路~~)

~~~~~~~~~~~~~~~~~~~~~~~~~~正文开始~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

一、制作三子棋游戏的思路

  1. 制作游戏单(menu),让玩家选择玩游戏或者退出游戏。
  2. 进入游戏后初始化棋盘并进行打印。
  3. 三子棋函数的创立
  4. 玩家先落子。
  5. 计算机随机落子。
  6. 判断哪一方胜利。
  7. 玩家选择是否还玩游戏。(自动就会跳出来了)

二、详细步骤(附每一步代码)

要实现此游戏,需要创建几个模块,当然是为了方便啦,一个头文件和两个源文件。

  1. test.c(测试逻辑)
  2. game.h.c(函数头文件)
  3. game.c(函数内容)
    (ps.括号里的命名比较具象,为了让读者能够更好理解该模块表达的意思)

(一)游戏单的创建

void menu() {printf("\n");printf("这是个三子棋的游戏,冒险者你准备好了吗?\n");printf("如果你准备好了请扣个1,没准备好请扣个0\n");printf("1.直接进入\n");printf("0.那退出吧\n");
}
void test() {int input = 0;do   //这里是为了实现游戏的重复进行 {int input = 0;menu();printf("请选择:>");scanf("%d", &input);  //玩家输入1或者0显示玩不玩游戏switch (input) {   //switch是分支选择是否开始玩游戏case 1:printf("开始游戏\n");game();   //game()是这个游戏的程序开始玩的函数break;case 0:printf("退出游戏\n");break;default:printf("输入错误\n");break;}} while (input);  //i是输入的是1或者0,此时,非0的意思是继续游戏,0是退出游戏
}
int main() {menu();test();return 0;
}

大家到这里就有疑惑了,那个game();是个什么东东,这找了半天也没发现这个game();的表达式呀!客官且听我分析,在该文件中还有一个未定义的game();在抬头,后面会有模块写出game()函数的。

(二) 初始化棋盘并打印棋盘

首先,在运用一个函数的时候,不仅仅要定义一个函数,而且还要声明它,就好比你辛辛苦苦写完了一本实验报告,但是忘记给老师了,老师就在实验报告这一栏的评分打零分。显而易见,三子棋的棋盘是一个3*3的二维数组。(ps.函数为何要声明,是因为计算机运行这个代码是从上往下逐级取指令,在后期我也会出一篇博客讲关于函数栈帧和函数声明和定义的博客)
代码如下:

1. 初始化棋盘:

  1. 头文件game.h.c中:
#define ROW 3   //采用宏定义行(ROW),这是因为可以更改宏定义后面的数字使得玩N字棋
#define COL 3   //采用宏定义列(COL),理由如上
  1. 源文件game.c中
void chu_shi_hua_board(char board[ROW][COL], int row, int col) {for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {board[i][j] = ' ';  //此时是定义棋盘里面的所有格子为空}}
}

此时大家可能有疑惑了,为什么这个函数就凭空出现了呢?大家不要着急,此函数的声明在头文件game.h.c中显示,详情看下面的代码。
那定义完棋盘了以后,发现,怎么打印了一堆空格?所以,接下来来进行在每个空格中打印分割线吧!

2. 打印棋盘:

void da_yin_board(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++) {  //i遍历行数int j = 0;for (j = 0; j < col; j++) {  //j遍历列数printf(" %c ", board[i][j]);if (j < col - 1) {  //最后一行多了一个|,用判断语句j<col-1printf("|");}}printf("\n");if (i < row - 1) {    //最下面多了个---for (j = 0; j < col; j++) {printf("---");if (j < col - 1) {   //最右边多了个|printf("|");}}printf("\n");}}
}在这里插入代码片

看到这里,大家可能又有疑惑了,这串代码什么意思?那就让我在下面放上两串代码:

printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
printf("---|---|---\n");

很轻松的看的出来,board[i][j]对应的是初始化中的空格,刚好对应“—”中的最中间的那个“-”,其中的if(j<col-1)和if (i < row - 1)是因为如果没有这个语句,右边或者底下会多出来一个符号,不美观!强迫症必须把它对齐!!!

在这里解释一下棋盘是以什么组合形式打印的:
错误例子:
这个组合形式有点诡异,如果你仔细一想,这怎么把顶上三个组合在一起了?如果宏定义定的行列不是3呢?是5呢?是10呢?结果发现只有三列!是不对滴!

正确的组合:

以一个大格子和一个竖线所组合的为一个组合,是正确滴!

(三)玩家下棋

闲话不多说,直接上代码:

void wan_jia_turn_board(char board[ROW][COL], int row, int col) {printf("玩家下棋\n");while (1) {      //while(1)就是个死循环,永远满足条件,永远循环,因为1(非0)为真printf("请输入要下棋的坐标点位:>");int x = 0;int y = 0;scanf("%d %d", &x, &y); //点的坐标if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) {  //程序小白不知道数组是从0开始的,故从1开始,下面要减去1if (board[x - 1][y - 1] == ' ') {board[x - 1][y - 1] = '*';  //空格就代表只有个空格,加上玩家下的棋子break;}else {printf("坐标点位被占用,请重新输入\n");}}else {printf("输入非法\n");}}
}

(四)计算机下棋

void ji_suan_ji_turn_board(char board[ROW][COL], int row, int col) {printf("计算机下棋\n");while (1) {int x = rand() % row;  //x为rand模上个row范围为(0到2)int y = rand() % col;  //范围也是(0到2)if (board[x][y] == ' ') {board[x][y] = 'o';break;}}
}

此处随机数解释过于多,简单介绍为模3是模出来余数,范围是0到2,后续我会出一篇博客来向大家介绍一下随机数!

(五)判断输赢

如何判断输赢是此游戏最难的一关,大家要仔细看!四种情况:玩家赢,计算机赢,平局,游戏没结束(继续)。

//判断棋盘是不是满了(欲知static为何物,之后博客来讲解)
static int is_full(char board[ROW][COL], int row, int col) {int i = 0;int j = 0;for (i = 0; i < row; i++) {for (j = 0; j < col; j++) {return 0;}}return 1;
}//判断谁赢
char shei_ying(char board[ROW][COL], int row, int col) {//情况过于多,横相等,竖相等,两个斜对角线中任一一条对角线相等int i = 0;//三列相等for (i = 0; i < row; i++) {if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') {  //三列当中任一一列是相等的且任一一个元素为空格都不行return board[0][i]; //细节是返回的元素正好是判断电脑还是玩家赢的符号}}//三行相等for (i = 0; i < col; i++) {if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') {return board[i][0];}}// \样的对角线相等if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ') {return board[1][1];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') {return board[1][1];}//平局if (is_full(board, row, col) == 1) {return 'P';}//继续,谁都没赢,也没平局return'C';
}

(六)game();

哈哈哈,虽然那么多且听下篇分析,但这个game();里面的内容没有忘记!上代码!

void game() {char ret = 0;//先声明一个二维数组char board[ROW][COL];//初始化棋盘使其为全空格chu_shi_hua_board(board, ROW, COL);//打印棋盘的横线与竖线da_yin_board(board, ROW, COL);while (1) {   //死循环,玩家和计算机交替下棋//玩家下棋wan_jia_turn_board(board, ROW, COL);//打印棋盘的横线与竖线da_yin_board(board, ROW, COL);//判断谁赢了的函数ret = shei_ying(board, ROW, COL);if (ret != 'C') {break;    //跳出此次循环}//计算机下棋的函数ji_suan_ji_turn_board(board,ROW,COL);//打印棋盘的横线与竖线da_yin_board(board, ROW, COL);//判断输赢ret = shei_ying(board, ROW, COL);if (ret != 'C') {break;  //跳出此次循环}}  if (ret == 'o') {printf("电脑赢\n");}else if (ret == '*'){printf("玩家赢\n");}else if (ret == 'P') {printf("平局\n");}
}
//电脑赢:o
//玩家赢:*
//平局:P
//游戏继续:C

代码汇总

game.h.c

#pragma once#define ROW 3   //采用宏定义行(ROW),这是因为可以更改宏定义后面的数字使得玩N字棋
#define COL 3   //采用宏定义列(COL),理由如上//所需要用的库函数(库函数也是由其他函数创立的,涉及到了函数栈帧的创建与销毁)
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<stdlib.h>//初始化棋盘的函数
void chu_shi_hua_board(char board[ROW][COL], int row, int col);//打印棋盘的函数
void da_yin_board(char board[ROW][COL], int row, int col);//玩家先下棋的函数
void wan_jia_turn_board(char board[ROW][COL], int row, int col);//计算机下棋的函数
void ji_suan_ji_turn_board(char board[ROW][COL], int row, int col);//判断谁赢了的函数
char shei_ying(char board[ROW][COL], int row, int col);

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"game.h.c"void menu() {printf("\n");printf("这是个三子棋的游戏,冒险者你准备好了吗?\n");printf("如果你准备好了请扣个1,没准备好请扣个0\n");printf("1.直接进入\n");printf("0.那退出吧\n");
}
void game() {char ret = 0;//先声明一个二维数组char board[ROW][COL];//初始化棋盘使其为全空格chu_shi_hua_board(board, ROW, COL);//打印棋盘的横线与竖线da_yin_board(board, ROW, COL);while (1) {   //死循环,玩家和计算机交替下棋//玩家下棋wan_jia_turn_board(board, ROW, COL);//打印棋盘的横线与竖线da_yin_board(board, ROW, COL);//判断谁赢了的函数ret = shei_ying(board, ROW, COL);if (ret != 'C') {break;    //跳出此次循环}//计算机下棋的函数ji_suan_ji_turn_board(board,ROW,COL);//打印棋盘的横线与竖线da_yin_board(board, ROW, COL);//判断输赢ret = shei_ying(board, ROW, COL);if (ret != 'C') {break;  //跳出此次循环}}  if (ret == 'o') {printf("电脑赢\n");}else if (ret == '*'){printf("玩家赢\n");}else if (ret == 'P') {printf("平局\n");}
}
//电脑赢:o
//玩家赢:*
//平局:P
//游戏继续:Cvoid test() {srand(time(NULL));int input = 0;do   //这里是为了实现游戏的重复进行 {menu();printf("请选择:>");scanf("%d", &input);  //玩家输入1或者0显示玩不玩游戏switch (input) {   //switch是分支选择是否开始玩游戏case 1:/*printf("开始游戏\n");*/game();   //game()是这个游戏的程序开始玩的函数break;case 0:printf("退出游戏\n");break;default:printf("输入错误\n");break;}} while (input);  //i是输入的是1或者0,此时,非0的意思是继续游戏,0是退出游戏
}
int main() {test();return 0;
}

game.c

#define _CRT_SECURE_NO_WARNINGS 1#include"game.h.c"
//初始化空的棋盘
void chu_shi_hua_board(char board[ROW][COL], int row, int col) {for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {board[i][j] = ' ';  //此时是定义棋盘里面的所有格子为空}}
}
//打印棋盘当中的横线和竖线,使得棋盘更加简洁明了
void da_yin_board(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++) {  //i遍历行数int j = 0;for (j = 0; j < col; j++) {  //j遍历列数printf(" %c ", board[i][j]);if (j < col - 1) {  //最后一行多了一个|,用判断语句j<col-1printf("|");}}printf("\n");if (i < row - 1) {    //最下面多了个---for (j = 0; j < col; j++) {printf("---");if (j < col - 1) {   //最右边多了个|printf("|");}}printf("\n");}}
}
//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//printf("---|---|---\n");//玩家下棋
void wan_jia_turn_board(char board[ROW][COL], int row, int col) {printf("玩家下棋\n");while (1) {      //while(1)就是个死循环,永远满足条件,永远循环,因为1(非0)为真printf("请输入要下棋的坐标点位:>");int x = 0;int y = 0;scanf("%d %d", &x, &y); //点的坐标if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) {  //程序小白不知道数组是从0开始的,故从1开始,下面要减去1if (board[x - 1][y - 1] == ' ') {board[x - 1][y - 1] = '*';  //空格就代表只有个空格,加上玩家下的棋子break;}else {printf("坐标点位被占用,请重新输入\n");}}else {printf("输入非法\n");}}
}
//计算机下棋
//随机生成坐标,只要坐标没有被占用,就下棋
void ji_suan_ji_turn_board(char board[ROW][COL], int row, int col) {printf("计算机下棋\n");while (1) {int x = rand() % row;  //x为rand模上个row范围为(0到2)int y = rand() % col;  //范围也是(0到2)if (board[x][y] == ' ') {board[x][y] = 'o';break;}}
}//判断棋盘是不是满了(欲知static为何物,之后博客来讲解)
static int is_full(char board[ROW][COL], int row, int col) {int i = 0;int j = 0;for (i = 0; i < row; i++) {for (j = 0; j < col; j++) {return 0;}}return 1;
}//判断谁赢
char shei_ying(char board[ROW][COL], int row, int col) {//情况过于多,横相等,竖相等,两个斜对角线中任一一条对角线相等int i = 0;//三列相等for (i = 0; i < row; i++) {if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') {  //三列当中任一一列是相等的且任一一个元素为空格都不行return board[0][i]; //细节是返回的元素正好是判断电脑还是玩家赢的符号}}//三行相等for (i = 0; i < col; i++) {if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') {return board[i][0];}}// \样的对角线相等if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ') {return board[1][1];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ') {return board[1][1];}//平局if (is_full(board, row, col) == 1) {return 'P';}//继续,谁都没赢,也没平局return'C';
}

总结

人工智障怎么那么笨啊!玩了那么多把它就是赢不了!
话回正题,此串代码虽然很长很复杂,但是思路很清晰,按照思路来,终有一刻会敲好的,在设计这个代码的时候,要进行分区设计,这样子会让程序更加清晰,还有很多复杂的需要细节的小程序块需要大家多多注意,只要肯吃苦,代码就能敲好!

亟须解决的问题

这个代码看似很完善,可是,有一个小bug,没有处理出来当电脑先下棋还是玩家先下棋,欲知后事如何,且看下篇博客~~~~~~~~

客官,都读到这边了,给个三连吧 ~谢谢~

三子棋(N子棋)游戏的保姆级超详细教程(C语言)相关推荐

  1. shiro550反序列化漏洞原理与漏洞复现(基于vulhub,保姆级的详细教程)

    漏洞原理 本文所有使用的脚本和工具都会在文末给出链接,希望读者可以耐心看到最后. 啥是shiro? Shiro是Apache的一个强大且易用的Java安全框架,用于执行身份验证.授权.密码和会话管理. ...

  2. 手把手教你如何写一个三子棋/N子棋的小游戏

    这里写目录标题 第一步 游戏进入界面 第二步,初始化棋盘 第三步,打印棋盘 第四步,玩家和电脑下棋 第五步,判断输赢 三子棋或者N子棋怎么写? 让我们先来玩一把.再来看看怎么写. 程序运行界面 1为玩 ...

  3. 超详细分解c 语言——实现扫雷游戏(详解)

    之前利用三子棋程序,介绍探讨了 c语言简单游戏编写主要模块及使用方法. 今天,我们就利用这个经典扫雷游戏,来加强对于程序思路的编写及思路. 目录 前引 游戏规则介绍 一.基本游戏思路(游戏实现方法) ...

  4. 联想用u盘重装系统步骤_保姆级重装系统教程在此

    重装系统不用愁,管家给你露一手!保姆级重装系统教程在此,请客官笑纳. 装前必读: 1. "联想原厂系统镜像"程序目前只适用联想或ThinkPad品牌的64位Win10家庭中文版电脑 ...

  5. 如何安装配置JDK(保姆级完美解决教程+附百度网盘链接)

    JDK种类繁多,在这里我以JDK14为例,为什么以JDK14为例呢?当然是为了破解白嫖idea2020旗舰版.^_^^_^(如果大家感兴趣,我后续会一个教程)     首先去甲骨文官方网站下载JDK1 ...

  6. CocosCreator像素鸟小游戏实现(有源码)超详细教程 TS实现小游戏 零基础开发

    CocosCreator像素鸟小游戏实现(有源码)超详细教程 TS实现小游戏 大家中秋国庆快乐哈 前言 老规矩先看效果 源码的获取方式在最下面 对于本游戏来说canvas这样设置最佳哦 游戏实现思路: ...

  7. OidProducer保姆级铺码教程(教你如何自制点读书)

    软件介绍: 1.OidProducer(铺码用软件) 2.Adobe Acrobat DC(打印用软件,打印时一定要选择实际大小,不能放大缩小文件) 步骤简介: 1.先设置(必须认真设置好才开始铺码, ...

  8. 移动硬盘已连接USB我的电脑不显示的全网最细汇总多种解决方法(保姆级图文详细步骤)

    步骤 系列文章 前言 检查设备,确认你有盘符,如果没有盘符下面的方法无用 1.重启设备,禁用后启用设备 2.重装设备,卸载设备后再次插入安装设备 总结 系列文章 提示:转到日常小技巧专栏,观看更多内容 ...

  9. 直接用IDEA刷leetcode的插件--Leetcode editor (保姆级安装示例教程)

    直接用IDEA刷leetcode的插件–Leetcode editor (保姆级安装示例教程) 你是否还在发愁刷 leetcode 不好debug? 你是否还在往IDEA里粘代码调试好再"C ...

最新文章

  1. 二元函数泰勒公式例题_考研数一对二元函数的二阶泰勒公式的要求是了解,那我们要了解到什么程度呢?会出那种类型的题呢?...
  2. python写出的程序如何给别人使用-利用这10个工具,你可以写出更好的Python代码...
  3. 实战测试SO_REUSEADDR选项
  4. 2020-10-25(个人int误区)
  5. ES6 WeakSet数据结构 与Set十分相似
  6. java 8 stream_Java 8 Stream示例
  7. Linux系统有线网络抓包程序
  8. 想辞职专心做自媒体可以吗?有哪些建议吗?
  9. Electron的学习笔记
  10. ROS安装时rosdep init与rosdep update问题解决方法(2022.04.08亲测)
  11. Git:git合并分支
  12. 现代操作系统的基本特性(2)
  13. 下docfetcher先下Java,docfetcher
  14. 仿微信语音输入页面(讯飞语音)
  15. linux rstudio 中文乱码,谈谈R中的乱码(一)
  16. 联发科:心态决定未来走势
  17. 关于北京租房,我想吐槽的一些东西
  18. niagara框架中串口的打开方式
  19. 多元线性回归中的公式推导
  20. VNC Viewer 远程 Unable to connect to VNC Server using your chosen securitysetting. 问题解决

热门文章

  1. Angular 学习(二):Angular 简介
  2. 今天是你的生日,你就快乐些吧
  3. js 获取某一年第多少天是周几
  4. 根据灰度直方图调整图象亮度
  5. WGCNA算法研究笔记
  6. 谷歌黑客语法入门视频教程_入门教程:Google Go入门
  7. html段落布局,如何使用WPS2012中的“段落布局”详细图文介绍
  8. [英语语法]句法之状语从句
  9. 将要到来的三大技术革命与联系
  10. 计算机网络中的冗余部件大大降低了可靠,大学计算机第七篇练习题