C语言小游戏2——扫雷

代码下载

Github源码下载地址:
https://github.com/Kyrie-leon/C_Project/tree/master/MineSweeper

一、多文件思维

我们使用多文件实现扫雷游戏,培养多文件思维,分别为game.h、game.c和main.c

二、游戏总体思路与函数设计实现

扫雷游戏的主要思路为:

菜单Menu
埋雷SetMine
扫雷
ShowBoard
判断GetMine
炸死
你输了游戏结束
安全
你赢了游戏结束

这里需要注意几个重要的点:
1.我们用字符0表示没有雷,字符1表示有雷,显示的面板中对于未排除的地方用字符表示;
2.需要两个二维数组用来存放显示的面板和布雷的面板,这个数组的大小为10
10,为了防止越界的出现我们对边界进行全零填充,方便边界的检测;

上述函数封装在game.c文件中

2.1 游戏主控制函数Game

这里了解一下库函数memset
memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
在这里插入图片描述
ptr:为指针或是数组,
value:是赋给ptr的值,
num:是ptr的长度.

void Game()
{
	//定义一个随机种子用于电脑落子
	srand((unsigned long)time(NULL));
	//定义棋盘
	char board[ROW][COL];
	//棋盘初始化为空
	memset(board, ' ', sizeof(board));
	//定义变量result用于判断输赢情况
	char result='x';
	do
	{
		ShowBoard(board, ROW, COL);
		PlayerMove(board, ROW, COL);
		result = Judge(board, ROW, COL);
		//出现三子成珠,跳出循环,给出输赢结果
		if (result != NEXT)
		{
			break;
		}
		ComputerMove(board, ROW, COL);
		result = Judge(board, ROW, COL);
		//出现三子成珠情况,跳出循环,给出输赢结果
		if (result != NEXT)
		{
			break;
		}
	} while (1);

	//打印输赢结果
	if (P_COLOR == result)
	{
		printf("你赢了!\n");
	}
	else if (C_COLOR == result)
	{
		printf("你输了!\n");
	}
	else
	{
		printf("和棋!\n");
	}
	//此处再次调用显示棋盘函数,用于显示最终的落子情况,如果不调用该函数,会出现看不到最后一次落子的棋盘
	ShowBoard(board, ROW, COL);
}

2.2 游戏菜单Menu

void Menu()
{
	printf("################################\n");
	printf("####  1.Play        2.Exit  ####\n");
	printf("################################\n");
	printf("Please input your selection#: ");
}

2.3 埋雷SetMines

//埋雷
void SetMines(char mines_board[][COL], int row, int col,int num)
{
	//定义一个计数器统计埋雷的个数
	int count = num;

	//循环实现埋雷,当计数器为0表明雷已经被埋完
	while (count)
	{
		int x = rand() % 10 + 1;
		int y = rand() % 10 + 1;
		//只有没有被埋雷的地方才能埋雷同时计数器减一
		if (mines_board[x][y] == '0')
		{
			mines_board[x][y] = '1';
			count--;
		}
	}
}

2.3 显示面板ShowBoard

用一个二维数组来存储并显示,对于没有排除的位置统一用*存储

//显示每一行后的横线
void ShowLine(int nums)
{
	printf("---");
	for (int i = 0; i < nums; i++) {
		printf("-");
	}
	printf("\n");
}

//显示10*10面板,此处的显示需要不断调试计算
void ShowBoard(char show_board[][COL], int row, int col)
{
	printf("   ");
	//打印横坐标标签
	for (int i = 1; i <row-1; i++)
	{
		printf(" %d  ", i);
	}
	printf("\n");
	//双层循环,外层打印列标签,内层打印每一列
	for (int i = 1; i <row-1; i++)
	{
		printf("%2d ", i);
		for (int j = 1; j < col - 1; j++)
		{
			printf(" %c |", show_board[i][j]);
		}
		printf("\n");
		ShowLine(2 * col + col + 4);
	}

}

2.4 坐标周围布雷情况GetMines

//统计该坐标八个方向的雷
int GetMines(char mines_board[][COL], int row, int col, int x, int y)
{
	return mines_board[x - 1][y - 1] + mines_board[x - 1][y] + mines_board[x + 1][y + 1] + mines_board[x][y + 1] \
		+ mines_board[x + 1][y + 1] + mines_board[x + 1][y] + mines_board[x+1][y - 1]+mines_board[x][y-1]\
		- 8 * '0';
}

三、多文件代码清单

3.1 game.h

