C语言学习-数组应用-三子棋(4.1)

简介: C语言学习-数组应用-三子棋(4.1)


三子棋的设计思路:

我们创建三个文件拆分三子棋的实现过程:


一个测试文件(test.c)游戏实现文件(game.c)头文件(game.h)



我们先对测试文件进行编辑。


1. 游戏菜单功能的实现

在 test.c 中写一个主函数:


int main()//主函数尽量放的东西少一点
{
  test();//可以通过创建一个函数实现其他功能
  return 0;
}

我们通过刚刚创建的 test 函数实现一些功能:


1. 玩一把不过瘾,在玩一把


2. 创建游戏菜单


3. 创建游戏实现函数


void test()
{
  int choice = 0;
  do  //我们使用一个do while循环,如果玩一把三子棋不过瘾,可以再玩一把
  {
  menu();
  printf("请选择:>");
  scanf("%d", &choice);//接收玩家输入的值
  switch (choice)
  {
  case 1:
    printf("三子棋游戏开始\n");
    game();//我们将游戏的实现分装成一个函数
    break;
  case 0:
    printf("游戏已退出\n");
    break;
  default:
    printf("输入错误,请重新输入\n");
    break;
  }
  } while (choice);
}
这是我们的游戏菜单:
void menu()//打印游戏菜单界面
{
  printf("***************************************\n");
  printf("*****  开始游戏:1    退出游戏:0  *****\n");
  printf("***************************************\n");
}

写到这里,我们可以先测试一下代码运行的结果:


这样,我们就完成了菜单逻辑的制作


2. 游戏功能具体实现

接下来我们要具体实现函数 game 。


2.1 棋盘的初始化与打印

实现思路:


首先,我们需要记录玩家和电脑的落子情况



通过分析,能落子的地方有九个,


我们可以通过创建一个3*3的数组对其进行记录。


我们在头文件中用#define定义常量,便于日后观察或修改



再在测试文件中引用:


#include "game.h"//引用自己的头文件
void game()//三子棋的实现
{
  char board[ROW][COL];//存放玩家或电脑的落子情况
}

接下来我们需要让落子位置初始化,并将三子棋的棋盘打印出来:


我们将初始化和棋盘打印这两个步骤分装成两个函数并在 game.c 中实现:



为了能够使用这两个函数,我们需要在头文件中声明:


void init_board(char board[ROW][COL], int row, int col);

void print_board(char board[ROW][COL], int row, int col);

初始化棋盘的具体实现:


void init_board(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 print_board(char board[ROW][COL], int row, int col)
  {
  int i = 0;
  for (i = 0; i < col; i++)
  {
    printf("  %d ", i + 1);//添加纵坐标
  }
  printf("\n");
  for (i = 0; i < row; i++)
  {
    int j = 0;
    printf("%d", i + 1);//添加横坐标
    for (j = 0; j < col; j++)
    {
    printf(" %c ", board[i][j]);//打印三行,每行三个 %c 
    if (j < col - 1)//井字棋的棋盘只需要中间两排竖着的分割线
    {
      printf("|");//打印三行,每行两个‘|’
    }
    }
    printf("\n");
    if (i < row - 1)//只需要两行分割线
    {
    printf(" ");//对齐
    for (j = 0; j < col; j++)
    {
      printf("---");//打印两行,每行三个‘---’
      if (j < col - 1)
      {
      printf("|");//打印两行,每行两个‘|’
      }
    }
    }
    printf("\n");
  }
  }

最后呈现的结果是这样的:



2.2 玩家与电脑对战的实现

接下来就是下棋了,我们把下棋分为两步:


1. 玩家回合


2. 电脑回合


并且每次下完把棋盘打印出来:



然后将函数在头文件中声明,在 game.c 中实现:


声明:


void player_move(char board[ROW][COL], int row, int col);

void computer_move(char board[ROW][COL], int row, int col);

玩家回合的实现:


void player_move(char board[ROW][COL], int row, int col)
  {
  printf("玩家回合\n");
  while (1)
  {
    printf("请输入要下的坐标:>");
    int x = 0;
    int y = 0;
    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");
    }
  }
  }


