超详细教学用c语言实现三子棋小游戏

简介: 超详细教学用c语言实现三子棋小游戏

1.三子棋游戏规则

在设计游戏前,我们先得知道最基础的游戏规则,以实现我们最基本的设计需求:

1、人机双方轮流在格子里落子棋子,先连成三棋一线者视为胜利

2、棋盘被摆满棋子仍未分出胜利,视为平局

2.分析需求

       其实设计程序的难点就在于,把抽象的逻辑概念转化为计算机语言可识别的逻辑语句,再简单的设计需求,都要用科学的分析方法,先分析需求,再针对性的设计,不可想当然的拿起手就开始写代码,也就是我们始终强调的编程思维。要用工程化的方法解决实际问题。


       首先,一个游戏是必须有菜单界面的,供以玩家选择是否开始游戏或者退出游戏,同时我们也要考虑游戏多次游玩的情况,需要用一个循环语句将菜单界面封装起来,可以让玩家在游玩后再次进入菜单选择是否开始游戏;其次是游戏的初始化问题,我们需要一个数组来保存玩家和电脑下棋时落子的位置,并且要在屏幕上打印出来,在这里我们就得考虑落子的问题,玩家落子很简单,用一个数组保存就行了,但是电脑的落子应该是随机的,我们可以考虑使用rand函数和time函数来做到随机生成的功能,,并且在落子的时候我们得考虑输入是否合理,不能越界输入,不能重复在同一个位置落子;解决了以上的问题后,我们要思考,怎么设计一个判断输赢的逻辑,怎么样是玩家赢,怎么样是电脑赢,怎么样算是平局。


       在有了以上大致的规划和思考后,我们就可以正式的开始设计了。


       在开始设计之前,我们首先得明确,这个程序是由多个功能的函数语句共同实现的,因此在创建工程时,我们就应该提前做好创建多个文件的工作,不同的文件实现不同的功能和逻辑,以保证程序的健康性和完整性,也方便我们日后更改。我们可以分为一下三个文件实现,game.h用于包含程序所需要的头文件和函数的申明,chessboard.c用于实现棋盘上具体的初始化和判断逻辑,game.c文件为主要的主程序所在的文件,是程序的主体,也是各个函数的集合实现。

3.菜单界面设计

       首先需要在屏幕上打印菜单,然后让玩家选择,若玩家输入不正确或者想在游玩后再次游玩的话,则需要个循环来实现,因此,这里需要的是先执行循环体再判断循环条件的语句,也就是do...whlie...语句,以下是代码实现

void screen()
{
  printf("--------------------------\n");
  printf("---------1.Play ----------\n");
  printf("--------------------------\n");
  printf("---------2.Esc  ----------\n");
  printf("--------------------------\n");
}
int input = 0;//记录玩家的输入选择
  printf("请进行输入确认是否开始游戏\n");
  do
  {
    screen();//调用屏幕打印菜单的函数
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("开始游戏\n");
      printf("三子棋\n");
      game();
      break;
    case 2:
      printf("退出游戏\n");
      break;
    default:
      printf("非法输入,请重新输入\n");
      break;
    }
  } while (input != 2);

4.游戏主体设计(棋盘的初始化部分)

       在这里,为了方便以后对棋盘大小进行更改,我们可以在头文件中进行宏定义,我们用ROWCOL分别来代表行和列

#define ROW 3 //定义 行为3
#define COL 3 //定义 列为3

    然后我们需要用一个3*3的数组去对我们的棋盘的数据进行保存,并对其进行初始化,将其封装成一个函数,并且放在chessboard.c文件中实现,需要传入的参数是一个数组和行以及列。我们对数组中每一个赋值为空格。

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

 在完成了初始化之后,我们需要打印棋盘到屏幕上供玩家看见,但是此时打印出来的全是空格,玩家是看不见什么有意义的信息的,所以我们要制定一个棋盘的框架,既要实现需求,也要保证美观,所以需要打印部分分割线

