#一,扫雷游戏#
前段时间写了一个简单的扫雷游戏,很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。
运行情况
大发现
通过这两次的小游戏,发觉自己对于函数使功能模块化的理解更加深刻,同时对于问题的思考有了方向性,这种经验我看真的很重要,所以以后要注重这方面。