电脑回合的实现:


void computer_move(char board[ROW][COL], int row, int col)
  {
  printf("电脑回合\n");
  while (1)
  {
    int x = rand() % row;//生成随机数
    int y = rand() % col;
    if (board[x][y] == ' ')
    {
    board[x][y] = '#';
    break;
    }
  }
  }

生成随机数时:



现在就到了最后一步,


2.3 判断胜负平局的实现

判断输赢:


void game()//三子棋的实现
{
  char ret = 0;//我们通过返回字符判断输赢
  char board[ROW][COL];//存放玩家或电脑的落子情况
  init_board(board, ROW, COL);//初始化棋盘
  print_board(board, ROW, COL);//打印棋盘
  while (1)//创建一个循环,直到分出胜负或平局再跳出
  {
  player_move(board, ROW, COL);//玩家下
  print_board(board, ROW, COL);
  ret = is_win(board, ROW, COL);
  if (ret != 'C')//判断游戏是否继续
  {
    break;//当ret不等于C时,跳出循环,判断输赢
  }
  computer_move(board, ROW, COL);//电脑下
  print_board(board, ROW, COL);
  ret = is_win(board, ROW, COL);
  if (ret != 'C')
  {
    break;
  }
  }
     //判断输赢
  if (ret == '*')
  {
  printf("你赢了\n");
  }
  else if (ret == '#')
  {
  printf("电脑获胜\n");
  }
  else if (ret == 'Q')
  {
    printf("平局\n");
  }
}

这样,当ret返回 * 是玩家获胜,返回 # 时电脑获胜,都不返回则平局。


接下来需要实现 is_win 函数:


(实现函数时,别忘了在头文件中声明哦)


