C语言零基础速通扫雷游戏(上)

一 . 准备环境

1,Visual Studio下载好,创建出 .c 后缀的源文件

下载:Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (microsoft.com)

2. 启动 —— 创建新项目——创建c++空项目——快捷键(CTRL + SHIFT + A)添加新项

—— 把文件后缀的 .cpp 改为 .c

3.如果用的是搜狗输入法, 建议打开中文时使用英文标点

2,复制下面代码在最前面

#define _CRT_SECURE_NO_WARNINGS 1  //防止报错
#include<stdio.h>                  //头文件, 调用库函数需要的

// 是注释,  程序在执行的时候会忽视 //后面的东西,  可以随便写 

二 . 准备知识

1.数据类型

char字符型:如:‘×’ ‘¥’ ‘2’ ‘P’,用单引号引起来表示字符型的数据

char c = '*';
//创建了一个字符变量c,c的值是'*'
//不同的字符对应不同的ASCII码值,  int型的2,和字符型的'2'只是看起来一样。
// ; 就是程序中的句号, 代表一句话结束

int整型:整数类型,如:1,-5,78,666……

int a = 666; 
//创建了一个整型变量 a,a的值是666

2.数组

        数组是一组相同元素的集合,下面的代码是两个不同类型数组初始化的例子

char arr[5] = {'1', '2', '3', '4', '5'};
//在char类型的数组arr里面放了5个元素, 分别是'1' '2' '3' '4' '5', 每个元素的类型是char型

int brr[3] = {1, 2, 3};
//整型数组brr里面放了三个元素, 分别是 1 2 3 , 元素类型是int型

数组元素存放好了之后,  我希望找到里面的元素,  怎么办呢?  数组的每个元素都有它的下标,  数组下标从0开始的. 在int brr[3]数组里, 

                brr[0] == 1

                brr[1] == 2

                brr[2] == 3                (= 可以理解为 "让它等于" ,  == 可以理解为 "是不是等于")

同理,  在char arr数组里面, arr[2] 就代表数组里面的 '3'

        如果说一维数组就是一行m个相同元素的集合,  二维数组就是n行m列相同元素的集合,

         一维数组代表线, 二维数组就是面,   多了一个维度.  以下是二维数组的初始化. 

int crr[3][5] = {1, 2, 3, 4, 5,
                 2, 3, 4, 5, 6
                 3, 4, 5, 6, 7};         //写成一排不用换行也可以

char drr[2][2] = {'*', ' ', ' ', '*'};   //第二第三个元素是空格,  空格也是字符类型的一种

二维数组的使用和一维数组一样也是通过下标来访问的,  我们习惯的行和列都是从1开始的,  而二维数组里的行和列下标都是从0开始的,  所以使用下标的时候要减1.

       

         在int crr[3][5]里 第二行第二列的元素3就是 crr[1][1]

                                  第三行第四列的元素6就是 crr[2][3]

                                  第三行第五列的元素7就是 crr[2][4]

        在char drr[2][2]里 第三个元素是 ' ' (空格)  虽然是写成一排,  但实际上还是两行两列

        所以第三个元素的位置是第二行第一列,  就是 drr[1][0]


注意:在表示数组的时候前面要加上数组的元素类型(char  /  int),  如果不加表示的其实是数组里面的元素。


3.函数 

        函数是解决特定任务的一段代码,   int main()这个就是一个函数, 也叫main(梦)函数,  C语言中代码是从main函数的第一句开始执行的,  梦开始的地方嘛~ 

        main函数前面的 int 代表main函数的返回类型是int这种数据类型 

        main(), 小括号里面什么都没有, 说明没有参数

        库函数就是已经写好了的函数,  如 printf 输出, (CTRL+F5)运行

#include<stdio.h>

int main(){

    printf("hello world!\n");
    // \n代表换行
    
    return 0;
    //return 0; 就是返回0的意思,  当函数的返回类型是void时, 就不需要return
}

printf 双引号里面的内容可以直接打印出来,  实际输出hello word (换行)

到这里,  终于可以开始做扫雷游戏的第一步了,  第一步就应该是开始的界面,  能够选择游戏开始或者游戏结束这样一个界面.  我们写一个新函数menu来实现这个功能,  这个menu函数就是自定义函数, 

#define _CRT_SECURE_NO_WARNINGS 1  //防止报错
#include<stdio.h>                  //头文件, 调用库函数需要的

void menu(){                       //void代表空的返回类型
    printf("*************************************\n");
    printf("*************  1. paly  *************\n");
    printf("*************  0. exit  *************\n");
    printf("*************************************\n");
}
int main(){
    menu();
    return 0;
}
​
接下来,  我们需要与这个程序互动,  来决定是要开始游戏还是结束游戏,  我们需要scanf函数. 这时候出现一个问题, scanf函数如何判断我的输入的数据类型呢? 这里需要用到 占位符
如果输入类型是int , 占位符需要%d, 
如果输入的类型是char,  占位符需要%c,
如果输入类型是字符串(由多个字符组成的串), 占位符需要%s, 双引号括起来的其实就是字符串
%d后面的&input,  "&" 的名字叫"取地址"
新的两段代码的意思是:   把刚刚创建的input变量的值改变为我自己输入的值
int main(){

    menu();

    int input = 0;
    scanf("%d", &input);
    
    return 0;
}

