开始
实验
我要
提问
我要
纠错
C语言快速实现五子棋

快速实验五子棋

一、实验介绍

1.1 实验内容

五子棋大家一定都玩过,想不想试着用 C 语言来实现一个简易版的五子棋呢?下面就让我们现在开始,用最简单易懂的代码来编写一个控制台下的五子棋,并逐步完善它,每个人都能轻松学会哦!

1.2 知识点

  • 游戏的逻辑
  • 判断结果的算法
  • 界面的设计

1.3 实验环境

  • vim编辑器
  • Xfce终端
  • gcc编译器

1.4 适合人群

本课程比较简单,没有复杂的语法和逻辑,适合具有C基础的用户,一起探索和发现C语言可以做一些有趣的事情

1.5 源码下载:

# 下载
wget http://labfile.oss.aliyuncs.com/courses/313/wuziqi.zip 
# 解压
unzip wuziqi.zip
# 进入项目目录
cd wuziqi
# 编译
gcc Linux-1.c -o wuzi
# 运行
./wuzi

注:包含两个文件,其中1是第一节课的源码,2为第二节课修改后的源码

1.6 效果截图

图片描述信息

大家看了截图,有没有一种想拍死我的冲动Σ( ° △ °|||)︴

别急,看起来似乎很丑,实际上……确实很丑。不过没关系,这次的项目课,主要是给大家介绍一个小项目的开发流程,同时帮大家复习 C 语言基础知识,让大家轻松实现一个功能比较齐全的小游戏,后续我还会继续开设这一系列的项目课,带大家完善游戏功能和界面。

废话有点多了,下面就让我们开始吧!

二、实验步骤

2.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.2、main函数的编写

开始编写主函数之前,我们先简单的考虑一下,一个游戏通常的流程是怎么样的 (⊙o⊙?)首 先肯定是进入游戏的一个主界面,然后点击开始按钮进入游戏,接着显示游戏画面,判断输赢,游戏结束。那么一个五子棋游戏的流程呢?

图片描述信息

//用来记录轮到玩家1还是玩家2,奇数表示轮到玩家1,偶数轮到玩家2
int whoseTurn = 0;

int main(void)
{
    //自定义函数,用来初始化游戏,也就是显示欢迎界面并且进入游戏显示棋盘
    initGame();

    //这个循环就是用来让两个玩家轮流落子的
    while (1)
    {
        //每次循环自增1,这样就可以做到2人轮流
        whoseTurn++;

        //自定义函数,用来执行落子操作
        playChess();
    }

    return 0;
}

主函数大概就是这样了,是不是很简单明了呢?

附一张用gedit编辑main函数的图:

图片描述信息

2.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>

图片描述信息

2.4、printChessboard 函数

功能:

  • 打印出行号和列号,并打印出棋盘
  • 数组元素的值为0,打印出星号(*),表示该位置没有人落子
  • 数组元素的值为1,打印实心圆(X,玩家1的棋子)
  • 数组元素的值为2,打印空心圆(O,玩家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("  X");
            else if (2 == chessboard[i][j])
                printf("  O");
            else
                printf("  *");
        }
        printf("\n");
    }
}

2.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");
    }
}

2.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 + 2] && 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循环是判断两个斜线方向上是否有五子连线出现。

最后效果:

图片描述信息

2.7、项目完成

到目前为止,完整的代码。

//棋子 X O

#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("  X");
            else if (2 == chessboard[i][j])
                printf("  O");
            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 + 2] && 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;
}

三、实验总结

到这里,我们的五子棋游戏就基本上完成了。不过,我们的项目课并没有结束,我们还要对项目做一个后期的维护工作。下一课,就让我们对这个小游戏稍加完善。

下一节:修复BUG

0% Complete

加载中,精彩就在后面...

卡住了?点击重试