char is_win(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 (is_full(board, row, col) == 1)//判断棋盘是否满了
  {
    return 'Q';
  }
  return 'C';
函数 is_full 的实现:
  int is_full(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;
  }


现在,三子棋就做完了,我们一起赢一把看看效果:



学会了吗?


快去试试吧!


3. 三子棋源码

这里是源码:


1. test.c 文件:


#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"//引用自己的头文件
void menu()//打印游戏菜单界面
{
  printf("***************************************\n");
  printf("*****  开始游戏:1   退出游戏:0  *****\n");
  printf("***************************************\n");
}
void game()//三子棋的实现
{
  char ret = 0;//我们通过返回字符判断输赢
  char board[ROW][COL];//存放玩家或电脑的落子情况
  init_board(board, ROW, COL);//初始化棋盘
  print_board(board, ROW, COL);//打印棋盘
  while (1)//创建一个循环,直到分出胜负或平局再跳出
  {
  player_move(board, ROW, COL);//玩家下
  print_board(board, ROW, COL);
  ret = is_win(board, ROW, COL);
  if (ret != 'C')//判断游戏是否继续
  {
    break;//当ret不等于C时,跳出循环,判断输赢
  }
  computer_move(board, ROW, COL);//电脑下
  print_board(board, ROW, COL);
  ret = is_win(board, ROW, COL);
  if (ret != 'C')
  {
    break;
  }
  }
     //判断输赢
  if (ret == '*')
  {
  printf("你赢了\n");
  }
  else if (ret == '#')
  {
  printf("电脑获胜\n");
  }
  else if (ret == 'Q')
  {
    printf("平局\n");
  }
}
void test()
{
  srand((unsigned int)time(NULL));//通过时间戳生成随机数
  int choice = 0;
  do  //我们使用一个do while循环,如果玩一把三子棋不过瘾,可以再玩一把
  {
  menu();
  printf("请选择:>");
  scanf("%d", &choice);//接收玩家输入的值
  switch (choice)
  {
  case 1:
    printf("三子棋游戏开始:\n");
    game();//我们将游戏的实现分装成一个函数
    break;
  case 0:
    printf("游戏已退出\n");
    break;
  default:
    printf("输入错误,请重新输入\n");
    break;
  }
  } while (choice);
}
int main()//主函数尽量放的东西少一点
{
  test();//可以通过创建一个函数实现其他功能
  return 0;
}
2. game.h 文件:
#pragma once
#include
#include
#include
#define ROW 3 
#define COL 3
void init_board(char board[ROW][COL], int row, int col);
void print_board(char board[ROW][COL], int row, int col);
void player_move(char board[ROW][COL], int row, int col);
void computer_move(char board[ROW][COL], int row, int col);
char  is_win(char board[ROW][COL], int row, int col);
3. game.c 文件:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void init_board(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 print_board(char board[ROW][COL], int row, int col)
  {
  int i = 0;
  for (i = 0; i < col; i++)
  {
    printf("  %d ", i + 1);//添加纵坐标
  }
  printf("\n");
  for (i = 0; i < row; i++)
  {
    int j = 0;
    printf("%d", i + 1);//添加横坐标
    for (j = 0; j < col; j++)
    {
    printf(" %c ", board[i][j]);//打印三行,每行三个 %c 
    if (j < col - 1)//井字棋的棋盘只需要中间两排竖着的分割线
    {
      printf("|");//打印三行,每行两个‘|’
    }
    }
    printf("\n");
    if (i < row - 1)//只需要两行分割线
    {
    printf(" ");//对齐
    for (j = 0; j < col; j++)
    {
      printf("---");//打印两行,每行三个‘---’
      if (j < col - 1)
      {
      printf("|");//打印两行,每行两个‘|’
      }
    }
    }
    printf("\n");
  }
  }
  void player_move(char board[ROW][COL], int row, int col)
  {
  printf("玩家回合\n");
  while (1)
  {
    printf("请输入要下的坐标:>");
    int x = 0;
    int y = 0;
    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");
    }
  }
  }
  void computer_move(char board[ROW][COL], int row, int col)
  {
  printf("电脑回合\n");
  while (1)
  {
    int x = rand() % row;//生成随机数
    int y = rand() % col;
    if (board[x][y] == ' ')
    {
    board[x][y] = '#';
    break;
    }
  }
  }
  int is_full(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;
  }
  char is_win(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 (is_full(board, row, col) == 1)//判断棋盘是否满了
  {
    return 'Q';
  }
  return 'C';
  }

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。


如果喜欢本文的话,欢迎点赞和评论,写下你的见解。


如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。



相关文章
|
3天前
|
存储 算法 C语言
通义灵码在考研C语言和数据结构中的应用实践 1-5
通义灵码在考研C语言和数据结构中的应用实践,体验通义灵码的强大思路。《趣学C语言和数据结构100例》精选了五个经典问题及其解决方案,包括求最大公约数和最小公倍数、统计字符类型、求特殊数列和、计算阶乘和双阶乘、以及求斐波那契数列的前20项和。通过这些实例,帮助读者掌握C语言的基本语法和常用算法,提升编程能力。
|
23小时前
|
存储 编译器 C语言
【c语言】数组
本文介绍了数组的基本概念及一维和二维数组的创建、初始化、使用方法及其在内存中的存储形式。一维数组通过下标访问元素,支持初始化和动态输入输出。二维数组则通过行和列的下标访问元素,同样支持初始化和动态输入输出。此外,还简要介绍了C99标准中的变长数组,允许在运行时根据变量创建数组,但不能初始化。
17 5
|
4天前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
10天前
|
存储 人工智能 BI
C语言:数组的分类
C语言中的数组分为一维数组、多维数组和字符串数组。一维数组是最基本的形式,用于存储一系列相同类型的元素;多维数组则可以看作是一维数组的数组,常用于矩阵运算等场景;字符串数组则是以字符为元素的一维数组,专门用于处理文本数据。
|
7天前
|
存储 C语言
C语言:一维数组的不初始化、部分初始化、完全初始化的不同点
C语言中一维数组的初始化有三种情况:不初始化时,数组元素的值是随机的;部分初始化时,未指定的元素会被自动赋值为0;完全初始化时,所有元素都被赋予了初始值。
|
10天前
|
C语言
C语言数组
C语言数组
12 0
|
10天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
25 3
|
1天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
17 10
|
5天前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。
|
11天前
|
C语言
c语言回顾-函数递归(上)
c语言回顾-函数递归(上)
27 2