我们已知printf会打印双引号里面的内容,  而如何通过占位符, 打印我们刚刚输入的input呢 ? 

int main(){

    int input = 0;
    scanf("%d", &input);
    
    printf("%d\n", input);

    return 0;
}

在上述代码中, scanf和printf函数的参数是什么呢?

        scanf 有两个参数, 一个是 "%d", 一个是 &input, 两个参数用逗号相连

        printf有两个参数, 一个是"%d\n",  一个是input

4.if_else和do_while

        回到我们的游戏的menu函数打印的菜单我们发现,  1是开始游戏,  0是退出游戏, 这是两种不同选择,

        如果我们的input是1, 我们去执行开始游戏的代码,

        如果我们的input是0, 我们去执行结束游戏的代码,

        在c语言中如何实现呢?

int main(){
    
    menu();//menu代码放在main函数的上面就不展示了
    int input = 0;
    scanf("%d", &input);
    
    if(input == 1){
        printf("开始游戏\n");    
    }
    else(input == 0){
        printf("游戏结束\n");
    }
    return 0;
}

这个if _ else 语句有两个分支,  可是如果我输入的既不是1也不是0,  输错了,  想重新输入一次怎么办呢?  只要再加上一个分支就好了,  这时候就需要 if__else if__ else了

int main(){
    
    menu();
    int input = 0;
    scanf("%d", &input);
    
    if(input == 1){
        printf("开始游戏\n");    
    }
    else if(input == 0){
        printf("游戏结束\n");
    }
    else{
        printf("重新输入\n");
    }
    return 0;
}
这样就新添加了一个分支,  可是时间运行发现,  当输入其他值的时候,  只是打印了"重新输入"四个字,  程序并没有返回上面的scanf让我们重新输入,  (else后面没有条件是代表除了上述两个条件以外的其他所有情况)
我们需要让它循环起来,  这里使用do_while循环,  do就是执行,  当while后面的条件不满足的时候就跳出循环
 

input != 0 意思是 input 不等于 0 , 

程序从上往下执行的时候

当input不等0这个条件满足的时候,  就继续循环, 跳回到do后面, 

当input不等0这个条件不满足的时候, 就跳出循环, 也就是input等于0时循环结束, 

int main(){
    
    int input = 0;
    do{
        menu();
        scanf("%d", &input);
        
        if(input == 1){
            printf("开始游戏\n");    
        }
        else if(input == 0){
            printf("游戏结束\n");
        }
        else{
            printf("重新输入\n");
        }
    } while(input != 0)

    return 0;
}

三 . 游戏逻辑

1.game函数

当我们输入1的时候, 程序只是苍白的打印了一下"开始游戏", 显然我们需要一个game函数来实现我们的扫雷游戏

扫雷游戏都需要什么步骤呢?

        1.首先我需要打印一下我们的游戏界面,  创造函数DisplayBoard()

        2.然后我们需要在棋盘里面随机布置雷,  创造函数SetMine()

        3.最后我们要排查雷, 判断游戏胜利与否, 创造函数FindMine()

只要在我们能game函数里实现这些函数,  我们的扫雷游戏也就实现了

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void game() {

	//打印棋盘
	DisplayBoard();
	
	//随机布置雷
	SetMine();
	
	//排查雷
	FindMine();
}

void menu() {
	printf("*************************************\n");
	printf("*************  1. paly  *************\n");
	printf("*************  0. exit  *************\n");
	printf("*************************************\n");
}

int main() {

	int input = 0;
	
    do {
        menu();
		scanf("%d", &input);
        
		if (input == 1) {
			printf("游戏开始\n");
			game();
		}
		else if (input == 0) {
			printf("游戏退出\n");
		}
		else {
			printf("请重新输入\n");
		}
	} while (input != 0);

	return 0;
}

我们的游戏棋盘是什么样子的呢? 如果我希望让它9*9, 里面全都是'*'  如图


不难发现这其实是一个char类型的二维数组 里面所有的元素都是'*'

那就创建这个二维数组  char  show[9][9]  ,  不过怎么初始化呢? 难道要打上去9*9==81个'*'嘛?

这时候为了方便就不得不引入另一种循环    ---->   for循环

2.for循环

for循环的基本格式为:    

        for  ( 初始化 ; 判断 ;  调整) {                中间以分号相连

                循环体;

        }

