【C/C++】手把手教你如何实现简易版三子棋(上)

简介: 【C/C++】手把手教你如何实现简易版三子棋

目录


写在前面

实现三子棋小游戏需要具备哪些知识呢?

游戏设计

1.main函数设计

2.menu函数的设计

3.game函数的设计

3.1InitBoard函数

3.2DisplayBoard函数

3.3player_move函数

3.4computer_move函数

3.4.1随机坐标的生成

3.5is_win函数

3.5.1is_is_full

完整源码


前言


大家是否曾经和同桌玩过三子棋

小游戏呢?

也许大家都玩过这个游戏,但是对它的称呼却并不相同,那首先带大家回忆一下玩法和规则

5.png5.png


看到这些九宫格,有没有感觉到死去的记忆突然攻击你呢?

今天我们就试着用c语言来实现这个小游戏。


正文


实现三子棋小游戏需要具备哪些知识呢?


1.分支与循环语句

2.函数

3.数组

没错,当我们能简单的使用上述知识,就可以来试着写一个三子棋小游戏了!

如果觉得上述知识没有简单掌握的话,请参照我之前的博客,里面有庖丁解牛式讲解。


游戏设计


游戏设计我们采用多文件的形式:

1.test.c          //只包含main()、menu()、game()

2.game.c      //用于定义游戏需要的各个函数

3.game.h     //用于包含各种头文件和函数声明


1.main函数设计


首先,我们需要向玩家展示一份游戏菜单,如下图:


6.png

玩家此时可以选择


1.开始游戏

0.退出游戏


而且每结束一局游戏游戏后,都会再次打印菜单,所以此处需要一个循环。

那么三种循环语句我们该如何选择呢?

为了确保程序一运行之后,就能打印菜单,我们选择do......while循环


do
{
         menu();//为了代码的整洁,我们设计一个单独的menu函数来打印菜单
}while(...)

之后我们还需要定义一个变量input来接收玩家的选择


int input = 0;
printf("请选择:>");
scanf("%d", &input);

那么还有个问题,我们do......while该何时停止呢?


此时有个巧妙的设计,当玩家输入“0”来选择退出游戏是,循环就应该停止,那不妨就用input来控制循环,如下:

int main()
{
    int input = 0;
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        .........
    } while (input);
    return 0;
}


接下来我们根据玩家输入的选择来实现菜单中的功能


        switch (input)
        {
        case 1:
            game();//进入游戏,我们用另外一个函数game()单独实现
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新输入\n");//对于选项以外的输入,给出提示
            break;
        }


但这里我们main函数的主体已经实现完毕。

来看看完整代码:

int main()
{
  srand((unsigned int)time(NULL));
  int input = 0;
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      game();
      break;
    case 0:
      printf("退出游戏\n");
      break;
    default:
      printf("选择错误,请重新输入\n");
      break;
    }
  } while (input);
    return 0;
}


2.menu函数的设计


menu()函数,我们只需要用它将菜单的内容打印到屏幕上即可,所以它的实现很简单:

void menu()
{
  printf("******三子棋*******\n");
  printf("*******************\n");
  printf("******1.start******\n");
  printf("******0.exit ******\n");
  printf("*******************\n");
  printf("*******************\n");
}


3.game函数的设计


7.png

如图,玩家和电脑每走一步,都会将棋盘打印出来,因此,我们需要用一个3*3的二维数组来存放棋子。

char board[ROW][COL];//ROW为行数,COL为列数

由于在后面的设计中,我们会多次用到行数和列数,我们干脆在game.h中用define定义ROW与COL。今后如果想设计四子棋,五子棋,十子棋等等,我们只需改变ROW和COL即可。


#define ROW 3
#define COL 3

仔细观察棋盘,当某个位置没有落子时,它是空白的。所以我们首先得初始化一下二维数组board,使其内容都为空格。这里我们用一个函数InitBoard来实现数组的初始化。


3.1InitBoard函数


数组的初始化也很简单,通过for循环的嵌套遍历整个数组即可实现

//初始化棋盘
void InitBoard(char board[ROW][COL], const int row, const int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    for (j = 0; j < col; j++)
    {
      board[i][j] = ' ' ;
    }
  }
}