isplayboard(board, ROW, COL);//打印棋盘
void displayboard(char board[ROW][COL], int row, int col)
{
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++)
    {
      printf(" %c ", board[i][j]);
      //打印分割列
      if (j < col - 1)
        printf("|");
    }
    //打印分割行
    if (i < row - 1)
      printf("\n---|---|---\n");
  }
  printf("\n");
}

我们可以运行着测试一下

5.游戏主体设计(玩家下棋部分)

        在拥有完整的棋盘后,我们就可以开始着手设计玩家下棋和电脑下棋的部分了,首先是玩家下棋的部分,我们可以用之前定义的board数组来防止玩家落子的结果,可以设计如下的封装函数。使用while循环来保证玩家落子是真确合法的,否则就一直进行循环,直到输入正确的落子坐标。

palyer_move(board, ROW, COL);//玩家下棋
void palyer_move(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  printf("\n----玩家下棋----\n");//提示语句
  while (1)
  {
    printf("请输入落子的坐标:");
    scanf("%d %d", &x, &y);
    if (x <= 0 || x > 3 || y <= 0 || y > 3)//判断输入是否越界
    {
      printf("非法输入,请重新输入\n");
    }
    else if (board[x-1][y-1] == ' ')
    {
      board[x-1][y-1] = '*';//用*表示玩家落子
      break;
    }
    else
      printf("输入位置被占用,请重新输入\n");
  }
}

6.游戏主体(电脑下棋部分)

       电脑下棋,最需要的就是随机,不然每一次电脑都是一个下法,游戏就不能正常游玩了,在这里,我们可以使用rand函数来生成随机数,使用srand函数来作为生成随机数的种子,为了保证随机数的动态变化,我们使用time函数来获取时间戳供以srand函数作为参数,对于以上知识点有不了解的小伙伴可以参考笔者的这篇文章,里面对rand函数,srand函数,time函数有非常详细的教学和参数返回值的讲解:c语言实现猜数字小游戏以及如何做到生成真正的随机数

computer_move(board, ROW, COL);//电脑下棋
srand((unsigned int)time(NULL));//初始化随机数的种子
void computer_move(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  printf("\n----电脑下棋----\n");//提示语句
  while (1)
  {
    int x = rand() % ROW;//使得随机数大小为1--3
    int y = rand() % COL;//使得随机数大小为1--3
    if (board[x][y] == ' ')
    {
      board[x][y] = '#';//用#表示电脑落子
      break;
    }
  }
}

在这里的while循环是为了避免不合理的落子,比如在以及有玩家落子的地方是不允许再一次落子的,在不满足这样的条件下会再一次生成新的随机数,直到可以有正确的落子坐标为止

7.判断输赢逻辑逻辑设计

在玩家和电脑落子都设计完成后,我们需要思考,怎么样的情况下算是玩家赢,怎么样的情况下算是电脑赢,怎么样算是平局,在玩家或者电脑赢了后怎么样实现立即停止游戏。


       我们可以设计一个函数,用其返回值来表示谁赢的了游戏的胜利,谁先完成三子连成一线就返回谁的棋子符号,在这里可以用俩个for循环判断3行和3列是否已经达成三子一线的情况,并且不能是三个空格,如果满足条件,则返回满足条件的元素符号,也就是*或者#,在用俩个if语句去判断俩条对角线是否满足条件,都没完成就继续游戏,并且设计一个函数判断棋盘是否满了,当棋盘下满后返回Q表示棋盘已经下满了,游戏平局,否则返回C表示游戏继续正常进行

//玩家赢 - '*'
//电脑赢 - '#'
//平局 --- 'Q'
//游戏继续-'C'
char IsWin(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
        //实现判断每一行是否相同
    if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
    {
      return board[i][0];
    }
  }
  for (i = 0; i < col; i++)
  {    
         //实现判断每一列是否相同
    if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
    {
      return board[0][i];
    }
  }
     //实现判断每一条对角线是否相同
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
  {
    return board[1][1];
  }
  if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
  {
    return board[1][1];
  }
  //判断平局
  if (IsFull(board, row, col))
  {
    return 'Q';
  }
  return 'C';
}