int main(){
    
    int i = 0;
    for(i = 0; i < 10; i++)        //i++是 i = i + 1的意思, 循环每次自增1
    {
        printf("哎, 你好, 哈哈\n");
    }
    return 0;
}

i = 0 初始化决定了循环在哪里开始

i < 10 判断决定了循环在哪里结束       当 i 不满足判断 i < 10 (即 i >= 10) 的时候跳出循环

i++ 调整 决定了循环是因为什么结束的

可是我们的char show[9][9]是二维数组,  我们就需要循环嵌套循环来进行初始化,  i从0到8的每一个i值, 都有j从0到8的9次循环,  一共进行了9*9 == 81次循环,

我们先不完全初始化 char show[9][9] = {0}; 当不完全初始化的时候,其他的元素默认是0

在用for循环进行81次循环让show数组里面的所以元素都变成 ' * ' 

int i = 0;
int j = 0;
for(i = 0; i < 9; i++){
    for(j = 0; j < 9; j++)  
    {
        show[i][j] = '*';    
    }
}

我们打印棋盘的时候也可以利用这种格式, 有一些小调整,  打印的每个数组元素之间多一个空格, 分散一点,  并且在每一行元素打印完之后, 加一个\n换行.

int i = 0;
int j = 0;
for(i = 0; i < 9; i++){
    for(j = 0; j < 9; j++)
    {
        printf("%c ", show[i][j])    
    }
    printf("\n");
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void DisplayBoard(char show[9][9]){
    int i  = 0;  int j = 0;
    for(i = 0; i < 9; i++){
        for(j = 0; j < 9; j++)
        {
            printf("%c ", show[i][j]);
        }
        printf("\n");
    }
}

void game() {
    
    char show[9][9]= {0};

    int i = 0;  int j = 0;
    for(i = 0; i < 9; i++){
        for(j = 0; j < 9; j++)
        {
             show[i][j] = '*';    
        }
    }
    
	//打印棋盘
	DisplayBoard(show);
	
	//随机布置雷
	//SetMine();
	
	//排查雷
	//FindMine();
}

void menu() {
	printf("*************************************\n");
	printf("*************  1. paly  *************\n");
	printf("*************  0. exit  *************\n");
	printf("*************************************\n");
}

int main() {

	int input = 0;
	
    do {
        menu();
		scanf("%d", &input);
        
		if (input == 1) {
			printf("游戏开始\n");
			game();
		}
		else if (input == 0) {
			printf("游戏退出\n");
		}
		else {
			printf("请重新输入\n");
		}
	} while (input != 0);

	return 0;
}

3.优化1

优化一下目前的代码,   能不能在行和列前面都加上一排数字, 方便我们查找坐标, 这边直接贴出来我的方案, 

void DisplayBoard(char show[9][9]) {
	int i = 0;  int j = 0;
	printf("------------------------------------\n");
	for (i = 0; i <= 9; i++) {
		printf("%d ", i);
	}
	printf("\n");

	for (i = 0; i < 9; i++) {
		printf("%d ", i+1);
		for (j = 0; j < 9; j++)
		{
			printf("%c ", show[i][j]);
		}
		printf("\n");
	}
}

我觉得9*9的棋盘太大了, 我能不能更改一下棋盘的大小?

代码里面这么多9, 能不能一口气全改了? 

VS里数组在初始化的时候[]里面不能填变量,  这时候就需要用到我们的define了

#define ROW 5    //定义数组的行是5    
#define COL 5    //定义数组的列是5

这时候把所有代表行的都换成ROW,  把所有列的都换成COL, 需要更改的时候改一下后面的数字就可以了

4.游戏逻辑的思考

我们的目标是:   

输入一个坐标, 像(1,1) (3,5) 这样的, 游戏可以判断我这个位置是不是雷,

如果是雷就返回 "游戏失败" .  如果不是雷, 就返回周围雷的个数, 以便判断雷的位置.

把所有不是雷的地方找出来, 返回 "游戏胜利"

而且我每一次输入1开始游戏,  雷的位置要随机变化

问题

        1.怎么随机布置雷?
        2.我数组里面如何即存储字符 ' * ' ,  又存储里面是不是雷的信息呢?

        3.如果我访问到我的数组边缘怎么办? 数组边缘的周围一圈的信息里面有我不知道的信息呀, 数组大小就是9*9, 你去访问9行9列后面的或者下面的元素,  程序不就出错了吗? 

回答

        1.C语言提供srand函数可以产生随机数,  通过学习这个函数布置随机雷

        2.显然是不可能同时存储两个信息的,  但是我们可以创建一个一摸一样的二维数组专门用来存放雷的信息,  这样在一个位置通过访问不同的数组就可以找到不同的信息了

        3.我们可以创造大一圈的二维数组,  也就是11*11的数组,  而找雷的时候只找内圈的9*9, 这样就不怕越界访问啦

        有关后面的具体实现我们下期再聊~~

下一期地址: C语言零基础速通扫雷游戏(下)-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值