用于存放实现游戏的所有库函数和函数声明,以及一些宏定义方便后续更改
使用ifndef/define/endif防止头文件的重复包含和编译。

#ifndef _GAME_H
#define _GAME_H

#include <stdio.h>
#include <Windows.h>
#include <time.h>
#pragma warning(disable:4996)

//定义扫雷面板大小
#define ROW 12
#define COL 12
//定义埋雷的个数,定义面板大小的五分之一,即10*10*1/5=20
#define NUM 20


void Menu();
void Game();
#endif

3.2 game.c

用于存放实现游戏的所有函数定义

#include "game.h"

void Menu()
{
	printf("##########################\n");
	printf("## 1. Play      2. Exit ##\n");
	printf("##########################\n");
	printf("请输入# ");
}

//埋雷
void SetMines(char mines_board[][COL], int row, int col,int num)
{
	//定义一个计数器统计埋雷的个数
	int count = num;

	//循环实现埋雷,当计数器为0表明雷已经被埋完
	while (count)
	{
		int x = rand() % 10 + 1;
		int y = rand() % 10 + 1;
		//只有没有被埋雷的地方才能埋雷同时计数器减一
		if (mines_board[x][y] == '0')
		{
			mines_board[x][y] = '1';
			count--;
		}
	}
}

//显示每一行后的横线
void ShowLine(int nums)
{
	printf("---");
	for (int i = 0; i < nums; i++) {
		printf("-");
	}
	printf("\n");
}

//显示10*10面板,此处的显示需要不断调试计算
void ShowBoard(char show_board[][COL], int row, int col)
{
	printf("   ");
	//打印横坐标标签
	for (int i = 1; i <row-1; i++)
	{
		printf(" %d  ", i);
	}
	printf("\n");
	//双层循环,外层打印列标签,内层打印每一列
	for (int i = 1; i <row-1; i++)
	{
		printf("%2d ", i);
		for (int j = 1; j < col - 1; j++)
		{
			printf(" %c |", show_board[i][j]);
		}
		printf("\n");
		ShowLine(2 * col + col + 4);
	}

}



void Game()
{
	//1.定义显示面板数组与布雷数组
	char show_board[ROW][COL];
	char mines_board[ROW][COL];

	//2.初始化显示数组为*,布雷数组为0
	memset(show_board, '*', sizeof(show_board));
	memset(mines_board, '0', sizeof(mines_board));

	//3.随机埋NUM个雷,有雷的位置设为字符1,
	srand((unsigned long)time(NULL));
	SetMines(mines_board,ROW,COL,NUM);

	//定义一个计数器用于计数面板中剩余的空位,即去除边界后的数组大小减去埋雷的个数 行数-2*列数-2-埋雷的个数
	int count = (ROW - 2)*(COL - 2) - NUM;
	//定义坐标
	int x = 0;
	int y = 0;

	//循环检测雷是否排空
	do
	{
		//显示面板
		ShowBoard(show_board,ROW,COL);
		//输入坐标排雷
		printf("请输入位置坐标:");
		scanf("%d %d", &x, &y);
		//判断输入坐标是否越界,填充边界后的数组正好从1开始,10结束
		if (x < 1 || x>10 || y < 1 || y>10)
		{
			printf("您输入的坐标越界,请重新输入!\n");
			continue;
		}
		//判断输入坐标是否已排除过
		if (show_board[x][y] != '*')
		{
			printf("您输入的位置已经排除,请重新输入!\n");
			continue;
		}
		//判断输入位置是否有雷
		if (mines_board[x][y] == '1')
		{
			break;
		}


		//不满足上述条件下,判断该位置周边埋有多少个雷
		int num = GetMines(mines_board, ROW, COL, x, y);
		//统计该坐标八个方向有多少雷
		show_board[x][y] = num + '0';
		//空位数-1
		count--;
		system("cls");
	} while (count>0);

	if (count > 0)
	{
		printf("你被炸死了!\n");
	}
	else
	{
		printf("你赢了!\n");
	}
	printf("下面是雷区的排布!\n");
	ShowBoard(mines_board, ROW, COL);
}

3.3 main.c

实现三子棋游戏的主菜单和实现步骤

#include "game.h"

int main()
{
	int quit = 0;
	int select = 0;
	while (!quit)
	{
		Menu();
		scanf("%d", &select);
		switch (select)
		{
		case 1:
			Game();
			printf("要不要在来一把?\n");
			break;
		case 2:
			quit = 1;
			break;
		default:
			printf("你输入的选择有误,请重新输入!\n");
			break;
		}
	}
	printf("ByeBye\n");
	system("pause");
	return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值