C语言简单扫雷游戏更新 & 三字棋游戏


#一,扫雷游戏#
前段时间写了一个简单的扫雷游戏,很LOW,听取了一些建议后,修复了一些BUG.看起来还能玩,废话不多说,先奉上代码。


头文件:game.h

//*Copyright(c) 2018,葵
//*All rights reserved.
//*
//*文件名称:排雷游戏
//*
//*当前版本:1.1
//*作者:葵司
//*完成日期:2018年5月5日
//*
//*取代版本:1.0
//*作者:葵司
//*


#ifndef  _GAME_H_
#define  _GAME_H_


#include <stdio.h>
#include <stdlib.h>
#include <time.h>


enum OPTION
{
	PLAY = 1,
	EXIT = 2
};


#define ROW 9
#define COL 9
#define ROWS ROW+2//为了便于寻找雷的数目,再创建一个包围游戏数组的数组
#define COLS COL+2

void interface();
int  Choose(int a);
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void PrintBoard(char board[ROWS][COLS], int rows, int cols);
void SetMine(char board[ROWS][COLS],int row,int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int GetMineCount(char mine[ROWS][COLS], int x, int  y);
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y);


#endif//_GAME_H_

游戏主函数:lucky.c


#include "game.h"

int nume = 0 ;//全局变量定义


//菜单函数
void MENU()
{
		printf("                          #####                             \n");
		printf("                   ######       ######                      \n");
		printf("              #######    1.PLAY     #######                 \n");
		printf("              #######    2.EXIT     #######                 \n");
		printf("                   ######       ######                      \n");
		printf("                          #####                             \n");
}


//选择界面
void interface()
{
	printf("*********************************************************************\n");
	printf("********************   1.菜鸟   *************************************\n");
	printf("********************   2.高手   *************************************\n");
	printf("********************   3.老鸟   *************************************\n");
	printf("*********************************************************************\n");
	printf("\n");
}


//难度选择,返回不同的雷的数目
int  Choose(int a)
{
	int num = 0;
	switch (a)
	{
	case 1:
		num = ROW;
		break;
	case 2:
		num = ROW + ROW / 2;
		break;
	case 3:
		num = ROW * 2 ;
		break;
	default:
	{
			   printf("输入错误,请重新输入!");
			   break;
	}
	}
	return num;
}



//游戏函数
void game()
{
	char mine[ROWS][COLS] = { '0' };  
	char show[ROWS][COLS] = { '0' };



	InitBoard(mine, ROW, COL, '0');//布雷的函数初始化为‘0’
	InitBoard(show, ROWS, COLS, '*');//显示的函数初始化为‘0’



	//PrintBoard(mine, ROW, COL);
	PrintBoard(show, ROW, COL);


	SetMine(mine,ROW,COL);//生成随机位置的雷
	PrintBoard(mine, ROW, COL);
	FindMine(mine, show, ROW, COL);//找雷函数
}


//执行函数
void TEST()
{
	srand((unsigned)time(NULL));
	int input = 0;
	int i = 0;
	do
	{
		MENU();
		printf("请选择->");
		scanf("%d", &input);
		printf("\n");
		switch (input)
		{
		case 1:
		{
				  interface();
				  printf("请选择难度->");
				  scanf("%d", &i);
				  printf("\n");
				  nume= Choose(i);//全局变量赋予其雷的数目
				  game();
				  break;
		}
		case 2:
			printf("您将退出游戏!\n");
			printf("\n");
			return;
		default:
			printf("您输错了,请再输入!\n");
			break;
		}
	} while (input);
}



//主函数
int main()
{
	TEST();
	system("pause");
	return 0;
}

小发现
1.全局变量的定义和声明

之前将全局变量雷数定义在头文件 game.h 中,不能对其进行初始化,现将其定义在 lucky.c 中并对其进行初始化。
一 般来说,不会将全局变量的定义写在头文件中,因为如果多个C源文件都添加了头文件,那很容易引起重定义的问题,这时候一般编译器都会提示。这就是我之前遇到问题的原因。
全局变量是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字 extern 对该变量作“外部变量声明”。表示该变量是一个已经定义的全局变量。有了此声明,就可以从“声明”处起,合法地使用该全局变量。


游戏函数:game.c

#include "game.h"

extern int nume ;//全局变量声明



//初始化数组
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 PrintBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (j = 0; j <= row; j++)
	{
		printf("%-2d ", j);
	}
	printf("\n");
	for (i = 0; i < row; i++)
	{
		printf("%-2d", i+1);
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}


//设置炸弹
void SetMine(char board[ROWS][COLS])
{
	int count =nume;
	int x = 0;
	int y = 0;
	while (count)
	{
		x = rand() % ROW + 1;
		y = rand() % COL + 1;
		if ('0' == board[x][y])
		{
			board[x][y] = '1';//有雷的地方设置为字符1
			count--;
		}
	}
}



