【万字讲解C语言入门小游戏】——三子棋
今天给大家介绍一个C语言入门小游戏——三子棋。
那么有的人可能会说:”网上这么多介绍三子棋的博客,我为什么要看你的啊。”
问的好,那是因为我的这篇博客不仅详细、老少皆宜,而且我还下不赢我写的代码(不是) 。
废话不多说,开整!
文章目录
- 游戏运行逻辑
- 游戏主体的实现
- 整体思路
- 代码分析
- 初始化棋盘:InitBoard(char board[ROW][COL], int row, int col)
- 打印棋盘:DisplayBoard(char board[ROW][COL], int row, int col)
- 玩家下棋:PlayerMove(char board[ROW][COL], int row, int col)
- 优化了的电脑下棋:ComputerMove(char board[ROW][COL], int row, int col)
- 判断胜利的条件:IsWin(char board[ROW][COL], int row, int col)
- 游戏运行实况
游戏准备
为了实现三子棋游戏,并且有一个良好的编程习惯,我们需要三个文件分辨存储我们需要的代码:
- main.c —— 游戏主体结构
- function.c —— 游戏函数的实现
- extern.h —— 头文件,函数的声明
这里我们先给出游戏整体的代码,大家可以先看一眼,我们在下面一一分解做出详细介绍:
主函数(main.c)代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include"extern.h"void game()
{char board[ROW][COL];//创建一个二维数组用来保存数据InitBoard(board, ROW, COL);//初始化棋盘,让二维数组里的值都为空DisplayBoard(board, ROW, COL);//显示棋盘char ret = 0; //在循环外部创建retprintf("请输入下棋的位置,格式为 数字空格数字 如“1 1”\n");while (1){//玩家的回合PlayerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);ret = IsWin(board, ROW, COL);if (ret != 'c')break;//电脑的回合printf("电脑走:\n");ComputerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);printf("\n");ret = IsWin(board, ROW, COL);if (ret != 'c')break;}if (ret == '*')printf("玩家获胜!\n");if (ret == '#')printf("电脑获胜!\n");if (ret == 'e')printf("平局!\n");DisplayBoard(board, ROW, COL);printf("\n");
}int main()
{int input = 0;srand((unsigned int)time(NULL));do{menu();printf("要来一局三子棋吗?\n");scanf("%d", &input);printf("\n");switch (input){case 1:game();break;case 0:printf("退出程序");break;default:printf("输入有误,请重新输入!\n");break;}} while (input);return 0;
}
功能函数(function.c)代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"extern.h"
void menu()
{printf("**********************************\n");printf("******* 1.game 0.exit *********\n");printf("**********************************\n");
}
void InitBoard(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 DisplayBoard(char board[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){printf(" %c ", board[i][j]);if (j < col - 1){printf("|");}}printf("\n");if (i < row - 1){for (int j = 0; j < col; j++){printf("---");if (j < col - 1){printf("|");}}printf("\n");}}
}void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0; int y = 0;while (1){printf("请选择下的位置:\n");scanf("%d %d", &x, &y);if (x >= 1 && x <= 3 && y >= 1 && y <= 3){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else{printf("输入位置已被占用,请重新输入\n");}}else{printf("输入位置有误,请重新输入\n");}}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{//按行检查有没有已经下两个的for (int i = 0; i < row; i++){int flag = 0;for (int j = 0; j < col; j++){if (board[i][j] == '*'){flag++;}}if (flag == 2){for (int j = 0; j < col; j++){if (board[i][j] == ' '){board[i][j] = '#';return 0;}}}}//按列检查有没有已经下两个的for (int i = 0; i < col; i++){int flag = 0;for (int j = 0; j < row; j++){if (board[j][i] == '*'){flag++;}}if (flag == 2){for (int j = 0; j < row; j++){if (board[j][i] == ' '){board[j][i] = '#';return 0;}}}}//检查左斜有没有已经下两个的if (board[0][0] == board[1][1] || board[1][1] == board[2][2] || board[0][0] == board[2][2]){for (int i = 0; i < row; i++){if (board[i][i] == ' '){board[i][i] = '#';return 0;}}}//检查右斜有没有已经下两个的if (board[0][2] == board[1][1] || board[1][1] == board[2][0] || board[0][2] == board[2][0]){int right = 2;for (int i = 0; i < row; i++){if (board[i][right] == ' '){board[i][right] = '#';return 0;}right--;}//如果中心为空,则抢先下中心if (board[1][1] == ' ') {board[1][1] = '#';return 0;}//不符合上诉任一条件则随机下棋。//这里可以写成其他的抢先占据优势位置的方法。else {int x = 0;int y = 0;while (1){x = rand() % row;y = rand() % col;if (board[x][y] == ' '){board[x][y] = '#';return 0;}}}
}int IsFull(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++){if (board[i][j] == ' '){return 0;}}}return 1;
}char IsWin(char board[ROW][COL], int row, int col)
{int i = 0;//判断行for (i = 0; i < row; i++){if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' '){return board[i][1];}}//判断列for (i = 0; i < col; i++){if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' '){return board[1][i];}}//判断斜线;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];}int ret = IsFull(board, ROW, COL);if (ret == 1){return 'e';//棋盘满了返回e}return 'c'; //棋盘没有满且没有赢家返回c
}
头文件(extern.h)代码:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//打印游戏界面extern void menu();
//初始化棋盘,让二维数组里的值都为空
extern void InitBoard(char board[ROW][COL],int row, int col);
//显示棋盘
extern void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
extern void PlayerMove(char board[ROW][COL], int row, int col);//电脑下棋,电脑不随机下棋
extern void ComputerMove(char board[ROW][COL], int row, int col);//判断输赢
extern char IsWin(char board[ROW][COL], int row, int col);
本文的介绍正式开始!
游戏运行逻辑
void menu()
{printf("**********************************\n");printf("******* 1.game 0.exit *********\n");printf("**********************************\n");
}
int main()
{int input = 0;srand((unsigned int)time(NULL));do{menu();printf("要来一局三子棋吗?\n");scanf("%d", &input);printf("\n");switch (input){case 1:game();break;case 0:printf("退出程序");break;default:printf("输入有误,请重新输入!\n");break;}} while (input);return 0;
}
当我们开始一局游戏时,首先出现在界面上的应该是游戏菜单,在这里我们使用menu()
函数实现该功能。
当程序开始运行后,先打印出菜单界面:
输入我们的选择(1 or 0)进入switch
语句,如果选择为0则退出游戏程序,如果选择为1则进入case 1
开始运行游戏程序。如果输入其他的字符,则会提示重新输入!
游戏主体的实现
当我们的选择为1(来一局游戏)后,程序就会进入到game函数里。
我们来看一看game函数的实现思路:
#define ROW 3
#define COL 3
void game()
{char board[ROW][COL];//创建一个二维数组用来保存数据InitBoard(board, ROW, COL);//初始化棋盘,让二维数组里的值都为空DisplayBoard(board, ROW, COL);//显示棋盘char ret = 0; //在循环外部创建retprintf("请输入下棋的位置,格式为 数字空格数字 如“1 1”\n");while (1){//玩家的回合PlayerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);printf("\n");ret = IsWin(board, ROW, COL);if (ret != 'c')break;//电脑的回合ComputerMove(board, ROW, COL);DisplayBoard(board, ROW, COL);printf("\n");ret = IsWin(board, ROW, COL);if (ret != 'c')break;}if (ret == '*')printf("玩家获胜!\n");if (ret == '#')printf("电脑获胜!\n");if (ret == 'e')printf("平局!\n");DisplayBoard(board, ROW, COL);printf("\n");
}
我们先使用宏定义行和列的数量,这样方便我们在日后将代码改为其他规格的棋盘。
整体思路
在game()函数里,我们需要创建一个二维数组来保存数据,这是因为一个3*3的二位数组在逻辑结构上和我们的三子棋棋盘形状是一样的,方便我们进行代码的编写。
数组创建后,我们需要初始化棋盘 InitBoard(board, ROW, COL);
,即将数组里面的值都初始化为空格(若不初始化,数组里保存的是随机值)。
初始化之后,我们要将棋盘打印出来 DisplayBoard(board, ROW, COL);
,这里的打印不是直接打印数组内容,我们还需要加入一些横线和竖线用来分隔“棋子”。
接着,便是游戏的过程:玩家先开始落子,电脑随后落子。每次落子都要打印棋盘并判断输赢,出现胜者或平局后游戏程序结束!
代码分析
初始化棋盘:InitBoard(char board[ROW][COL], int row, int col)
void InitBoard(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] = ' ';}}
}
这里较为容易,只需要传参,使用两层循环,将数组内的值都赋值为空即可。
打印棋盘:DisplayBoard(char board[ROW][COL], int row, int col)
根据需求,我们需要一个双层循环来打印行和列:
void DisplayBoard(char board[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){printf(" %c ", board[i][j]);if (j < col - 1) //如果不判断的话,会在末尾多打印一个竖线{printf("|");}}printf("\n");//打印分割每行棋子的横线if (i < row - 1)//同理,不判断会多打印一行横线{for (int j = 0; j < col; j++){printf("---");if (j < col - 1){printf("|");}}printf("\n");}}
}
结果如图所示:
此时我们可以想想上文提到的宏定义ROW和COL,将这两个改为10会打印:
无需改动其他参数,我们就得到了一个10*10的棋盘!
玩家下棋:PlayerMove(char board[ROW][COL], int row, int col)
void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0; int y = 0;while (1){printf("请选择下的位置:\n");scanf("%d %d", &x, &y);if (x >= 1 && x <= 3 && y >= 1 && y <= 3){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else{printf("输入位置已被占用,请重新输入\n");}}else{printf("输入位置有误,请重新输入\n");}}
}
实现该功能共有三点:
- 接收玩家输入的地址信息并判断(数组元素应该是玩家输入值-1)
- 判断玩家要下的位置是否已经有棋子
- 将玩家的棋子地址保存到二维数组的相应位置
为了方便玩家确定落子的位置,站在玩家的角度思考,每行每列的下标都是从1开始到3为止。
下面行标,右边列表 | 1 | 2 | 3 |
---|---|---|---|
1 | 棋子 | 棋子 | 棋子 |
2 | 棋子 | 棋子 | 棋子 |
3 | 棋子 | 棋子 | 棋子 |
优化了的电脑下棋:ComputerMove(char board[ROW][COL], int row, int col)
为什么我在本文开头说我下不赢电脑呢?,答案就在下面的代码中,对于电脑的落子我做出了一些优化,让电脑能够堵玩家的棋子,减少了电脑输得可能性(ps:所以我大多只能和电脑平局)。当然该处代码还是不够完善,仅仅只能堵玩家的棋子,不能做到每一步都占据棋盘的优势位且不会主动争夺胜利(往往会为了堵玩家不去连成三子)。
void ComputerMove(char board[ROW][COL], int row, int col)
{//按行检查有没有已经下两个的棋子for (int i = 0; i < row; i++){int flag = 0;for (int j = 0; j < col; j++){if (board[i][j] == '*'){flag++;}}if (flag == 2){for (int j = 0; j < col; j++){if (board[i][j] == ' '){board[i][j] = '#';return 0;}}}}//按列检查有没有已经下两个的棋子for (int i = 0; i < col; i++){int flag = 0;for (int j = 0; j < row; j++){if (board[j][i] == '*'){flag++;}}if (flag == 2){for (int j = 0; j < row; j++){if (board[j][i] == ' '){board[j][i] = '#';return 0;}}}}//检查左斜有没有已经下两个的if (board[0][0] == board[1][1] || board[1][1] == board[2][2] || board[0][0] == board[2][2]){for (int i = 0; i < row; i++){if (board[i][i] == ' '){board[i][i] = '#';return 0;}}}//检查右斜有没有已经下两个的if (board[0][2] == board[1][1] || board[1][1] == board[2][0] || board[0][2] == board[2][0]){int right = 2;for (int i = 0; i < row; i++){if (board[i][right] == ' '){board[i][right] = '#';return 0;}right--;}//如果中心为空,则抢先下中心if (board[1][1] == ' ') {board[1][1] = '#';return 0;}//不符合上诉任一条件则随机下棋。//这里可以写成其他的抢先占据优势位置的方法。else {int x = 0;int y = 0;while (1){x = rand() % row;y = rand() % col;if (board[x][y] == ' '){board[x][y] = '#';return 0;}}}
}
另一种版本
void ComputerMove(char board[ROW][COL], int row, int col)
{//按行检查有没有已经下两个的for (int i = 0; i < row; i++){if (board[i][0] == board[i][1] || board[i][1] == board[i][2] || board[i][0] == board[i][2]){for (int j = 0; j < col; j++){if (board[i][j] == ' '){board[i][j] = '#';return 0;}}}}//按列检查有没有已经下两个的for (int i = 0; i < col; i++){if (board[0][i] == board[1][i] || board[1][i] == board[2][i] || board[0][i] == board[2][i]){for (int j = 0; j < row; j++){if (board[j][i] == ' '){board[j][i] = '#';return 0;}}}}//检查左斜有没有已经下两个的if (board[0][0] == board[1][1] || board[1][1] == board[2][2] || board[0][0] == board[2][2]){for (int i = 0; i < row; i++){if (board[i][i] == ' '){board[i][i] = '#';return 0;}}}//检查右斜有没有已经下两个的if (board[0][2] == board[1][1] || board[1][1] == board[2][0] || board[0][2] == board[2][0]){int right = 2;for (int i = 0; i < row; i++){if (board[i][right] == ' '){board[i][right] = '#';return 0;}right--;}//如果中心为空,则抢先下中心if (board[1][1] == ' ') {board[1][1] = '#';return 0;}//不符合上诉任一条件则随机下棋。//这里可以写成其他的抢先占据优势位置的方法。else {int x = 0;int y = 0;while (1){x = rand() % row;y = rand() % col;if (board[x][y] == ' '){board[x][y] = '#';return 0;}}}
}
注:
该函数随机落子的部分需要使用两个头文件:
- <stdlib.h>
- <time.h>
判断胜利的条件:IsWin(char board[ROW][COL], int row, int col)
//判断棋盘是否满了
int IsFull(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++){if (board[i][j] == ' '){return 0;}}}return 1;
}
//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{int i = 0;//判断行for (i = 0; i < row; i++){if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' '){return board[i][1];}}//判断列for (i = 0; i < col; i++){if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' '){return board[1][i];}}//判断斜线;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];}int ret = IsFull(board, ROW, COL);if (ret == 1){return 'e';//棋盘满了返回e}return 'c'; //棋盘没有满且没有赢家返回c
}
IsWin函数的返回值有四种
- ‘e’,棋盘满了的情况,平局
- ‘c’,棋盘没有满且没有胜者,游戏继续
- ‘*’,玩家获胜
- ‘#’,电脑获胜
游戏运行实况
(ps:这其实是我故意让电脑赢的,是的没错)
以上就是三子棋游戏的全部内容,相信大家看到这里应该也能够独立的完成属于自己专有的“三子棋游戏”,也相信大家在这过程中对于C语言,对于编程有了更进一步的看法。
很高兴能够为大家带来帮助,码字不易,如果大家觉得我这篇文章有帮助的话,可以给个
【万字讲解C语言入门小游戏】——三子棋相关推荐
- C语言趣味小游戏——三子棋
全篇无任何废话,本文的解释大多数都在代码段中,所以一定要看代码,边看边学边理解. 这只是初学者入门的一个小游戏,不难懂,没有什么复杂的内容 可以先学习一下比三子棋还简单的猜数字小游戏 C语言趣味小游戏 ...
- 用c语言实现小游戏三子棋
小游戏之三子棋 小游戏之三子棋 三子棋的基本玩法 游戏制作思路 制作步骤 1.菜单 2.初始化棋盘 3.打印棋盘 4.玩家下棋 5.电脑下棋 6.判断胜利 完整代码 小结 三子棋的基本玩 ...
- C语言简易小游戏--三子棋
小时候,在学校和小伙伴除了互相追逐着玩闹玩游戏以外,相信很多小伙伴们也有和朋友在课堂上拿一张纸玩井字棋的美好回忆.(如下图酱紫啦~) 那今天呢,我们就用C语言制作一款简易的井字棋来练习一下对代码的感觉 ...
- (超详解)C语言实现小游戏三子棋
目录 一,实现目标 二,整体思路 1.创立三个文件:text.c(实现测试的逻辑) ...
- 童年小游戏三子棋(C语言数组实现)
目录 认识游戏 游戏框架 游戏实现 基本构思 棋子 棋盘 下棋 判断输赢 效果展示 全部代码 游戏效果 认识游戏 三子棋是黑白棋的一种.三子棋是一种民间传统游戏,又叫九宫棋.圈圈叉叉.一条龙.井字棋等 ...
- 趣味益智小游戏 三子棋+五子棋 优化版(可任意选择棋盘大小)
前言 今天牛牛给大家分享的是c语言实现三子棋和五子棋游戏,初学者可能有些不理解的地方,记得私信提问哦,牛牛会一 一回答的. 目录 前言 一.游戏介绍 二.游戏设计思路 2.1 主函数测试区(test. ...
- 简单小游戏------三子棋
目录 前言 一.整体的逻辑 创建菜单 创建棋盘 打印棋盘 玩家下棋 电脑下棋 判断输赢 整合代码 game.c test.c game.h 总结 前言 经典数组游戏三子棋 一.整体的逻辑 1.创建菜单 ...
- 【C语言入门小游戏】三子棋
快点点赞 目录 文本目录 前言 一.游戏整体思路 二.代码实现以及思路 1.棋盘的初始化 2.棋盘的打印 3.玩家下棋 4.电脑自动下棋(智障化下棋) 5.判断输赢 三.代码的运行 前言 相信很多学习 ...
- C语言小游戏——三子棋(井字棋)
文章目录 前言 一.前期准备 二.游戏整体结构及思路 1.程序主体结构: 2.代码解读 3.完整源代码 总结 前言 相信大家小时候都玩过井字棋小游戏,拿石子在地上画或是用笔在纸上画······今天我们 ...
- c语言小游戏 三子棋,C语言实现简单的三子棋小游戏
学习完数组就能用C语言写出的简单小游戏: 编译器使用的为VS2013 代码分为三个部分 1.头文件 game.h 2.源文件 game.c 用来存放游戏中所有的函数 3.源文件 test.c 用来存放 ...
最新文章
- python tk text scrollbar_tk.Scrollbar控件的使用
- solaris下ftp配置说明
- linux ssh密钥认证
- sybase 事务插入时不可查询_InnoDB事务与锁
- linux usleep 线程控制权_linux多线程同步—信号量
- ES6之主要知识点(二) 变量的解构赋值。默认值
- 如何调查一家公司是否靠谱?
- CASE WHEN 小结
- Android驱动开发第一章
- 条码扫描二维码扫描—ZXing android 改进版本
- 20200210_logistic回归来预测违约的概率
- 通过反编译深入理解Java String及intern
- 营业执照、组织机构代码、税务登记号规则
- PPT文件限制编辑如何取消
- C++实现装饰者模式Decorator Pattern
- 苹果确认了!iPhone 将改用 USB-C 接口
- django JsonResponse返回中文时显示unicode编码(\u67e5\u8be2)
- ffmpeg+h264_nvenc+vs2019配置编译
- RJ45和RJ48的区别
- 商业级、工业级、军品级、宇航级CPU有着不同标准