C语言实现三子棋(详解版)(一)

简介: C语言实现三子棋(详解版)

1. 介绍


今天就带大家用C语言实现一下简洁版本的三子棋,三子棋也称为井字棋。


我们先看看三子棋的预期效果:


这里*代表玩家的棋子,#代表电脑的棋子。


247e0bd30a488f3e359b2da83b9613a0_7aa709be95ae4122b02c72ee019c70ab.png


我们通过观察发现,打印出的棋盘是一片空白,表面上看着没有数据,实际上打印的是字符数组,字符中存放的是空格。


注意:当我们想要完成一个比较大的项目时,可采取模块化处理。即:将函数的声明放在头文件中。将函数的定义和实现放在源文件里,不论该函数有多么小。


2. 准备工作


作者在写三子棋时将整个工程分为了三个部分:


  • test.c
  • game.c
  • game.h


  1. test.c源文件用于测试三子棋代码,程序从test.c文件开始执行。
  2. game.c文件中存放与游戏实现相关函数的定义和实现
  3. game.h文件中存放与游戏实现相关的函数的声明、


3. 棋盘相关操作


通过前面我们了解到,所谓的下棋操作本质上是对字符数组中的元素进行操作,通过改变字符数组中的元素的值再将其打印出来,达到下棋效果。但是棋盘是含有横纵坐标的。如果我们定义一维字符数组,则无法方便的表示棋盘中的横纵坐标。综合考虑,二维字符数组比较符合要求。


因为棋盘是三行三列的布局,所以先创建一个二维字符数组:


char board[3][3] = {0};


但是经过思索,这里创建数组时,直接指定数组的行数和列数是不太稳妥的,因为万一我想实现一个6*6或其他参数类型的棋盘,再从这里修改就不太方便。所以,我们可以使用#define定义常量来控制数组的行数和列数,这样我们就可以很方便的修改棋盘的参数了。如下:


#define ROW 3//行
#define COL 3//列
char board[ROW][COL] = {0};//创建一个3*3的数组


通常而言,#define 定义常量这种语句一般放置于头文件中。


棋盘创建好了之后,首先将其每个元素初始化成空格。初始化成功之后,最好将棋盘打印出来,看是否初始化成功。


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


棋盘打印我们可以直接遍历该二维数组进行打印:


//打印棋盘 版本一
void DisplayBoard(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
  int j = 0;
  for (j = 0; j < col; j++)
  {
    printf("%c", board[i][j]);
  }
  printf("\n");
  }
}


但是这种方式的效果不直观,一片空白。我们可以为棋盘设计一个轮廓,使其可视化。先来看看棋盘的规律:


7117c420efaf9ccb85b2fb756e5fa91b_305b25de0751477a9a5b761ead6350df.png


先看每一行的打印,整个棋盘有三行,每一行的打印是一组,整个棋盘按行就被分为了三组,每一组可用绿色和橙色部分表示,只不过最后一组的橙色部分没有打印而已。所以通过分析可以通过数组的行数控制分组数。


先对绿色部分进行分析:绿色部分先打印三个空格,再打印一个竖线,继续打印三个空格,再打印一个竖线,继续打印三个空格,最后一个空格不打印。


橙色部分:先打印三个减号,在打印一个竖线,继续打印三个减号,再打印一个竖线,继续打印三个减号,最后一个竖线不打印。


注意:每一组打印完之后要记得换行。


代码实现:


//打印棋盘 版本二
void DisplayBoard(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)//控制行数
  {
  printf(" %c | %c | %c ", board[i][0], board[i][1], board[i][2]);
  printf("\n");
  if (i < row - 1)
  {
    printf("---|---|---");
    printf("\n");
  }
  }
}


但是这样就算成功了吗?


我们将ROW和COL改成5试试呢:


70e6101ee216eedf95a7dba59392a435_cdb88c636127467fb9eaaf1878e6f741.png


我们发现,棋盘虽然变成5行,但是列数并没有发生改变。


同理,在之前的基础上,我们也可以对列数进行分析:


f45ea501ed2d87a8302e3b3e9134ad07_14d27c4fb5404008ad53a42f4f6f1802.png


整个棋盘按每一行分,也能将每一行的打印分为三部分,每部分由绿色和橙色组成。因为这个棋盘有三列,所以每一行的打印可以分为三组,假设是五列,就可以分为五组,那么每一行的打印就可通过列数来控制了。


通过观察可以发现:每一行要打印的绿色部分(三个空格和三个减号)的次数就是该棋盘的列数,每一行要打印的橙色部分(竖线部分)的次数就是该棋盘的列数-1次。所以我们可以将代码再改进:


//打印棋盘 版本三
void DisplayBoard(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)//控制行数
  {
  //每一行的打印  %c | %c | %c 
  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)
  {
    int j = 0;
    for (j = 0; j < col; j++)
    {
    printf("---");
    if (j < col - 1)
      printf("|");
    }
    printf("\n");
  }
  }
}


4.下棋流程


棋盘打印好了就可以开始下棋了。


【玩家下棋】


首先让玩家下棋,玩家输入落子的坐标,再判断该坐标是否符合要求,符合要求就落子,若不符合要求就重新输入坐标。


注意:在玩家下棋时,由于不确定玩家是不是程序员,所以就不知道横纵坐标实际上是从0开始的。我们就设计成横纵坐标从1开始,在判断合法性时,稍作处理就好。在本游戏中x,y分别代表行号和列号,并不是严格的数学上的横纵坐标,例如3,6代表的是3行6列。


//玩家下棋
void Player(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  printf("请玩家输入你的落子坐标>:(使用空格隔开)\n");
  while (1)
  {
  scanf("%d %d", &x, &y);
  //检查坐标的合法性
  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");
    continue;
  }
  }
}

【电脑下棋】


电脑下棋同理,让电脑生成相应数组下标范围内的随机数,再判断该下标的元素是否为空格(即未被占用),为空格则在该坐标落子,若不为空格,重新生成随机数,直到生成的随机数满足要求为止。


void Computer(char board[ROW][COL], int row, int col)//电脑下棋
{
  int x = 0;
  int y = 0;
  while (1)
  {
  x = rand() % 3;
  y = rand() % 3;
  if (board[x][y] == ' ')
  {
    board[x][y] = '#';
    break;
  }
  }
}

相关文章
|
6月前
|
C语言
以c语言为基础实现的简易三子棋
以c语言为基础实现的简易三子棋
40 1
|
6月前
|
C语言
c语言简单三子棋
c语言简单三子棋
|
6月前
|
算法 C语言 C++
【C语言实战项目】三子棋游戏
【C语言实战项目】三子棋游戏
59 1
|
6月前
|
C语言
C语言之三子棋小游戏
C语言之三子棋小游戏
|
6月前
|
C语言
用c语言实现三子棋
用c语言实现三子棋
26 0
|
5月前
|
C语言
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
30 1
|
6月前
|
C语言
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(二)
我们可以通过创建并定义符号常量NUMBER,来作为判断是否胜利的标准。如三子棋中,令NUMBER为3,则这八个方向中有任意一个方向达成3子连珠,则连珠的这个棋子所代表的玩家获胜。
68 1
|
6月前
|
算法 C语言 C++
万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢(一)
三子棋游戏设计的核心是对二维数组的把握和运用。
78 1
|
6月前
|
编译器 C语言
【C语言入门小游戏】三子棋
【C语言入门小游戏】三子棋
52 0
【C语言入门小游戏】三子棋
|
5月前
|
存储 C语言
三子棋(C语言版)
三子棋(C语言版)