其中的判断棋盘是否下满的函数具体实现如下

static int IsFull(char board[ROW][COL], int row, int col)
{
  int i = 0;
  int j = 0;
    //对棋盘进行遍历
  for (i = 0; i < row; i++)
  {
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == ' ')
        return 0;//判断有空位,返回0
    }
  }
  return 1;//判断棋盘已满,返回1
}

      并且在游戏的主程序中,玩家每下一次棋以及电脑每下一次棋,都进行一次判断,用一个while循环,当某一方胜利或者平局时,也就是ret拿到的返回值只要不是C(游戏继续),就执行if语句,跳出循环,具体实现如下,在跳出循环后我们要对结果进行打印,也就是使用if...else if...else的多分支语句进行判断

while (1)
  {
    palyer_move(board, ROW, COL);//玩家下棋
    displayboard(board, ROW, COL);//打印棋盘
    //判断输赢
    ret = IsWin(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
    computer_move(board, ROW, COL);//电脑下棋
    displayboard(board, ROW, COL);//打印棋盘
    //判断输赢
    ret = IsWin(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
  }
  if (ret == '*')
  {
    printf("玩家赢\n");
  }
  else if (ret == '#')
  {
    printf("电脑赢\n");
  }
  else
  {
    printf("平局\n");
  }

综上完成了各个模块的设计后,我们就可以进行组装,最后完成程序的设计

8.游戏完整代码

1.头文件代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3 //定义 行为3
#define COL 3 //定义 列为3
//打印屏幕菜单
void screen();
//初始化
void initialize(char board[ROW][COL], int row, int col);
//打印棋盘
void displayboard(char board[ROW][COL],int row,int col);
//玩家下棋
void palyer_move(char board[ROW][COL], int row, int col);
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);
//判断输赢
char IsWin(char board[ROW][COL], int row, int col);

2.棋盘模块代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void screen()
{
  printf("--------------------------\n");
  printf("---------1.Play ----------\n");
  printf("--------------------------\n");
  printf("---------2.Esc  ----------\n");
  printf("--------------------------\n");
}
void initialize(char board[ROW][COL], int row, int col)
{
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++)
    {
      board[i][j] = ' ';
    }
  }
}
void displayboard(char board[ROW][COL], int row, int col)
{
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++)
    {
      printf(" %c ", board[i][j]);
      //打印分割列
      if (j < col - 1)
        printf("|");
    }
    //打印分割行
    if (i < row - 1)
      printf("\n---|---|---\n");
  }
  printf("\n");
}
void palyer_move(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  printf("\n----玩家下棋----\n");
  while (1)
  {
    printf("请输入落子的坐标:");
    scanf("%d %d", &x, &y);
    if (x <= 0 || x > 3 || y <= 0 || y > 3)
    {
      printf("非法输入,请重新输入\n");
    }
    else if (board[x-1][y-1] == ' ')
    {
      board[x-1][y-1] = '*';
      break;
    }
    else
      printf("输入位置被占用,请重新输入\n");
  }
}
void computer_move(char board[ROW][COL], int row, int col)
{
  int x = 0;
  int y = 0;
  printf("\n----电脑下棋----\n");
  while (1)
  {
    int x = rand() % ROW;//使得随机数大小为1--3
    int y = rand() % COL;//使得随机数大小为1--3
    if (board[x][y] == ' ')
    {
      board[x][y] = '#';
      break;
    }
  }
}
static int IsFull(char board[ROW][COL], int row, int col)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < row; i++)
  {
    for (j = 0; j < col; j++)
    {
      if (board[i][j] == ' ')
        return 0;
    }
  }
  return 1;
}
//玩家赢 - '*'
//电脑赢 - '#'
//平局 --- 'Q'
//游戏继续-'C'
char IsWin(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
    if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
    {
      return board[i][0];
    }
  }
  for (i = 0; i < col; i++)
  {
    if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
    {
      return board[0][i];
    }
  }
  if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
  {
    return board[1][1];
  }
  if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
  {
    return board[1][1];
  }
  //判断平局
  if (IsFull(board, row, col))
  {
    return 'Q';
  }
  return 'C';
}

