【维生素C语言】第四章 - 数组(二)

简介: 本章将对C语言的数组进行讲解,从一维数组开始讲起。已经学了三个章节了,所以本章还附加了三子棋和扫雷两个简单的小游戏,读者可以试着写一写,增加编程兴趣,提高模块化编程思想。

四、实现三子棋(Tic-Tac-Toe)


0x00 游戏介绍

3f02f577ad8bc467f9a778e9ba9a8099_20210521084424834.png

三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。


0x01 实现思路

📚 分模块:当代码量较大,功能较多时,我们可以拆分代码,分模块来实现各个功能;


     ①  test.c       测试游戏的逻辑;


     ②  game.c    游戏相关函数的实现;


     ②  game.h    关于游戏相关的函数声明、符号声明以及头文件的包含;


0x02 游戏界面

💬 test.c


该代码实现游戏界面部分


#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void load_game_menu()
{
    printf("\n");
    printf("*********************************\n");
    printf("********** 1. 开始游戏 ***********\n");
    printf("********** 0. 退出游戏 ***********\n");
    printf("*********************************\n");
}
int main(int argc, char const* argv[])
{
    int input = 0;
    do
    {
        /* 载入游戏菜单 */
        load_game_menu();
        printf("请选择: ");
        scanf("%d", &input);
        switch (input)
        {
            case 1:
                printf("\ntest:游戏开始\n");
                // game();
                break;
            case 0:
                printf("\n退出游戏\n");
                break;
            default:
                printf("\n输入错误,请重新输入!\n");
                break;
        }
    } while (input);
    return( 0 );
}


🚩 该部分运行结果如下(完成一部分功能就运行一下看看,及时发现BUG,越早发现越容易找到BUG)

123634ac8cad55405031b5b752ed1d5a_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDUwMjg2Mg==,size_16,color_FFFFFF,t_70.png


0x03 创建棋盘&初始化棋盘

写game() 函数,创建棋盘,然后将初始化棋盘函数


💬 test.c


void game()
{
    /* 创建棋盘 */
    char board[ROW][COL];
    /* 初始化棋盘 - 初始化空格 */
    init_board(board, ROW, COL);
}

💬 game.h


#include <stdio.h>
/* 宏定义 */
#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);

💬 game.c


#include "game.h"
void init_board(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++)
  {
    board[i][j] = ' '; // 初始化为空格
  }
  }
}

0x04 打印棋盘

print_board()函数,本质上是打印数组的内容


利用循环画一个棋盘

760b36db02d2d8051351f3c422721181_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDUwMjg2Mg==,size_16,color_FFFFFF,t_70.png


💬 test.c


void game()
{
  //存储数据 - 二维数组
  char board[ROW][COL];
  //初始化棋盘 - 初始化空格
  init_board(board, ROW, COL);
  //打印一下棋盘 - 本质是打印数组的内容
  print_board(board, ROW, COL);
}

💬 game.h


#include <stdio.h>
/* 宏定义 */
#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);

💬 game.c


void print_board(char board[ROW][COL], int row, int col)
{
  int i = 0;
  for (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");
  }
}

🚩 运行结果:

469db6bc9b7fb49333ab077913b93421_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDUwMjg2Mg==,size_16,color_FFFFFF,t_70.png


❓如果修改了棋盘大小,怎么办?


💡 代码优化


void print_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++)
  {
    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");
  }
  }
}


🚩 我们将 ROW 和 COL 修改为5试试看能否生成一个5x5的棋盘;

4ee602c5eaa91d61a8da801e9e4180ee_20210521155540714.png

4793333f65bf0c5a6365dd4baf409b35_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDUwMjg2Mg==,size_16,color_FFFFFF,t_70.png


0x05 设计玩家回合

💬 test.c     game ( )


void game()
{
  //存储数据 - 二维数组
  char board[ROW][COL];
  //初始化棋盘 - 初始化空格
  init_board(board, ROW, COL);
  //打印一下棋盘 - 本质是打印数组的内容
  print_board(board, ROW, COL);
  //玩家下棋
  player_round(board, ROW, COL);
}
💬 game.h    player_round ( )
void player_round(char board[ROW][COL], int row, int col);
💬 game.c    player_round ( )
void player_round(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 >= 1 && x <= row && y >= 1 && y <= col)
  {
    /* 判断坐标是否被占用 */
    if (board[x - 1][y - 1] == ' ') // 玩家输的坐标 -1
    {
    /* 下棋 */
    board[x - 1][y - 1] = '*';
    break;
    }
    else
    printf("[提示] 该处已经有棋子,请重新输入!\n");
  }
  else
    printf("[提示] 坐标非法,请重新输入!\n");
  }
}

0x06 设计电脑回合

💬 test.c     main ( ) 中存放随机数种子,game ( )