//返回玩家输入坐标周围的雷数
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{

	int minecount = 0;


	if (mine[x - 1][y] == '1')
		minecount++;
	if (mine[x - 1][y + 1] == '1')
		minecount++;
	if (mine[x][y + 1] == '1')
		minecount++;
	if (mine[x + 1][y + 1] == '1')
		minecount++;
	if (mine[x + 1][y] == '1')
		minecount++;
	if (mine[x + 1][y - 1] == '1')
		minecount++;
	if (mine[x][y - 1] == '1')
		minecount++;
	if (mine[x - 1][y - 1] == '1')
		minecount++;
	return minecount;
}



//利用函数递归调用实现的扩展函数
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (0 == GetMineCount(mine, x, y))
		{
			show[x][y] = ' ';
			if (show[x][y - 1] == '*')
			{
				expand(mine, show, row, col, x, (y - 1));
			}

			if (show[x - 1][y - 1] == '*')
			{
				expand(mine, show, row, col, x, (y - 1));
			}
			if (show[x][y + 1] == '*')
			{
				expand(mine,show, row, col, x, (y + 1));
			}

			if (show[x - 1][y] == '*')
			{
				expand(mine, show, row, col, (x - 1), y);
			}

			if (show[x - 1][y + 1] == '*')
			{
				expand(mine, show, row, col, x, (y - 1));
			}

			if (show[x + 1][y] == '*')
			{
				expand(mine, show, row, col, (x + 1), y);
			}
			if (show[x + 1][y - 1] == '*')
			{
				expand(mine, show, row, col, x, (y - 1));
			}
			if (show[x + 1][y - 1] == '*')
			{
				expand(mine, show, row, col, x, (y - 1));
			}
		}
	else
	{
		show[x][y] = GetMineCount(mine, x, y) + '0';
	}
}



//找雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int num = 0;
	int i = 0;
	int j = 0;
	int count = 0;//计数器,防止第一次踩到雷就退出游戏

	while (1)
	{
		printf("请输入您的坐标-->:");
		scanf("%d%d", &x, &y);
		printf("\n");
		if (0 == (x >= 1 && x <= row && y >= 1 && y <= col))
		{
			printf("输入非法!请重新输入!\n");
			printf("\n");
		}
		while (1 == (x >= 1 && x <= row && y >= 1 && y <= col))
		{
			x--;
			y--;
			if ('*' != show[x][y])
			{
				printf("输入已确认区域,请重新输入!\n");
				printf("\n");
				break;
			}
			if ('*' == show[x][y])
			{
				if ('1' == mine[x][y])
				{
					if (1 == count)//判断是否第一次就踩到雷了
					{
						printf("炸了!再给你一次机会!\n");
						printf("\n");
						break;
					}
					else
					{
						system("cls");
						printf("   -_-恭喜你中奖了,你踩到雷了!GAME OVER!\n");
						printf("\n");
						return 0;
					}
				}
				else
				{
					expand(mine, show, row, col, x, y);
					PrintBoard(show, ROW, COL);
					break;
				}
			}
		}
		for (i = 0; i < row; i++)
		{
			for (j = 0; j < col; j++)
			{
				if (show[i][j] == '*')//通过判断 * 的多少来判断是否排雷成功
				{
					num++;
				}
			}
		}
		if (num == nume)
		{
			system("cls");
			printf("  ^_^恭喜你,扫雷成功!^_^\n");
			printf("\n");
			return;
		}
		num = 0;
	}
}

更新内容
1.递归函数进行扩展

扩展时对输入坐标的一周进行递归处理,扩大其范围


运行情况

这里写图片描述


#二,三字棋游戏
三字棋游戏实现的功能有:

  • 电脑随机出棋子,并输出不同情况下的结果。

头文件:game.h

//*Copyright(c) 2018 kui
//*All rights reserved
//* 
//*文件名称:三字棋游戏
//*
//*当前版本:1.1
//*作    者:葵司
//*
//*
//*完成日期:2018年4月25日
//*


#ifndef _GAME_H_
#define _GAME_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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);
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);
static int is_full(char board[ROW][COL], int row, int col);

#endif

游戏主函数:test.c

#include "game.h"

//菜单函数
void MENU()
{
	printf("****************************************\n");
	printf("*******   1.PLAY      2.EXIT   *********\n");
	printf("****************************************\n");
}


//游戏函数
void game()
{
    char ret = '1';
	char board[ROW][COL];
	Init_Board(board, ROW, COL);//初始化棋盘
	Print_Board(board, ROW, COL);//打印棋盘
	while (1)
	{
		Player_Move(board, ROW, COL);//玩家走
		ret = Is_Win(board, ROW, COL);//判断输赢
		if ('*' == ret)
		{
			system("CLS");//清屏函数
			printf("您赢了!\n");
			printf("\n");
			Print_Board(board, ROW, COL);
			break;
		}
		else if ('p' == ret)
		{
			system("CLS");
			printf("平局!\n");
			printf("\n");
			Print_Board(board, ROW, COL);
			break;
		}
		Computer_Move(board, ROW, COL);//电脑走
		ret = Is_Win(board, ROW, COL);
		if ('0' == ret)
		{
			system("CLS");
			printf("电脑赢了!\n");
			printf("\n");
			Print_Board(board, ROW, COL);
			break;
		}
		else if ('p'==ret)
		{
			system("CLS");
			printf("平局!\n");
			printf("\n");
			Print_Board(board, ROW, COL);
			break;
		}
		Print_Board(board, ROW, COL);
	}
}