InitBoard是设计游戏内容的函数,所以我们将它在game.h中声明,并在game.c中定义。

接着,玩家游戏开始前,我们最好先将棋盘打印一下,方便玩家选择坐标进行落子,

所以我们需要设计一个打印棋盘的函数。


3.2DisplayBoard函数


计算机里没有提供棋盘的图案,所以这需要我们手动来设计。

为了简便我们用' | '' - '来“拼凑”一个棋盘大概长这个样子:


9.png


那么我们简单分析一下这个棋盘的组成

10.png

注意红色圈圈里的其实是空格,而最中间的红色圈圈就是将来要落子的位置。

因此棋盘就是数组元素与' | '' -  '组成的,其中2、4为分割线。那我们直接打印就好:

void DisplayBoard(char board[ROW][COL], const int row, const int col)
{
    for (int i = 0; i < row; i++)
  {
    printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
    if(i<row-1)
      printf("---|---|---\n");
  }
}

这样我们就可以成功的打印3*3的棋盘了。但有一点小小的瑕疵就是,如果将来我们修改ROW

COL的值,想要打印10*10的棋盘,显然此刻的 DisplayBoard 不能完成。我们可以试着改造一个

动态的、大小可变的棋盘打印函数,如下:


void DisplayBoard(char board[ROW][COL], const int row, const int col)
{
  /*for (int i = 0; i < row; i++)
  {
    printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
    if(i<row-1)
      printf("---|---|---\n");
  }*/
  int i = 0;
  for (i = 0; i < row; i++)
  {
        //打印棋子和' | '
    int j = 0;
    for (j = 0; j < col; j++)
    {
      printf(" %c ", board[i][j]);
      if (j < col - 1)//防止最后一列再次打印' | '
        printf("|");
    }
    printf("\n");
        //打印分割线
    if (i < row - 1)
    {
      for (j = 0; j < col; j++)
      {
        printf("---");
        if (j < col - 1)//防止最后一行打印分割线
          printf("|");
      }
    }
    printf("\n");
  }
}


DisplayBoard是设计游戏内容的函数,所以我们将它在game.h中声明,并在game.c中定义。

到目前为止,我们的game()函数已经完善到这步:

void game()
{
    char board[ROW][COL];
    InitBoard(board, ROW, COL);//初始化棋盘
    DisplayBoard(board, ROW, COL);//打印棋盘
//下棋
    ......
}


接下来就是最重要的环节,下棋的部分了。


我们默认让玩家先手,这是我们设计一个函数来实现玩家下棋。


3.3player_move函数


玩家下棋时,首先我们给出提示玩家该下棋了:



         

下棋其实本质就是,将棋子的坐标保存到数组中,在通过DisplayBoard打印。

当然,在落子之前,我们必须得判断棋子的坐标是否合理。比如:


1.该坐标是不是已经被其他棋子占用

2.该坐标是否超出了棋盘的范围


当玩家不小心下到了不合理的位置,我们要给出提示,并让他重新选择下棋的坐标,直到下在正确的位置,所以,此处应该运用到循环。


注意:大部分玩家并非程序员,所以他们可能并不知道数组的下标是从0开始的。所以我们需要将

玩家输入的坐标减一之后再使用。

void player_move(char board[ROW][COL], const int row, const int col)
{
  int x = 0;
  int y = 0;
  printf("玩家下棋:>\n");
  while (1)
  {
    printf("请输入坐标:>");
    scanf("%d %d", &x,&y);
        //判断下棋坐标是否超出棋盘范围
        //如果合理,break结束循环
    if (x >= 1 && x <= row && y >= 1 && y <= col)
    {
            //判断该坐标是否已有棋子
      if (board[x - 1][y - 1] == ' ')
      {
        board[x - 1][y - 1] = '*';
        break;
      }
      else
        printf("该坐标已被占用,请重新输入\n");
    }
    else
    {
      printf("超出棋盘范围,请重新输入\n");
    }
  }
}


3.4computer_move函数


