• C语言基础4:数组(一、二维数组的初识化、创建与存储、数组越界、冒泡排序、三子棋、扫雷实现)


    C语言基础4:数组(一、二维数组的初识化、创建与存储、数组越界、冒泡排序、三子棋、扫雷实现)

    1. 一维数组

    1.1 一维数组的创建和初始化

      数组是一组相同类型元素的集合。

    1.1.1 一维数组的创建

    type_t arr_name [const_n];
    ①type_t 是指数组的元素类型
    ②const_n 是一个常量表达式,用来指定数组的大小

    例如:

    //代码1
    int arr1[10];
    //代码2
    int count = 10;
    int arr2[count];
    //代码3
    char arr3[10];
    float arr4[1];
    double arr5[20];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注:数组创建,在C99标准之前, [] 中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念。

    1.1.2 一维数组的初始化

      数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。

    不完全初始化:剩下的元素默认为0
    int arr1[10] = {1,2,3};
    
    其他初始化的方式:
    int arr2[] = {1,2,3,4};
    int arr3[5] = {12345}char arr4[3] = {'a',98, 'c'};
    
    数组中存放了:a、b、c
    char arr5[] = {'a','b','c'};
    数组中存放了:a、b、c、d、e、f、‘/0char arr6[] = "abcdef";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      sizeof 用于计算 数组所占空间的大小,用于计算变量、数组、类型的大小(单位:字节),sizeof是操作符
      strlen 用于求字符串的长度,也就是 ‘\0’ 之前的字符个数,只能用于求字符串长度。它是库函数,使用得引用头文件。

      这里可以简单理解为:sizeof(arr) = sterlen(arr) + ‘\0’
    在这里插入图片描述

    #include<stdio.h>
    #include<string>
    int main()
    {
    	char arr4[] = "abcdef";
    	printf("%d\n", sizeof(arr4));
    	printf("%d\n", strlen(arr4));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。
    在这里插入图片描述

    #include<stdio.h>
    #include<string>
    int main()
    {
    	char arr1[] = "abcd";
    	char arr2[] = { 'a','b','c','d' };
    	printf("%d\n", sizeof(arr1));
    	printf("%d\n", strlen(arr1));
    	printf("%d\n", sizeof(arr2));
    	printf("%d\n", strlen(arr2));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.2 一维数组的使用

      [] 为下标引用操作符,其实就是数组访问的操作符。
    在这里插入图片描述
    总结:
    (1)数组是使用下标来访问的,下标是从0开始。
    (2)数组的大小可以通过计算得到。

    1.3 一维数组在内存中的存储

      数组在内存中是连续存放的,通过下面的例子,可以看出:随着数组下标的增长,元素的地址,也在有规律的递增。
    在这里插入图片描述

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <stdio.h>
    int main()
    {
    	int i = 0;
    	int sz = 0;
    	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    	sz = sizeof(arr) / sizeof(arr[0]);
    	for (i = 0; i < sz; i++)
    	{
    		printf("&arr[%d] = %p\n", i,&arr[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2. 二维数组

    2.1 二维数组的创建和初始化

    数组创建
    int arr[3][4];
    char arr[3][5];
    double arr[2][4];
    
    • 1
    • 2
    • 3
    • 4
    创建一个34列的数组,创建后我们发现他们的数据是如下存储的。
    #include<stdio.h>
    #include<string>
    int main()
    {
    	int arr[3][4] = { 1,2,3,4,5 };
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    修改代码如下,将45放在第二行中,
    #include<stdio.h>
    #include<string>
    int main()
    {
    	int arr[3][4] = { {1,2,3},{4,5} };
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
      二维数组在创建的时候,不可以采用一维数组arr[] = {1,2,3,4}这样的方式,否则会报错:
    在这里插入图片描述
      int arr[2][] = { {1,2,3},{4,5} };提示报错
      int arr[][3] = { {1,2,3},{4,5} };无报错
      也就是说,二维数组如果有初始化,行可以省略,但是列不能省略。
    在这里插入图片描述

    //数组初始化
    int arr[3][4] = {1,2,3,4};
    int arr[3][4] = {{1,2},{4,5}};
    int arr[][4] = {{2,3},{4,5}};//二维数组如果有初始化,行可以省略,列不能省略
    
    • 1
    • 2
    • 3
    • 4

    2.2 二维数组的使用

      int arr[3][4] = {{1,2,3},{4,5}},二维数组在内存中是连续存放的。他们的下标分别对应以下的行和列。

    行\列0123
    0arr[0][0]arr[0][1]arr[0][2]arr[0][3]
    1arr[1][0]arr[1][1]arr[1][2]arr[1][3]
    2arr[2][0]arr[2][1]arr[2][2]arr[2][3]

    在这里插入图片描述

    #include<stdio.h>
    #include<string>
    int main()
    {
    	int arr[3][4] = { {1,2,3},{4,5} };
    	int i = 0;
    	int j = 0;
    	for (i = 0;i < 3;i++)
    	{
    		for (j = 0;j < 4;j++)
    		{
    			printf("%d\t",arr[i][j]);
    		}
    		printf("\n");
    	}
    	return 0;
    }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.3 二维数组在内存中的存储

      修改代码如下,打印二维数组的存放的地址,观察发现二维数组arr[3][4]数组相当于存放了3个一维数组,并且每个数组所占空间为4个字节,在第1个一维数组和第2个一维数组过度的时候,他们的存储空间是连续的。
    在这里插入图片描述

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <stdio.h>
    int main()
    {
    	int arr[3][4] = { {1,2,3},{4,5} };
    	int i = 0;
    	int j = 0;
    	for (i = 0; i < 3; i++)
    	{
    		for (j = 0; j < 4; j++)
    		{
    			printf("&arr[%d][%d]=%p\t",i,j, &arr[i][j]);
    		}
    		printf("\n");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3. 数组越界

      数组的下标是有范围限制的。
      数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
    所有数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
      C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以在检查代码时,需要做好越界的检查。

    #include <stdio.h>
    int main()
    {
     int arr[10] = {1,2,3,4,5,6,7,8,9,10};
        int i = 0;
        for(i=0; i<=10; i++)
       {
            printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
       }
     return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      二维数组的行和列也可能存在越界。

    4. 数组作为函数参数

    4.1 冒泡排序

      冒泡排序核心思想:两两相比,比较后交换,例如10、9、8、7、6、5、4、3、2、1个数,需要比较9趟,每一趟需要计算相应的次数。元素个数=第n趟冒泡排序次数+该趟排序计算次数,我们通过设置一个数字n,来计算交换的次数,统计总共计算了多少次。
    在这里插入图片描述
      注意:数组在传参的时候,实际上传递过去的是数组 arr 首元素的地址 &arr[0]
    在这里插入图片描述

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <stdio.h>
    void bubble_sort(int arr[],int sz)
    {
    	int i = 0;
    	int j = 0;
    	int tmp = 0;
    	int n = 0;
    	for (i = 0; i < sz - 1; i++)	//确定冒泡排序的趟数
    	{
    		for (j = 0; j < sz - i - 1; j++)//每一趟冒泡排序
    		{
    			if (arr[j] > arr[j + 1])
    			{
    				tmp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = tmp;
    				n++;
    			}
    		}
    	}
    	printf("本次冒泡排序共计算了:%d次\n", n);
    }
    
    int main()
    {
    	int i = 0;
    	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	bubble_sort(arr,sz);
    	for (i = 0; i < sz; i++)
    	{
    		printf("%d ",arr[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    4.2 冒泡排序(优化)

      上述的冒泡排序,如果遇到一个1、2、3、4、5、6、7、8、9、10的数组,那么计算的时候也需要从头到尾再计算一遍,如何优化一下代码,避免将已经正确排序的数组再排序了呢。

      可以设置一个flag,用于标志数组是否已经正确排序。假设我们需要排序的数组是:1、10、2、3、4、5、6、7、8、9,那么用优化后的冒泡排序,总共算1趟,1趟计算9次,第2趟识别为有序的数组时就不再排序。
    在这里插入图片描述
    在这里插入图片描述

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <stdio.h>
    void bubble_sort(int arr[],int sz)
    {
    	int i = 0;
    	int j = 0;
    	int tmp = 0;
    	int n = 0;
    	for (i = 0; i < sz - 1; i++)	//确定冒泡排序的趟数
    	{
    		int flag = 1;//假设这一趟排序已经有序
    		for (j = 0; j < sz - i - 1; j++)//每一趟冒泡排序
    		{
    			if (arr[j] > arr[j + 1])
    			{
    				tmp = arr[j];
    				arr[j] = arr[j + 1];
    				arr[j + 1] = tmp;
    				n++;
    				flag = 0;//当运行到这里,说明冒泡排序还不完全
    			}
    		} 
    		if (flag == 1)
    		{
    			break;
    		}
    	}
    	printf("本次冒泡排序共计算了:%d次\n", n);
    }
    
    int main()
    {
    	int i = 0;
    	//int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
    	int arr[] = { 1,10,2,3,4,5,6,7,8,9};
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	bubble_sort(arr,sz);
    	for (i = 0; i < sz; i++)
    	{
    		printf("%d ",arr[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

      注意,上述用到的break是跳出外层for循环的意思,若没有循环,if 语句单独使用break 会报错。

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <stdio.h>
    int main()
    {
    	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
    	printf("%p\n", arr);
    	printf("%p\n", &arr[0]);
    	printf("%d\n", *arr);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
      从010FF748到1010FF74C,是因为10个元素总共40个字节,一个内存单元为8个字节,那么40÷8=5,也就是这10个元素总共占用5个内存单元,所以48+5=4C。

    #define _CRT_SECURE_NO_WARNINGS 1
    #include <stdio.h>
    int main()
    {
    	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
    	printf("%p\n", arr);	//取出数组的首元素地址
    	printf("%p\n", &arr[0]);//取出数组的首元素地址
    	printf("%p\n", &arr);	//取出的是整个数组的地址
    	printf("%d\n\n", *arr);	//取出数组的首元素地址存放的数据
    
    	printf("%p\n", arr+1);
    	printf("%p\n", &arr[0] + 1);
    	printf("%p\n", &arr+1);
    	printf("%d\n", *arr+1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    9. 数组的应用实例1:三子棋

    9.1 三子棋运行逻辑

      三子棋游戏规则:在 3 x 3 的九宫格中,双方摆上自己的棋子,当自己的3个棋子连成一条线,就算获胜,很多时候会出现和棋的情况。
      下面用程序模拟玩家和电脑进行下棋的过程,电脑的AI设置使用time 和 随机函数组合,实现电脑的随机下棋。
      整个程序分为三个部分组成:
    ①test.c:作为游戏测试逻辑的程序,主要明确游戏运行的整体框架。
    ②game.h:游戏程序的头文件,用于相关的函数声明,符合声明。
    ③game.c:游戏中个模块功能通过相关函数的实现,均存放于此。
      先看下游戏运行的整体框架逻辑:main()函数中,只有test()函数:
    (1)test()函数用于测试整个游戏的运行过程。
    (2)test()函数中,有两个函数:menu()函数、game()函数
    ①menu()函数 负责提示玩家输入键值控制游戏的开始和结束。
    ②game()函数则包含整个游戏的运行框架,其中又有各类子函数负责各相应模块的功能。
    在这里插入图片描述
    在这里插入图片描述

    9.2 头文件 game.h

    #define _CRT_SECURE_NO_WARNINGS 1
    #define ROW 3
    #define COL 3
    #include<stdio.h>
    #include<stdlib.h>
    #include<time.h>
    #include<Windows.h>
    //声明函数
    void InitBoard(char board[ROW][COL], int row, int col);
    void DisplayBoard(char board[ROW][COL], int row, int col);
    void PlayerMove(char board[ROW][COL], int row,int col);
    void ComputerMove(char board[ROW][COL], int row, int col);
    
    //告诉我们4种状态:
    //玩家赢,返回 '*'
    //电脑赢,返回 '#'
    //平局,返回'Q'
    //继续,返回'C'
    char IsWin(char board[ROW][COL], int row, int col);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    9.3 测试函数 test()

    #define _CRT_SECURE_NO_WARNINGS 1
    #include "game.h"
    void menu()
    {
        printf("*****************************************\n");
        printf("****      1.play          0.exit      ***\n");
        printf("*****************************************\n");
    }
    
    void game()
    {
        char ret = 0;
        char board[ROW][COL] = {0};//首先需要设计一个棋盘,用二维数组记录玩家存放的棋盘信息
        InitBoard(board, ROW, COL);//设置初识化棋盘函数,
        DisplayBoard(board,ROW,COL); //打印棋盘
        //开始下棋
        while (1)
        {
     
            PlayerMove(board,ROW,COL);      //玩家下棋
            DisplayBoard(board, ROW, COL);  //打印棋盘
            ret = IsWin(board, ROW, COL);   //判断如何赢
            if (ret != 'C')
            {
                break;
            }
            ComputerMove(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");
        }
    }
    
    void test()
    {
        int input = 0;
        srand((unsigned int)time(NULL));//设置随机数,用于电脑下棋
        do                         //设置一个循环,让游戏可以重复开始
        { 
            menu();                //设置一个游戏开始菜单的提示符
            printf("请选择:>");
            scanf("%d", &input);
            switch (input)
            {
            case 1:                //如果是1就进行游戏
                game();
                printf("三子棋\n");
                break;
            case 0:
                printf("退出游戏\n");
                break;
            default:
                printf("选择错误,请重新选择!\n");
                break;
            }
        } while (input);
    }
    
    int main()
    {
        test();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    9.4 游戏运行函数 game.cpp

    #define _CRT_SECURE_NO_WARNINGS 1
    #include "game.h"
    //初始化棋盘
    void InitBoard(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] = ' ';//为棋盘中的所有位置初始化为' '
    		}
    	}
    }
    
    //打印棋盘
    void DisplayBoard(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++)
    		{
    			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");
    		}
    	}
    }
    
    
    
    //玩家下棋
    void PlayerMove(char board[ROW][COL], int row, int col)
    {
    	int x = 0;
    	int y = 0;
    	printf("玩家走:>\n");
    	while (1)
    	{
    		printf("请输入要下的坐标:>");
    		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("该坐标已被占用");
    			}
    		}
    		else								//否则输入坐标非法
    		{
    			printf("坐标非法,请重新输入!\n");
    		}
    	}
    }
    
    //电脑下棋
    void  ComputerMove(char board[ROW][COL], int row, int col)
    {
    	int x = 0;
    	int y = 0;
    	printf("电脑走:>\n");//电脑的算法我们设置简单一些,随机走
    	while (1)
    	{
    		x = rand() % ROW;
    		y = rand() % COL;
    		if (board[x][y] == ' ')
    		{
    			board[x][y] = '#';		//电脑下棋用 # 填充
    			break;
    		}
    	}
    }
    
    //判断棋盘是否占满,用于电脑和人切磋过程的循环控制
    //返回1表示棋盘满了
    //返回0表示棋盘没满
    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;//棋盘满了
    }
    
    //判断输赢
    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][0] == board[i][2] && board[i][0] != ' ')
    		{
    			return board[i][0];
    		}
    	}
    	//竖三行判断是否相同
    	for (i = 0; i < col; i++)
    	{
    		if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
    		{
    			return board[0][i];
    		}
    	}
    	//两个对角线
    	if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != ' ')	
    		return board[0][0];
    	if (board[2][0] == board[1][1] && board[2][0] == board[0][2] && board[2][0] != ' ')
    		return board[2][0];
    
    	//判断是否平局
    	if (1 == IsFull(board, ROW, COL))//如果没有空格说明棋盘已经满了,返回1,说明平局
    	{
    		return 'Q';
    	}
    	return 'C';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    10. 数组的应用实例2:扫雷游戏

    10.1 扫雷运行逻辑

      一个 n x n 的格子中,有随机布防的雷,而扫雷需要做的是,在不踩到雷的前提下,将其余非雷区的空白格子点掉,当最后一个非雷区的空白格子被点掉后,则游戏胜利。否则踩到雷则游戏失败。
      我们设计扫雷的具体规则:使用鼠标,随机点击空白格子,若有雷,则游戏结束,若没有雷,则以自身为中心,提示周边 3 x 3 中所有雷的数量,直到最后一个雷被排掉则游戏胜利。
      整个程序分为三个部分组成:
    ①test.c:作为游戏测试逻辑的程序,主要明确游戏运行的整体框架。
    ②game.h:游戏程序的头文件,用于相关的函数声明,符合声明。
    ③game.c:游戏中个模块功能通过相关函数的实现,均存放于此。
      先看下游戏运行的整体框架逻辑:main()函数中,只有test()函数:
    (1)test()函数用于测试整个游戏的运行过程。
    (2)test()函数中,有两个函数:menu()函数、game()函数
    ①menu()函数 负责提示玩家输入键值控制游戏的开始和结束。
    ②game()函数则包含整个游戏的运行框架,其中又有各类子函数负责各相应模块的功能

      设计思路,如果设计一个 9 x 9 的棋盘,用于扫雷,那么玩家会点到 [0][0] 这种边界坐标,而我们判断坐标一圈是否有雷的情况,则需要统计 [-1][0]这种不存在数组中的坐标,因此我们需要设计的棋盘实际应该要大一圈,应该是 11 x 11 的棋盘。
      也就是我们实际埋雷的棋盘是 11 x 11 的,而玩家看到的棋盘则是 9 x 9 的,因此我们需要有两个棋盘,一个是用于计算埋雷计算周边有多少雷的棋盘(11 x 11),而另一个则是用于展示当前排雷进展的棋盘(9 x 9)。
    在这里插入图片描述
      经过辛苦的排雷,终于排雷成功:
    在这里插入图片描述
      出师不利,被炸:
    在这里插入图片描述

    9.2 头文件 game.h

    #define ROW 9
    #define COL 9
    #define ROWS ROW+2
    #define COLS COL+2
    #define EASY_COUNT 10
    #include <stdio.h>
    #include <time.h>
    #include <stdlib.h>
    void InitBoard(char board[ROWS][COLS], int row, int rol, char set);
    void DisPlayBoard(char board[ROWS][COLS], int row, int col);
    void SetMine(char board[ROWS][COLS], int row, int col);
    void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    9.3 测试函数 test()

    #define _CRT_SECURE_NO_WARNINGS 1
    #include "game.h"
    
    void menu()
    {
        printf("**********************************\n");
        printf("**    1.play    ***   0.exit    **\n");
        printf("**********************************\n");
    }
    
    void game()
    {
        char mine[ROWS][COLS] = { 0 };//存放布置雷的信息
        char show[ROWS][COLS] = { 0 };//存放排查后雷的信息
        InitBoard(mine, ROWS, COLS, '0');//初识化有雷的棋盘默认为字 '0'
        InitBoard(show, ROWS, COLS, '*');//初始化排查后的棋盘默认为 '*'
        DisPlayBoard(show, ROW, COL);//打印棋盘
        SetMine(mine, ROW, COL);//埋雷
        FindMine(mine, show, ROW, COL);//排雷
    }
    
    int main()
    {
        int input = 0;
        srand((unsigned int)time(NULL)); //设置随机函数,用于随机埋雷
        do
        {
            menu();
            printf("请输入1或0开始游戏:>\n");
            scanf("%d", &input);
            switch (input)
            {
            case 1:
                game();
                break;
            case 0:
                printf("游戏退出:>\n");
                break;
            default:
                printf("请重新输入:>\n");
                break;
            }
        } while (input);
        printf("扫雷结束:\n");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    9.4 游戏运行函数 game.cpp

    #define _CRT_SECURE_NO_WARNINGS 1
    #include "game.h"
    //初始化棋盘函数,可将棋盘初识化为相应的字符
    void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
    {
    	int i = 0;
    	int j = 0;
    	for (i = 0; i < rows; i++)
    	{
    		for (j = 0; j < cols; j++)
    		{
    			board[i][j] = set;
    		}
    	}
    }
    
    //打印扫雷的棋盘
    void DisPlayBoard(char board[ROWS][COLS], int rows, int cols)
    {
    	int i = 0;
    	int j = 0;
    	printf("——扫雷——:\n");
    	for ( i = 0; i <= cols; i++)
    	{
    		printf("%d", i);
    	}
    	printf("\n");
    	for (i = 1; i <= rows; i++)
    	{
    		printf("%d", i);
    		for (j = 1; j <= cols; j++)
    		{
    			printf("%c", board[i][j]);
    		}
    		printf("\n");
    	}
    	printf("——扫雷——:");
    }
    //埋雷,EASY_CONUT,用于设置雷的数量,每随机埋完一次雷后,雷减减
    void SetMine(char mine[ROWS][COLS], int row, int col)
    {
    	int count = EASY_COUNT;
    	while (count)
    	{
    		int x = rand() % row + 1;
    		int y = rand() % col + 1;
    		if (mine[x][y] == '0')
    		{
    			mine[x][y] = '1';
    			count--;
    		}
    	}
    }
    
    //统计周围一圈雷的数量
    int get_mine_count(char mine[ROWS][COLS], int x, int y)
    {
    	return mine[x - 1][y] +
    		mine[x - 1][y - 1] +
    		mine[x][y - 1] +
    		mine[x + 1][y - 1] +
    		mine[x + 1][y] +
    		mine[x + 1][y + 1] +
    		mine[x][y + 1] +
    		mine[x - 1][y + 1] - 8 * '0';
    }
    
    //排雷函数
    void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
    {
    	int x = 0;
    	int y = 0;
    	int win = 0;//win 用于统计已走的格子数量
    	//若已走格子数量<可走格子数 (棋盘总格子-雷数=可走格子数量),则执行循环,
    	while (win < row * col - EASY_COUNT)
    	{
    		printf("请输入要排雷的坐标:");
    		scanf("%d %d", &x, &y);
    		if (x >= 1 && x <= row && y >= 1 && y <= col)
    		{
    			if (mine[x][y] == '0')
    			{
    				int count = get_mine_count(mine, x, y);
    				show[x][y] = count + '0';
    				DisPlayBoard(show, ROW, COL);
    				//每排查一次雷,已走格子数量++
    				win++;
    			}
    			else
    			{
    				printf("被雷炸了:\n");
    				DisPlayBoard(mine, row, col);
    				break;
    			}
    		}
    		else
    			printf("坐标有误,请重新输入:\n");
    	}
    	//当已走格子数量等于可走格子数量时,说明剩余格子均是雷
    	if (win == ROW * COL - EASY_COUNT)
    	{
    		printf("排雷成功\n");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
  • 相关阅读:
    211 毕业就入职 30 人的小公司是什么体验
    SpringBoot讲义
    Ubuntu18.04安装QGC报错 `GLIBC_2.29‘ not found
    【Socket】①Socket技术概述
    计算机毕业设计选题推荐-智慧学生宿舍管理系统-Python项目实战
    为什么 Go for-range 的 value 值地址每次都一样?
    一种基于深度学习(卷积神经网络CNN)的人脸识别算法-含Matlab代码
    【JUC】一文弄懂@Async的使用与原理
    创建数字藏品艺术平台需要多少成本
    81.C++ STL map/ multimap容器
  • 原文地址:https://blog.csdn.net/a1766855068/article/details/125201380