void TEST()
{
	srand((unsigned)time(NULL));//生成时间随机数
	int input = 0;
	do
	{
		MENU();
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 2:
			return;
		default:
			break;
		}
	} while (input);
}


int main()
{
	TEST();
	system("pause");
	return 0;
}
小发现
随机函数 : srand() 和 rand()

1,rand()函数可以生成一个[0,RAND_MAX]间的随机整数;
2,srand()可以被认为是为rand()的“伪随机数”的结果指定一个固定的序列,若未引用srand()函数,则程序默认srand()括号中的值为1;如果引用的话,srand()括号中不能为空;
3,为了确保生成的随机数为尽可能符合概率上的随机,需要调用一个函数time()(是指返回自 Unix 纪元(January 1 1970 00:00:00 GMT)起的当前时间的秒数的函数,主要用来获取当前的系统时间,返回的结果是一个time_t类型),这个函数包含在头文件time.h里,在生成随机数的调用下需强制类型转换为(unsigned)time(),后面一个括号中必须填入(unsigned)time(NULL)或(unsigned)time(0)。


游戏函数:game.c

#include "game.h"

//初始化函数
void Init_Board(char board[ROW][COL], int row, int col)
{
	memset(board, ' ', row*col);//memset函数初始化函数
}

//打印界面
void Print_Board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("\n");
	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 != col - 1)
		{
			for (j = 0; j < row; j++)
			{
				printf("___");
				if (j < col-1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

//玩家走
void Player_Move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入您的坐标:");
		scanf("%d%d", &x, &y);
		printf("\n");
		if (1 == (1 <= x && ROW >= x && 1 <= y && COL >= y))
		{
			if (' ' == board[x-1][y-1])
			{
				board[x - 1][y - 1] = '*';//输入的坐标不能被占用
				break;
			}
			else
			{
				printf("\n");
				printf("重合输入!请重新输入!\n");
				printf("\n");
			}
		}
		else
		{
			printf("输入非法!请重新输入!\n");
			printf("\n");
		}
	}
}

//电脑走
void Computer_Move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % ROW;//随机坐标
		y = rand() % COL;
		if (' ' == board[x - 1][y - 1])
		{
            board[x][y] = '0';
			break;
		}
	}
}

//判断棋盘是否放满
static 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 x = 0;
	int y = 0;

	for (x = 1; x < row - 1; x++)
	{
		for (y = 0; y < col; y++)
		{
			if ((board[x][y] == board[x - 1][y]) && (board[x][y] == board[x + 1][y]) && board[x][y] != ' ')//横行相同
			{
				return board[x][y];
			}
		}
	}
	for (x = 0; x < row; x++)
	{
		for (y = 1; y < col - 1; y++)
		{
			if ((board[x][y] == board[x][y - 1]) && (board[x][y] == board[x][y + 1]) && board[x][y] != ' ')//竖行相同
			{
				return board[x][y];
			}
		}
	}

	for (x = 1; x < row - 1; x++)
	{
		for (y = 1; y < col - 1; y++)
		{
			if (((board[x][y] == board[x - 1][y - 1]) && (board[x][y] == board[x + 1][y + 1]) && board[x][y] != ' ')//对角线相同
				|| ((board[x][y] == board[x - 1][y + 1]) && (board[x][y] == board[x + 1][y - 1])) && board[x][y] != ' ')
				return board[x][y];
		}
	}
	if (is_full(board, ROW, COL) == 1)    //判断棋盘是否满了(平局)
	{
		return 'p';   //表示棋盘满了
	}
}

小发现
memset函数

函数原型是:void *memset(void *s, int ch, size_t n); 函数功能是:将s所指向的某一块内存中的前n个字节的内容全部设置为ch指定的ASCII值, 第一个值为指定的内存地址,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 其返回值为指向s的指针,它是对较大的结构体或数组进行清零操作的一种最快方法。 头文件是: memory.h 或 string.h 。
需要注意的:(1)memset中的第三个参数一定要使用sizeof操作符,因为每个系统下对类型长度的定义可能不一样。(2)memset中的第一个参数一定要是一个已知的、已经被分配内存的地址,否则会出错。(3)memset是按照字节对待初始化空间进行初始化的,对于单字节数据类型(char)可以初始化为任意支持的值,都没有问题,但是对于非多字节数据类型只能初始化为0,而不能初始化成别的初值,因为对所有字节按任意顺序赋值0的结果都是0。


运行情况


大发现

通过这两次的小游戏,发觉自己对于函数使功能模块化的理解更加深刻,同时对于问题的思考有了方向性,这种经验我看真的很重要,所以以后要注重这方面。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值