返回介绍

第 1 节 让我们开始吧!

发布于 2025-03-07 00:37:22 字数 12216 浏览 0 评论 0 收藏 0

一、实验介绍

1. 环境登录

无需密码自动登录,系统用户名 shiyanlou

2. 环境介绍

本实验环境采用带桌面的 Ubuntu Linux 环境,实验中会用到桌面上的程序:

  1. LX 终端(LXTerminal): Linux 命令行终端,打开后会进入 Bash 环境,可以使用 Linux 命令
  2. Firefox:浏览器,可以用在需要前端界面的课程里,只需要打开环境里写的 HTML/JS 页面即可
  3. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文