- C++ 开发 Web 服务框架
- 1 小时入门增强现实技术
- C++ 实现高性能内存池
- GDB 简明教程
- C++ 实现太阳系行星系统
- C++11/14 高速上手教程
- C 语言实现 Linux Shell 命令解释器
- C++ 打造 Markdown 解析器
- C 语言实现文件类型统计程序
- C 语言实现 Linux touch 命令
- C 语言入门教程
- C 语言实现多线程排序
- 多线程生产者消费者模型仿真停车场
- C++实现运动目标的追踪
- C 语言实现 Linux 网络嗅探器
- 100 行 C++ 代码实现线程池
- C 语言实现聊天室软件
- C 语言实现 Linux who 命令
- C 语言实现 Linux cp 命令
- C++实现第一人称射击游戏
- C++ 实现银行排队服务模拟
- 数据结构(新版)
- 软件工程(C 编码实践篇)
- C 语言制作简单计算器
- C 语言版 flappy_bird
- C 语言编写万年历
- C 语言版扫雷游戏
- C 语言实现一个支持 PHP 的简易 WEB 服务器
- C 语言制作 2048
- C 语言模拟 ATM 自动取款机系统
- Linux 系统编程
- C 语言利用 epoll 实现高并发聊天室
- C 语言快速实现五子棋
- C 语言实现 ping 程序
- 简单词法分析器(C++语言)
第 1 节 让我们开始吧!
一、实验介绍
1. 环境登录
无需密码自动登录,系统用户名 shiyanlou
2. 环境介绍
本实验环境采用带桌面的 Ubuntu Linux 环境,实验中会用到桌面上的程序:
- LX 终端(LXTerminal): Linux 命令行终端,打开后会进入 Bash 环境,可以使用 Linux 命令
- Firefox:浏览器,可以用在需要前端界面的课程里,只需要打开环境里写的 HTML/JS 页面即可
- GVim:非常好用的编辑器,最简单的用法可以参考课程 Vim 编辑器
3. 环境使用
使用 GVim 编辑器输入实验所需的代码及文件,使用 LX 终端(LXTerminal)运行所需命令进行操作。
对 vim 操作不熟悉的小伙伴可以用 gedit 代替。
下面简单说明一下我们要如何使用 Linux 来编写代码(熟悉 Linux 操作的小伙伴可以跳过) 1、编写代码(最最简单的方法) 2、编译运行
二、项目介绍
1. 源码下载:
wget -c http://labfile.oss.aliyuncs.com/contest/wuziqi.rar
# 解压
sudo apt-get install unrar
unrar e wuziqi.rar
注:源码分为 Windows 版和 Linux 版,有一些小区别,课程中会讲到,其中 1 是第一节课的源码,2 为第二节课修改后的源码,实验截图中用的代码为全英文,因为实验楼的环境无法输入中文%>_<%,完整版英文代码在第二课最后。
2. 成品及介绍
五子棋大家一定都玩过,想不想试着用 C 语言来实现一个简易版的五子棋呢?下面就让我们现在开始,用最简单易懂的代码来编写一个控制台下的五子棋,并逐步完善它,每个人都能轻松学会哦!
大家看了截图,有没有一种想拍死我的冲动Σ( ° △ °|||)︴ 别急,看起来似乎很丑,实际上……确实很丑。不过没关系,这次的项目课,主要是给大家介绍一个小项目的开发流程,同时帮大家复习 C 语言基础知识,让大家轻松实现一个功能比较齐全的小游戏,后续我还会继续开设这一系列的项目课,带大家完善游戏功能和界面。 废话有点多了,下面就让我们开始吧!
三、项目实战
1. 设计棋盘
> 注:实验楼环境无法输入中文,可以用 O 和 X 来代替棋子
我们首先需要一个棋盘(15 * 15),记录棋盘中每一个位置的“情况”。 那么我们可以定义一个 chessboard[16][16] 的数组,为什么不是 [15][15] 呢?因为这样我们就可以让数组的坐标正好对应棋盘的行和列,方便后面代码的编写。
#include <stdio.h>
#define N 15
//定义一个数组并为每一个元素赋初值 0
int chessboard[N + 1][N + 1] = { 0 };
2. main 函数的编写
开始编写主函数之前,我们先简单的考虑一下,一个游戏通常的流程是怎么样的( ⊙o⊙?) 首先肯定是进入游戏的一个主界面,然后点击开始按钮进入游戏,接着显示游戏画面,判断输赢,游戏结束。那么一个五子棋游戏的流程呢?
//用来记录轮到玩家 1 还是玩家 2,奇数表示轮到玩家 1,偶数轮到玩家 2
int whoseTurn = 0;
int main(void)
{
//自定义函数,用来初始化游戏,也就是显示欢迎界面并且进入游戏显示棋盘
initGame();
//这个循环就是用来让两个玩家轮流落子的
while (1)
{
//每次循环自增 1,这样就可以做到 2 人轮流
whoseTurn++;
//自定义函数,用来执行落子操作
playChess();
}
return 0;
}
主函数大概就是这样了,是不是很简单明了呢? 附一张用 gedit 编辑 main 函数的图:
3. initGame 函数
在这个函数中,我们要实现的功能是
- 显示一个简单的欢迎界面
- 要求输入 Y 之后显示出棋盘
下面,我们就开始吧!
void initGame(void)
{
char c;
printf("欢迎^_^请输入 y 进入游戏:");
c = getchar();
if ('y' != c && 'Y' != c)
exit(0);
//清屏,windows 下为 system("cls")
system("clear");
//这里我们又调用了一个自定义函数,函数的功能是打印出棋盘
printChessboard();
}
我们在 initGame 函数中使用了 exit 以及 system 这两个函数,所以要在程序的最上面包含 stdlib.h 这个头文件
#include <stdlib.h>
4. printChessboard 函数
功能:
- 打印出行号和列号,并打印出棋盘
- 数组元素的值为 0,打印出星号(*),表示该位置没有人落子
- 数组元素的值为 1,打印实心圆(●,玩家 1 的棋子)
- 数组元素的值为 2,打印空心圆(○,玩家 2 的棋子)
void printChessboard(void)
{
int i, j;
for (i = 0; i <= N; i++)
{
for (j = 0; j <= N; j++)
{
if (0 == i) //这样可以打印出列号
printf("%3d", j);
else if (j == 0) //打印出行号
printf("%3d", i);
else if (1 == chessboard[i][j])
//windows 下●占 2 列,前面只需加一个空格
printf(" ●");
else if (2 == chessboard[i][j])
printf(" ○");
else
printf(" *");
}
printf("\n");
}
}
5. playChess 函数
函数功能:
- 要求玩家输入准备落子的位置
- 如果当前是玩家 1 落子,就将 1 赋值给数组中对应位置的元素
- 如果当前是玩家 2 落子,就将 2 赋值给数组中对应位置的元素
- 每次落子完毕,判断当前玩家是否获胜
void playChess(void)
{
int i, j, winner;
//判断轮到玩家 1 还是玩家 2,然后把值赋给数组中对应的元素
if (1 == whoseTurn % 2)
{
printf("轮到玩家 1,请输入棋子的位置,格式为行号+空格+列号:");
scanf("%d %d", &i, &j);
chessboard[i][j] = 1;
}
else
{
printf("轮到玩家 2,请输入棋子的位置,格式为行号+空格+列号:");
scanf("%d %d", &i, &j);
chessboard[i][j] = 2;
}
//重新打印一次棋盘
system("clear");
printChessboard(); //再次调用了这个函数
/*
*下面这段调用了自定义函数(judge 函数)
*用来判断当前玩家下完这步棋后,他有没有获胜
*具体怎么判断的,马上就给大家解释哦
*/
if (judge(i, j, whoseTurn))
{
if (1 == whoseTurn % 2)
printf("玩家 1 胜!\n");
else
printf("玩家 2 胜!\n");
}
}
6. judge 函数
函数参数:
- x:当前落子的行号
- y:当前落子的列号
返回值:
- 1 或 0。1 表示当前玩家落子之后出现五子连一线,也就是当前玩家获胜
//看了之后是不是想吐血,其实一点也不复杂,详解在最后
int judge(int x, int y)
{
int i, j;
int t = 2 - whoseTurn % 2;
for (i = x - 4, j = y; i <= x; i++)
{
if (i >= 1 && i <= N - 4 && t == chessboard[i][j] && t == chessboard[i + 1][j] && t == chessboard[i + 2][j] && t == chessboard[i + 3][j] && t == chessboard[i + 4][j])
return 1;
}
for (i = x, j = y - 4; j <= y; j++)
{
if (j >= 1 && j <= N - 4 && t == chessboard[i][j] && t == chessboard[i][j + 1] && t == chessboard[i][j + 1] && t == chessboard[i][j + 3] && t == chessboard[i][j + 4])
return 1;
}
for (i = x - 4, j = y - 4; i <= x, j <= y; i++, j++)
{
if (i >= 1 && i <= N - 4 && j >= 1 && j <= N - 4 && t == chessboard[i][j] && t == chessboard[i + 1][j + 1] && t == chessboard[i + 2][j + 2] && t == chessboard[i + 3][j + 3] && t == chessboard[i + 4][j + 4])
return 1;
}
for (i = x + 4, j = y - 4; i >= 1, j <= y; i--, j++)
{
if (i >= 1 && i <= N - 4 && j >= 1 && j <= N - 4 && t == chessboard[i][j] && t == chessboard[i - 1][j + 1] && t == chessboard[i - 2][j + 2] && t == chessboard[i - 3][j + 3] && t == chessboard[i - 4][j + 4])
return 1;
}
return 0;
}
我可以猜到大家此时的心情 O(∩_∩)O 其实真的只是看着复杂,稍稍做一下解释,大家就会明白的 judge 这个函数中,一共只有 4 个 for 循环,那么这 4 个循环分别是做什么用的呢?
我们先来思考一下,假设我在 9 行 9 列落子,那么我要怎么去判断在我落子之后我有没有赢呢?其实很简单,我们来看下面这张图:
其实我们要形成五子连线,无非是在一行上有连续的五个子,或者一列,或者斜方向上,而 4 个 for 循环分别对横、竖以及两个斜线方向进行判断,看有没有五子连在一起。
我们以判断横行来举例,我们落子的位置是 9 行 9 列,那么我们只要判断一下 9 行 5 列到 9 行 9 列这五个位置是不是都是当前玩家的棋子,如果是,return 1,不是的话,继续判断从 9 行 6 列开始到 9 行 10 列这五颗棋子是不是都是当前玩家的棋子,如果是,return 1,不是,继续……
以此类推,直到判断 9 行 9 列到 9 行 13 列是不是全是当前玩家的棋子,是的话,return 1,不是 for 循环退出,进入下一个 for 循环。判断行的是代码中的第二个 for 循环,那么大家能看出第一个 for 循环是判断什么的吗?
没错,就是用来判断竖直方向上是否有五子连线出现。而后两个 for 循环是判断两个斜线方向上是否有五子连线出现。
最后效果:
7. 项目完成
到这里,我们的五子棋游戏就基本上完成了。不过,我们的项目课并没有结束,我们还要对项目做一个后期的维护工作。下一课,就让我们对这个小游戏稍加完善。
到目前为止,完整的代码(实验楼无法复制及输入中文,建议提示语句用英文替换,完整英文代码见第二课):
//棋子 ● ○
#include <stdio.h>
#include <stdlib.h>
#define N 15
int chessboard[N + 1][N + 1] = { 0 };
//用来记录轮到玩家 1 还是玩家 2
int whoseTurn = 0;
void initGame(void);
void printChessboard(void);
void playChess(void);
int judge(int, int);
int main(void)
{
//初始化游戏
initGame();
while (1)
{
//每次循环自增 1,实现玩家轮流下子
whoseTurn++;
playChess();
}
return 0;
}
void initGame(void)
{
char c;
printf("欢迎^_^请输入 y 进入游戏:");
c = getchar();
if ('y' != c && 'Y' != c)
exit(0);
system("clear");
printChessboard();
}
void printChessboard(void)
{
int i, j;
for (i = 0; i <= N; i++)
{
for (j = 0; j <= N; j++)
{
if (0 == i)
printf("%3d", j);
else if (j == 0)
printf("%3d", i);
else if (1 == chessboard[i][j])
printf(" ●");
else if (2 == chessboard[i][j])
printf(" ○");
else
printf(" *");
}
printf("\n");
}
}
void playChess(void)
{
int i, j, winner;
if (1 == whoseTurn % 2)
{
printf("轮到玩家 1,请输入棋子的位置,格式为行号+空格+列号:");
scanf("%d %d", &i, &j);
chessboard[i][j] = 1;
}
else
{
printf("轮到玩家 2,请输入棋子的位置,格式为行号+空格+列号:");
scanf("%d %d", &i, &j);
chessboard[i][j] = 2;
}
system("clear");
printChessboard();
if (judge(i, j))
{
if (1 == whoseTurn % 2)
printf("玩家 1 胜!\n");
else
printf("玩家 2 胜!\n");
}
}
int judge(int x, int y)
{
int i, j;
int t = 2 - whoseTurn % 2;
for (i = x - 4, j = y; i <= x; i++)
{
if (i >= 1 && i <= N - 4 && t == chessboard[i][j] && t == chessboard[i + 1][j] && t == chessboard[i + 2][j] && t == chessboard[i + 3][j] && t == chessboard[i + 4][j])
return 1;
}
for (i = x, j = y - 4; j <= y; j++)
{
if (j >= 1 && j <= N - 4 && t == chessboard[i][j] && t == chessboard[i][j + 1] && t == chessboard[i][j + 1] && t == chessboard[i][j + 3] && t == chessboard[i][j + 4])
return 1;
}
for (i = x - 4, j = y - 4; i <= x, j <= y; i++, j++)
{
if (i >= 1 && i <= N - 4 && j >= 1 && j <= N - 4 && t == chessboard[i][j] && t == chessboard[i + 1][j + 1] && t == chessboard[i + 2][j + 2] && t == chessboard[i + 3][j + 3] && t == chessboard[i + 4][j + 4])
return 1;
}
for (i = x + 4, j = y - 4; i >= 1, j <= y; i--, j++)
{
if (i >= 1 && i <= N - 4 && j >= 1 && j <= N - 4 && t == chessboard[i][j] && t == chessboard[i - 1][j + 1] && t == chessboard[i - 2][j + 2] && t == chessboard[i - 3][j + 3] && t == chessboard[i - 4][j + 4])
return 1;
}
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论