int main(int argc, char const* argv[])
{
    srand((unsigned int)time(NULL)); // 置随机数种子
    ...
}
void game()
{
    /* 创建棋盘 */
    char board[ROW][COL];
    /* 初始化棋盘 - 初始化空格 */
    init_board(board, ROW, COL);
    /* 打印棋盘 */
    print_board(board, ROW, COL);
    /* 玩家下棋 */
    player_round(board, ROW, COL);
    /* 电脑下棋 */
    computer_round(board, ROW, COL);
}

💬 game.h    引入随机数必要的头文件,computer_round ( )


#include <time.h>
#include <stdlib.h>
void computer_round(char board[ROW][COL], int row, int col);

💬 game.c    computer_round ( )


void computer_round(char board[ROW][COL], int row, int col)
{
  printf("[电脑回合]");
  while (1)
  {
  /* 随机坐标 */
  int x = rand() % row;
  int y = rand() % col;
  /* 判断坐标坐标是否被占用 */
  if (board[x][y] == ' ')
  {
    board[x][y] = '#';
    break;
  }
  }
}

0x07 游戏状态设计(整合玩家回合和电脑回合)

💬 test.c     game ( )  玩家和电脑走完走打印一下棋盘,更新最新的棋盘数据


void game()
{
  //存储数据 - 二维数组
  char board[ROW][COL];
  //初始化棋盘 - 初始化空格
  init_board(board, ROW, COL);
  //打印一下棋盘 - 本质是打印数组的内容
  print_board(board, ROW, COL);
  while (1)
  {
  //玩家下棋
  player_round(board, ROW, COL);
  print_board(board, ROW, COL);
  //电脑下棋
  computer_round(board, ROW, COL);
  print_board(board, ROW, COL);
  }

0x08 判断输赢&宣布胜利条件

💬 test.c     game ( )


void game()
{
  //存储数据 - 二维数组
  char board[ROW][COL];
  //初始化棋盘 - 初始化空格
  init_board(board, ROW, COL);
  //打印一下棋盘 - 本质是打印数组的内容
  print_board(board, ROW, COL);
  char ret = 0;//接受游戏状态
  while (1)
  {
  //玩家下棋
  player_round(board, ROW, COL);
  print_board(board, ROW, COL);
  //判断玩家是否赢得游戏
  ret = is_win(board, ROW, COL);
  if (ret != 'C')
    break;
  //电脑下棋
  computer_round(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
  {
  printf("平局\n");
  }
  print_board(board, ROW, COL);
}

💬 game.h    is_win ( )

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

💬 game.c    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][1] != ' ')
  {
    return  board[i][1];//
  }
  }
  /* 判断三列 */
  for (i = 0; i < col; i++)
  {
  if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
  {
    return board[1][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];
  }
  /* 判断平局 */
  //如果棋盘满了返回1, 不满返回0
  int ret = is_full(board, row, col);
  if (ret == 1)
  {
  return 'Q';
  }
  /* 继续 */
  return 'C';
}

💬 game.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; // 棋盘满了
}

0x09 代码运行

🚩 玩家获胜

524f7bf3cc58229a8d5465701394b3b2_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDUwMjg2Mg==,size_16,color_FFFFFF,t_70.png


🚩 电脑获胜

247389bef462d621b96e5318dc0b0244_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDUwMjg2Mg==,size_16,color_FFFFFF,t_70.png


🚩 平局

8c64828e6ea1dc09c69d9d9844b028e9_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDUwMjg2Mg==,size_16,color_FFFFFF,t_70.png

相关文章
|
存储 人工智能 程序员
一文彻底搞明白C语言的数组
本文详细介绍了C语言中的数组,包括定义、初始化(静态与动态)、存储方式、访问方法及常用操作,如遍历、修改元素和作为函数参数传递。数组是C语言中最基本的数据结构之一,掌握它对编程至关重要。下篇将介绍二维数组,敬请期待!
777 0
一文彻底搞明白C语言的数组
|
传感器 算法 安全
【C语言】两个数组比较详解
比较两个数组在C语言中有多种实现方法,选择合适的方法取决于具体的应用场景和性能要求。从逐元素比较到使用`memcmp`函数,再到指针优化,每种方法都有其优点和适用范围。在嵌入式系统中,考虑性能和资源限制尤为重要。通过合理选择和优化,可以有效提高程序的运行效率和可靠性。
1117 6
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
544 5
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
397 4
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
存储 编译器 C语言
【c语言】数组
本文介绍了数组的基本概念及一维和二维数组的创建、初始化、使用方法及其在内存中的存储形式。一维数组通过下标访问元素,支持初始化和动态输入输出。二维数组则通过行和列的下标访问元素,同样支持初始化和动态输入输出。此外,还简要介绍了C99标准中的变长数组,允许在运行时根据变量创建数组,但不能初始化。
354 6
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
存储 C语言
C语言:一维数组的不初始化、部分初始化、完全初始化的不同点
C语言中一维数组的初始化有三种情况:不初始化时,数组元素的值是随机的;部分初始化时,未指定的元素会被自动赋值为0;完全初始化时,所有元素都被赋予了初始值。
1745 2