3.游戏主函数文件代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void game()
{
  //打印棋盘
  void screen();
  char ret = 0;
  char board[ROW][COL] = { 0 };
  initialize(board, ROW, COL);//初始化棋盘
  displayboard(board, ROW, COL);//打印棋盘
  while (1)
  {
    palyer_move(board, ROW, COL);//玩家下棋
    displayboard(board, ROW, COL);//打印棋盘
    //判断输赢
    ret = IsWin(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
    computer_move(board, ROW, COL);//电脑下棋
    displayboard(board, ROW, COL);//打印棋盘
    //判断输赢
    ret = IsWin(board, ROW, COL);
    if (ret != 'C')
    {
      break;
    }
  }
  if (ret == '*')
  {
    printf("玩家赢\n");
  }
  else if (ret == '#')
  {
    printf("电脑赢\n");
  }
  else
  {
    printf("平局\n");
  }
}
int main()
{
  srand((unsigned int)time(NULL));
  int input = 0;
  printf("请进行输入确认是否开始游戏\n");
  do
  {
    screen();
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("开始游戏\n");
      printf("三子棋\n");
      game();
      break;
    case 2:
      printf("退出游戏\n");
      break;
    default:
      printf("非法输入,请重新输入\n");
      break;
    }
  } while (input != 2);
  return 0;
}

9.程序运行部分截图

本次的分享就到此为止啦,希望对各位小伙伴有所帮助

目录
相关文章
|
4天前
|
定位技术 C语言
c语言及数据结构实现简单贪吃蛇小游戏
c语言及数据结构实现简单贪吃蛇小游戏
|
1月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
66 16
|
1月前
|
算法 C语言
【C语言程序设计——循环程序设计】求解最大公约数(头歌实践教学平台习题)【合集】
采用欧几里得算法(EuclideanAlgorithm)求解两个正整数的最大公约数。的最大公约数,然后检查最大公约数是否大于1。如果是,就返回1,表示。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。作为新的参数传递进去。这个递归过程会不断进行,直到。有除1以外的公约数;变为0,此时就找到了最大公约数。开始你的任务吧,祝你成功!是否为0,如果是,那么。就是最大公约数,直接返回。
77 18
|
1月前
|
Serverless C语言
【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】
根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码,求解出数值x的平方根;运用迭代公式,编写一个循环程序,求解出数值x的平方根。注意:不能直接用平方根公式/函数求解本题!开始你的任务吧,祝你成功!​ 相关知识 求平方根的迭代公式 绝对值函数fabs() 循环语句 一、求平方根的迭代公式 1.原理 在C语言中,求一个数的平方根可以使用牛顿迭代法。对于方程(为要求平方根的数),设是的第n次近似值,牛顿迭代公式为。 其基本思想是从一个初始近似值开始,通过不断迭代这个公式,使得越来越接近。
57 18
|
1月前
|
C语言
【C语言程序设计——循环程序设计】统计海军鸣放礼炮声数量(头歌实践教学平台习题)【合集】
有A、B、C三艘军舰同时开始鸣放礼炮各21响。已知A舰每隔5秒1次,B舰每隔6秒放1次,C舰每隔7秒放1次。编程计算观众总共听到几次礼炮声。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。开始你的任务吧,祝你成功!
52 13
|
1月前
|
存储 安全 C语言
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
71 10
|
1月前
|
小程序 C语言
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
40 10
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
37 3
|
1月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
20 2
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
52 1

热门文章

最新文章

相关实验场景

更多