玩家落一子后轮到电脑下棋了,电脑是不会自主思考的,这里我们就不要求AI的方式下棋了。


我们让电脑在一个随机的坐标下棋即可。


电脑下棋时,我们照样得判断它随机生成的坐标是否合法。但是却不用提醒它“你下错了”,我们就


让它不停的尝试,直到找到正确的位置。此处依旧需要一个while循环。

void computer_move(char board[ROW][COL], const int row, const int col)
{
    printf("电脑下棋:>\n");
    while (1)
    {
        int x = ...//该怎么赋值?
        int y = ...
        if (board[x][y] == ' ')
        {
            board[x][y] = '#';
            break;
        }
    }
}


但这里我们怎么让电脑生成随机坐标的?


3.4.1随机坐标的生成


这里我们将认识一个函数 rand ,它的作用就是生成一个随机数

我们这里不详细介绍它的原理,只是简单介绍一下使用方法:


使用rand函数之前,我们得包含stdlib.h的头文件


最重要的一步,我们要在main()函数内部加上这句代码:

1. srand((unsigned int)time(NULL));
2. 
3.srand((unsigned int)time(NULL));
//别忘记包含time()函数所在的头文件  time.h


这样做之后,我们就可以使用rand函数了。

既然是随机数,我们就通过对随机数模3的操作,让它的值落在[0,3)的区间内。

int x = rand() % row;
int y = rand() % col;


然后仿照玩家下棋的逻辑,我们就顺利完成电脑下棋的函数。

void computer_move(char board[ROW][COL], const int row, const int col)
{
  printf("电脑下棋:>\n");
  while (1)
  {
    int x = rand() % row;
    int y = rand() % col;
    if (board[x][y] == ' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}


至此,玩家和电脑都会下棋了,我们只需要通过while循环让他俩一人一子即可,每落一子后将棋盘再展示一遍。

void game()
{
    char ret = 0;
    char board[ROW][COL];
    InitBoard(board, ROW, COL);
    DisplayBoard(board, ROW, COL);
    //下棋
    while (1)
    {
        player_move(board, ROW, COL);//玩家下棋
        DisplayBoard(board, ROW, COL);
        computer_move(board, ROW, COL);//电脑下棋
        DisplayBoard(board, ROW, COL);
    }
}

但到这里还没有结束,因为我们会发现一会儿程序就会进入死循环,因为棋盘下满后,电脑一直在

生成随机数妄图找到正确的位置,却找不到。

按照正常的逻辑,当一方获胜后,游戏将不在进行,并宣布某一方获胜。

当棋盘下满后,无任何一方获胜,就宣布平局。

那我们该怎么实现这样的逻辑呢?

目录
相关文章
|
6月前
|
算法 C语言 C++
【C语言实战项目】三子棋游戏
【C语言实战项目】三子棋游戏
59 1
|
11月前
|
C语言
三子棋真是太神奇啦~~~C语言三子棋小游戏详解,具体到每一步操作的解释说明,不信你学不会!
三子棋真是太神奇啦~~~C语言三子棋小游戏详解,具体到每一步操作的解释说明,不信你学不会!
58 2
|
25天前
|
C语言
初学者指南:使用C语言实现简易版扫雷游戏
初学者指南:使用C语言实现简易版扫雷游戏
30 0
|
6月前
|
安全 C语言
四步手把手教你实现扫雷游戏(c语言)
四步手把手教你实现扫雷游戏(c语言)
72 0
|
6月前
|
机器学习/深度学习 小程序 C语言
C语言初学者:原来我也可以实现扫雷小游戏(简易版)!
C语言初学者:原来我也可以实现扫雷小游戏(简易版)!
|
12月前
三子棋进阶版(源代码)
三子棋进阶版(源代码)
|
12月前
|
C语言
【C语言】扫雷小游戏(保姆教程)
【C语言】扫雷小游戏(保姆教程)
52 0
|
C语言
从零到一快速学会三子棋
从零到一快速学会三子棋
64 1
新手专用练手项目--三子棋
新手专用练手项目--三子棋
38 0
|
C语言
三子棋——【保姆级C语言小游戏】
三子棋——【保姆级C语言小游戏】
85 0