大家好,这一篇博客讲的是我对C语言伪图形与键盘操作的理解。如果有错误,还请读者大大们指出。
准备test.c源文件,game.c源文件,game.h头文件。
界面选择与坐标绑定
在game.h头文件中:
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <conio.h> // getch()函数需要的头文件
#include <Windows.h> // gotoxy()函数与HideCursor()函数需要的头文件
void gotoxy(int a, int b);
void game();
在test.c源文件中:
#include "game.h"
void gotoxy(int a, int b) // 固定画面位置
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = a;
pos.Y = b;
SetConsoleCursorPosition(handle, pos);
}
void HideCursor() // 隐藏光标
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void menuPrint(int y)
{
char arr[2][13] = { "开始游戏", "退出游戏" };
printf("********************\n");
// 坐标与数组连接
if (y == 0) // 传首元素地址
printf("****%12s****\n", &arr[0][0]); // %12s占12格右对齐
else
printf("****%-12s****\n", &arr[0][0]); // %-12s占12格左对齐
if (y == 1)
printf("****%12s****\n", &arr[1][0]);
else
printf("****%-12s****\n", &arr[1][0]);
printf("********************\n");
}
void menuOperate(char ch, int* y)
{
switch (ch)
{
case 0x48: // 0x48对应向上方向键
*y = (*y - 1 + 2) % 2; // 加2后%2,可防止成为负数这两个范围
break;
case 0x50: // 0x50对应向下方向键
*y = (*y + 1) % 2; // %2防止超出数组最大下标
break;
case 'a':
//game();
break;
}
}
void menu()
{
int y = 0; // 只支持纵向移动
char ch = 0; // 接收字符
while (1)
{
gotoxy(0, 0);
menuPrint(y); // 1.菜单打印
ch = _getch(); // 2.输入字符(getch函数不需要输入回车即可执行)
menuOperate(ch, &y); // 3.菜单操作
}
}
int main()
{
HideCursor(); // 隐藏光标
menu(); // 菜单选择
return 0;
}
程序展示:
QQ录屏20231221184108
加入 \033特殊转义字符方式改变颜色
void menuPrint(int y)
{
char arr[2][13] = { "开始游戏", "退出游戏" };
printf("********************\n");
// 坐标与数组连接
if (y == 0) // 传首元素地址
printf("****\033[41m%12s\033[0m****\n", &arr[0][0]); // 红色
else
printf("****%-12s****\n", &arr[0][0]); // %-12s占12格左对齐
if (y == 1)
printf("****\033[41m%12s\033[0m****\n", &arr[1][0]); // 红色
else
printf("****%-12s****\n", &arr[1][0]);
printf("********************\n");
}
程序展示:
QQ111
进入游戏与操作执行
在game.h头文件中:
#pragma once
#define HIGH 5 // 设置游戏画面高度
#define WIGHT 5 // 设置游戏画面宽度
#include <stdio.h>
#include <conio.h> // getch()函数需要的头文件
#include <Windows.h> // gotoxy()函数与HideCursor()函数需要的头文件
void gotoxy(int a, int b);
void game();
在test.c源文件中:
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void gotoxy(int a, int b) // 固定画面位置
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = a;
pos.Y = b;
SetConsoleCursorPosition(handle, pos);
}
void HideCursor() // 隐藏光标
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void menuPrint(int y)
{
char arr[2][13] = { "开始游戏", "退出游戏" };
printf("********************\n");
// 坐标与数组连接
if (y == 0) // 传首元素地址
printf("****\033[41m%12s\033[0m****\n", &arr[0][0]); // %12s占12格右对齐
else
printf("****%-12s****\n", &arr[0][0]); // %-12s占12格左对齐
if (y == 1)
printf("****\033[41m%12s\033[0m****\n", &arr[1][0]);
else
printf("****%-12s****\n", &arr[1][0]);
printf("********************\n");
}
void menuOperate(char ch, int* y)
{
switch (ch)
{
case 0x48: // 0x48对应向上方向键
*y = (*y - 1 + 2) % 2; // 加2后%2,可防止成为负数这两个范围
break;
case 0x50: // 0x50对应向下方向键
*y = (*y + 1) % 2; // %2防止超出数组最大下标
break;
case 'a':
system("cls"); // 清屏一次
game();
break;
}
}
void menu()
{
int y = 0; // 只支持纵向移动
char ch = 0; // 接收字符
while (1)
{
gotoxy(0, 0);
menuPrint(y); // 1.菜单打印
ch = _getch(); // 2.输入字符(getch函数不需要输入回车即可执行)
menuOperate(ch, &y); // 3.菜单操作
}
}
int main()
{
HideCursor(); // 隐藏光标
menu(); // 菜单选择
return 0;
}
在game.c源文件中:
#include "game.h"
void init(char arr[HIGH][WIGHT])
{
for (int i = 0; i < HIGH; i++)
for (int j = 0; j < WIGHT; j++)
arr[i][j] = '*';
}
void game()
{
char arr[HIGH][WIGHT] = { 0 };
init(arr);
int y = 0;
int x = 0;
char ch = 0;
while (1)
{
gotoxy(0, 0);
// 1.游戏打印
for (int i = 0; i < HIGH; i++)
{
for (int j = 0; j < WIGHT; j++)
{
if (i == y && j == x) // 若对应坐标y, x,则打印背景色
{
printf("\033[42m%c\033[0m ", arr[i][j]); // 背景色绿色
}
else // 其它正常打印
{
printf("%c ", arr[i][j]);
}
}
printf("\n");
}
// 2.输入字符
ch = _getch();
// 3.游戏操作
switch (ch)
{
case 0x48:// 0x48对应向上方向键
y = (y - 1 + HIGH) % HIGH; // 成为负数时转到HIGH - 1坐标
break;
case 0x50:// 0x50对应向下方向键
y = (y + 1) % HIGH; // 超过HIGH - 1时转到0坐标
break;
case 0x4b:// 0x4b对应向左方向键
x = (x - 1 + WIGHT) % WIGHT; // 成为负数时转到WIGHT - 1坐标
break;
case 0x4d:// 0x4d对应向右方向键
x = (x + 1) % WIGHT; // 超过WIGHT - 1时转到0坐标
break;
case 'a': // 输入小写字母a将'*'转为'#'
if (arr[y][x] == '*')
arr[y][x] = '#';
break;
case 's': // 输入小写字母s将'#'转为'*'
if (arr[y][x] == '#')
arr[y][x] = '*';
break;
}
}
}
程序展示:
程序展示3
总结
想要使用伪图形,首先准备gotoxy()函数固定画面,HideCursor()函数隐藏光标(也可以只使用system(“cls”)清屏函数,不过效果不如前两者),随后确定坐标要移动的方向,字符变量与其他要准备的条件,最后在循环中执行1.打印、2.输入、3.操作即可。
实例:将C语言扫雷实现伪图形与键盘操作
(注:以下的代码和被修改的代码来自本人上一篇博客《C语言扫雷小游戏(VS2022版)》)
开始菜单
void menu1()
{
char a = 0;
int y = 0; // 只限纵轴移动
while (1)
{
gotoxy(0, 0);
// 1.打印
char arr[2][13] = { "开始游戏", "退出游戏" };
printf("*************************\n");
if (y == 0)
printf("**** \033[41m->%-10s \033[0m****\n", &arr[0][0]);
else
printf("**** %-12s ****\n", &arr[0][0]);
if (y == 1)
printf("**** \033[41m->%-10s \033[0m****\n", &arr[1][0]);
else
printf("**** %-12s ****\n", &arr[1][0]);
printf("*************************\n");
printf("操作说明:>\n");
printf("上下方向键选择\n");
printf("小写字母a确定键\n");
// 2.输入
a = _getch();
// 3.操作
switch (a)
{
case 0x48:
y = (y - 1 + 2) % 2;
break;
case 0x50:
y = (y + 1 + 2) % 2;
break;
case 'a':
if (y == 0)
{
system("cls"); // 先清屏
game();
}
else
{
system("cls");// 先清屏
printf("%s\n", "退出游戏");
exit(0);
}
break;
}
}
}
选择难度界面
int option(int* rows, int* cols)
{
char a = 0;
int y = 0; // 只限纵轴移动
while (1)
{
gotoxy(0, 0); // 固定画面
// 1.打印画面
char arr[4][20] = { "1.初级(9×9)", "2.中级(16×16)", "3.高级(16×30)", "4.自定义" };
printf("*************************\n");
if (y == 0)
printf("**** \033[41m%-15s\033[0m****\n", &arr[0][0]);
else
printf("**** %-15s****\n", &arr[0][0]);
if (y == 1)
printf("**** \033[41m%-15s\033[0m****\n", &arr[1][0]);
else
printf("**** %-15s****\n", &arr[1][0]);
if (y == 2)
printf("**** \033[41m%-15s\033[0m****\n", &arr[2][0]);
else
printf("**** %-15s****\n", &arr[2][0]);
if (y == 3)
printf("**** \033[41m%-14s\033[0m****\n", &arr[3][0]);
else
printf("**** %-14s****\n", &arr[3][0]);
printf("*************************\n");
printf("操作说明:>\n");
printf("上下方向键选择\n");
printf("小写字母a确定键\n");
// 2.输入字符
a = _getch();
// 3.移动操作
switch (a)
{
case 0x48:
y = (y - 1 + 4) % 4;
break;
case 0x50:
y = (y + 1 + 4) % 4;
break;
case 'a':
if (y == 0)
{
*rows = 11;
*cols = 11;
return 1;
}
else if (y == 1)
{
*rows = 18;
*cols = 18;
return 2;
}
else if (y == 2)
{
*rows = 18;
*cols = 32;
return 3;
}
else if (y == 3)
{
system("cls");// 先清屏
myApply(rows, cols);
return 4;
}
break;
}
}
return 1;
}
打印画面
对于findMine函数:
void findMine(char** mine, char** show, int row, int col)
{
// 注:由于扫雷下标从1开始,在 3.游戏操作 中需要变通一下
int x = 1; // 横轴移动
int y = 1; // 纵轴移动
int time1 = (int)time(NULL); // 获取进入游戏的时间
int win = 0;
int first = 1;
while (win < row * col - getMineCount && win >= 0) // 当win为负数意思为被雷炸死
{
char ch = 0;
// 1.打印
int FCount = display(show, row, col, y, x, &time1);
// 2.输入
// kbhit()函数是为防止 程序停在getch()函数接收字符时 导致时间不流逝
if (_kbhit())
ch = _getch();
// 3.游戏操作
switch (ch)
{
case 'a':
if (show[y][x] == '*') // 判断输入的坐标是否被占用
{
if (first)
{
setMine(mine, row, col, y, x);
first = 0;
}
if (mine[y][x] == '1') // 判断输入的坐标是否是雷
{
win = -9;
}
else
{
int count = mineCount(mine, y, x);
show[y][x] = count + '0';
win++;
spread(mine, show, y, x, &win, row, col);
}
}
break;
case 's':
// 扫雷游戏有一个细节:当旗子数与雷数对应就不可插旗了
if (show[y][x] == '*' && FCount < getMineCount)
show[y][x] = 'F';
break;
case 'd':
if (show[y][x] == 'F')
show[y][x] = '*';
break;
case 'f':
if (show[y][x] >= '1' && show[y][x] <= '7')
numberSpread(mine, show, y, x, show[y][x] - '0', &win, row, col);
break;
case 0x48: // 向下移动
if (y > 1) // y大于1
y = y - 1;
else
y = row; // y不大于1直接转到row
break;
case 0x50: // 向上移动
if (y < row) // y小与row
y = y + 1;
else
y = 1; // y等于row直接转到1
break;
case 0x4b: // 向左移动
if (x > 1)
x = x - 1;
else
x = col; // x不大于1直接转到col
break;
case 0x4d: // 向右移动
if (x < col)
x = x + 1;
else
x = 1; // x等于col直接转到1
break;
case 27:
system("cls");
return ;
}
}
if (win == row * col - getMineCount)
{
printf("**********************\n");
printf("****** 你赢了 ******\n");
printf("**********************\n");
printf("按任意键返回:>\n");
char winTime = _getch();
system("cls");
}
else
{
display(mine, row, col, y, x, &time1);
printf("**********************\n");
printf("**很遗憾,你被炸死了**\n");
printf("**********************\n");
printf("按任意键返回:>\n");
char falseTime = _getch();
system("cls");
}
}
对于display函数:
int display(char** arr, int row, int col, int y, int x, int* time1)
{
gotoxy(0, 0);
int count = 0;
//游戏进行时间 为 当前时间 减去 进入游戏的时间
int time2 = (int)time(NULL) - *time1;
int i, j;
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("time:%d", time2); // 打印游戏时间
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("\n");
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++) // 打印扫雷内容
{
if (i == y && j == x)
{
printf("\033[42m%c\033[0m ", arr[i][j]);
if (arr[i][j] == 'F')
count++;
}
else
{
if (arr[i][j] == '*')
printf("%c ", arr[i][j]);
if (arr[i][j] == '0')
printf("\033[30m%c\033[0m ", arr[i][j]); // 黑色
if (arr[i][j] == '1')
printf("\033[36m%c\033[0m ", arr[i][j]); // 浅蓝色
if (arr[i][j] == '2')
printf("\033[34m%c\033[0m ", arr[i][j]); // 蓝色
if (arr[i][j] == '3')
printf("\033[33m%c\033[0m ", arr[i][j]); // 黄色
if (arr[i][j] == '4')
printf("\033[31m%c\033[0m ", arr[i][j]); // 红色
if (arr[i][j] == '5')
printf("\033[35m%c\033[0m ", arr[i][j]); // 紫色
if (arr[i][j] == '6')
printf("%c ", arr[i][j]);
if (arr[i][j] == '7')
printf("%c ", arr[i][j]);
if (arr[i][j] == 'F')
{
printf("\033[32m%c\033[0m ", arr[i][j]); // 绿色
count++;
}
}
}
printf("\n"); // 换行
}
if (count > getMineCount) // 防止雷数量成为负数
count = getMineCount;
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("雷:\033[31m%d\033[0m", getMineCount - count); // 表示当前雷的数量
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("\n");
printf("********************操作说明**********************\n");
printf("**** Esc.返回 a.查找 s.插旗 d.取旗 f.数字展开 ****\n");
printf("**************************************************\n");
return count; // 获得当前F的数量
}
伪图形C语言扫雷源代码
在game.h头文件中:
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <time.h> // time 函数需要的头文件
#include <stdlib.h> // rand、 srand、malloc 函数需要的头文件
#include <conio.h>
#include <windows.h>
void initBoard(char** arr, int rows, int cols, char set);
int display(char** arr, int row, int col, int y, int x, int* time);
void setMine(char** mine, int row, int col, int y, int x);
void findMine(char** mine, char** show, int row, int col);
void spread(char** mine, char** show, int y, int x, int* win, int row, int col);
void numberSpread(char** mine, char** show, int y, int x, int num, int* win, int row, int col);
// 动态扫雷实现
char** apply(int* rows, int* cols);
void release(char** arr, int rows, int cols);
int getMine();
void myApply(int* rows, int* cols);
int myGetMine();
// 伪图形扫雷实现
void game();
void gotoxy(int a, int b);
void HideCursor();
在test.c源文件中:
#include "game.h"
int getMineCount = 0;
void menu1()
{
char a = 0;
int y = 0; // 只限纵轴移动
while (1)
{
gotoxy(0, 0);
// 1.打印
char arr[2][13] = { "开始游戏", "退出游戏" };
printf("*************************\n");
if (y == 0)
printf("**** \033[41m->%-10s \033[0m****\n", &arr[0][0]);
else
printf("**** %-12s ****\n", &arr[0][0]);
if (y == 1)
printf("**** \033[41m->%-10s \033[0m****\n", &arr[1][0]);
else
printf("**** %-12s ****\n", &arr[1][0]);
printf("*************************\n");
printf("操作说明:>\n");
printf("上下方向键选择\n");
printf("小写字母a确定键\n");
// 2.读取
a = _getch();
// 3.选择
switch (a)
{
case 0x48:
y = (y - 1 + 2) % 2;
break;
case 0x50:
y = (y + 1 + 2) % 2;
break;
case 'a':
if (y == 0)
{
system("cls"); // 先清屏
game();
}
else
{
system("cls");// 先清屏
printf("%s\n", "退出游戏");
exit(0);
}
break;
}
}
}
int option(int* rows, int* cols)
{
char a = 0;
int y = 0; // 只限纵轴移动
while (1)
{
gotoxy(0, 0); // 固定画面
// 1.打印画面
char arr[4][20] = { "1.初级(9×9)", "2.中级(16×16)", "3.高级(16×30)", "4.自定义" };
printf("*************************\n");
if (y == 0)
printf("**** \033[41m%-15s\033[0m****\n", &arr[0][0]);
else
printf("**** %-15s****\n", &arr[0][0]);
if (y == 1)
printf("**** \033[41m%-15s\033[0m****\n", &arr[1][0]);
else
printf("**** %-15s****\n", &arr[1][0]);
if (y == 2)
printf("**** \033[41m%-15s\033[0m****\n", &arr[2][0]);
else
printf("**** %-15s****\n", &arr[2][0]);
if (y == 3)
printf("**** \033[41m%-14s\033[0m****\n", &arr[3][0]);
else
printf("**** %-14s****\n", &arr[3][0]);
printf("*************************\n");
printf("操作说明:>\n");
printf("上下方向键选择\n");
printf("小写字母a确定键\n");
// 2.输入字符
a = _getch();
// 3.移动操作
switch (a)
{
case 0x48:
y = (y - 1 + 4) % 4;
break;
case 0x50:
y = (y + 1 + 4) % 4;
break;
case 'a':
if (y == 0)
{
*rows = 11;
*cols = 11;
return 1;
}
else if (y == 1)
{
*rows = 18;
*cols = 18;
return 2;
}
else if (y == 2)
{
*rows = 18;
*cols = 32;
return 3;
}
else if (y == 3)
{
system("cls");// 先清屏
myApply(rows, cols);
return 4;
}
break;
}
}
return 1;
}
void game()
{
int rows = 0;
int cols = 0;
int num = option(&rows, &cols);
char** mine = apply(&rows, &cols);
char** show = apply(&rows, &cols);
getMineCount = getMine(num);
int row = rows - 2;
int col = cols - 2;
initBoard(mine, rows, cols, '0');
initBoard(show, rows, cols, '*');
system("cls");// 先清屏
findMine(mine, show, row, col);
release(mine, rows, cols);
release(show, rows, cols);
}
void test()
{
HideCursor();
srand((unsigned int)time(NULL)); // 使rand函数产生伪随机数
menu1();
}
int main()
{
test();
return 0;
}
在game.c源文件中:
#include "game.h"
extern int getMineCount;
void gotoxy(int a, int b)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = a;
pos.Y = b;
SetConsoleCursorPosition(handle, pos);
}
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), & cursor_info);
}
void initBoard(char** arr, int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
arr[i][j] = set;
}
int display(char** arr, int row, int col, int y, int x, int* time1)
{
gotoxy(0, 0);
int count = 0;
//游戏进行时间 为 当前时间 减去 进入游戏的时间
int time2 = (int)time(NULL) - *time1;
int i, j;
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("time:%d", time2);
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("\n");
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++) // 打印扫雷内容
{
if (i == y && j == x)
{
printf("\033[42m%c\033[0m ", arr[i][j]);
if (arr[i][j] == 'F')
count++;
}
else
{
if (arr[i][j] == '*')
printf("%c ", arr[i][j]);
if (arr[i][j] == '0')
printf("\033[30m%c\033[0m ", arr[i][j]); // 黑色
if (arr[i][j] == '1')
printf("\033[36m%c\033[0m ", arr[i][j]); // 浅蓝色
if (arr[i][j] == '2')
printf("\033[34m%c\033[0m ", arr[i][j]); // 蓝色
if (arr[i][j] == '3')
printf("\033[33m%c\033[0m ", arr[i][j]); // 黄色
if (arr[i][j] == '4')
printf("\033[31m%c\033[0m ", arr[i][j]); // 红色
if (arr[i][j] == '5')
printf("\033[35m%c\033[0m ", arr[i][j]); // 紫色
if (arr[i][j] == '6')
printf("%c ", arr[i][j]);
if (arr[i][j] == '7')
printf("%c ", arr[i][j]);
if (arr[i][j] == 'F')
{
printf("\033[32m%c\033[0m ", arr[i][j]); // 绿色
count++;
}
}
}
printf("\n"); // 换行
}
if (count > getMineCount) // 防止雷数量成为负数
count = getMineCount;
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("雷:\033[31m%d\033[0m", getMineCount - count); // 表示当前雷的数量
for (i = 0; i <= col / 2 - 2; i++)
printf("--");
printf("\n");
printf("********************操作说明**********************\n");
printf("**** Esc.返回 a.查找 s.插旗 d.取旗 f.数字展开 ****\n");
printf("**************************************************\n");
return count; // 获得当前F的数量
}
void setMine(char** mine, int row, int col, int y, int x)
{
int count = 0;
int i, j;
// 记录输入行和旁边的两行
int judgeRow[3] = { 0 };
for (int k = -1, a = 0; k <= 1; k++, a++)
judgeRow[a] = k + y;
// 记录输入列和旁边的两列
int judgeCol[3] = { 0 };
for (int k = -1, a = 0; k <= 1; k++, a++)
judgeCol[a] = k + x;
while (count < getMineCount) // 布置雷的数量,数量到达时则跳出循环
{
i = rand() % row + 1; // 行
// 当输入行且旁边两行与布置行不同时
if (i != judgeRow[0] && i != judgeRow[1] && i != judgeRow[2])
j = rand() % col + 1; // 列
// 当输入行或旁边两行与布置行相同时
else
{
do
{
j = rand() % col + 1;
// 若输入列或旁边两列与布置列相同则进入循环
} while (j == judgeCol[0] || j == judgeCol[1] || j == judgeCol[2]);
}
if (mine[i][j] == '0') // 若为 '0',则放置雷
{
mine[i][j] = '1';
count++;
}
}
}
int mineCount(char** mine, int y, int x)
{
int count = 0;
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
if (mine[y + i][x + j] == '1')
count++;
return count;
}
void findMine(char** mine, char** show, int row, int col)
{
// 注:由于扫雷下标从1开始,在 3.游戏操作 中需要变通一下
int x = 1; // 横轴移动
int y = 1; // 纵轴移动
int time1 = (int)time(NULL); // 获取进入游戏的时间
int win = 0;
int first = 1;
while (win < row * col - getMineCount && win >= 0) // 当win为负数意思为被雷炸死
{
char ch = 0;
// 1.打印
int FCount = display(show, row, col, y, x, &time1);
// 2.输入
// kbhit()函数是为防止 程序停在getch()函数接收字符时 导致的时间不流逝
if (_kbhit())
ch = _getch();
// 3.游戏操作
switch (ch)
{
case 'a':
if (show[y][x] == '*') // 判断输入的坐标是否被占用
{
if (first)
{
setMine(mine, row, col, y, x);
first = 0;
}
if (mine[y][x] == '1') // 判断输入的坐标是否是雷
{
win = -9;
}
else
{
int count = mineCount(mine, y, x);
show[y][x] = count + '0';
win++;
spread(mine, show, y, x, &win, row, col);
}
}
break;
case 's':
// 扫雷游戏有一个细节:当旗子数与雷数对应就不可插旗了
if (show[y][x] == '*' && FCount < getMineCount)
show[y][x] = 'F';
break;
case 'd':
if (show[y][x] == 'F')
show[y][x] = '*';
break;
case 'f':
if (show[y][x] >= '1' && show[y][x] <= '7')
numberSpread(mine, show, y, x, show[y][x] - '0', &win, row, col);
break;
case 0x48: // 向下移动
if (y > 1) // y大于1
y = y - 1;
else
y = row; // y不大于1直接转到row
break;
case 0x50: // 向上移动
if (y < row) // y小与row
y = y + 1;
else
y = 1; // y等于row直接转到1
break;
case 0x4b: // 向左移动
if (x > 1)
x = x - 1;
else
x = col; // x不大于1直接转到col
break;
case 0x4d: // 向右移动
if (x < col)
x = x + 1;
else
x = 1; // x等于col直接转到1
break;
case 27:
system("cls");
return ;
}
}
if (win == row * col - getMineCount)
{
printf("**********************\n");
printf("****** 你赢了 ******\n");
printf("**********************\n");
printf("按任意键返回:>\n");
char winTime = _getch();
system("cls");
}
else
{
display(mine, row, col, y, x, &time1);
printf("**********************\n");
printf("**很遗憾,你被炸死了**\n");
printf("**********************\n");
printf("按任意键返回:>\n");
char falseTime = _getch();
system("cls");
}
}
void spread(char** mine, char** show, int y, int x, int* win, int row, int col)
{
if (show[y][x] == '0') // 若周围没有雷才进入
{
for (int i = -1; i <= 1; i++) // 行
{
for (int j = -1; j <= 1; j++) // 列
{
if (y + i >= 1 && y + i <= row && x + j >= 1 && x + j <= col) // 防止超出9×9的范围
{
if (show[y + i][x + j] == '*') // 防止反复递归同一个坐标
{
int count = mineCount(mine, y + i, x + j);
show[y + i][x + j] = count + '0'; // 将已经递归过的坐标显示它周围雷的数量,防止反复递归同一个坐标
(*win)++; // 增加查找非雷坐标的数量
spread(mine, show, y + i, x + j, win, row, col); // 进入下一次递归
}
}
}
}
}
}
void numberSpread(char** mine, char** show, int y, int x, int num, int* win, int row, int col)
{
int unknown = 0; // 未知坐标的数量
int unknownBlank = 0;// 未知坐标且未插旗的数量
int FCount = 0; // 插旗坐标的数量
// 记录周围三者的数量
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
if (show[y + i][x + j] == '*' || show[y + i][x + j] == 'F')
unknown++;
if (show[y + i][x + j] == 'F')
FCount++;
if (show[y + i][x + j] == '*')
unknownBlank++;
}
}
if (unknown > num && FCount >= num && unknownBlank != 0) // 周围未知的坐标的数量要大于周围雷的数量
{
for (int i = -1; i <= 1; i++) // 行
{
for (int j = -1; j <= 1; j++) // 列
{
// 防止进入边框坐标
if (y + i >= 1 && y + i <= row && x + j >= 1 && x + j <= col)
{
// 数字展开时扫到雷
if (show[y + i][x + j] == '*' && mine[y + i][x + j] == '1')
{
(*win) = -9;
}
// 数字展开扫到非雷
else if (show[y + i][x + j] == '*' && mine[y + i][x + j] == '0')
{
int count = mineCount(mine, y + i, x + j);
show[y + i][x + j] = count + '0';
(*win)++;
spread(mine, show, y + i, x + j, win, row, col);
}
}
}
}
}
}
char** apply(int* rows, int* cols)
{
// 申请二级指针(二维数组)行的数量
char** arr = (char**)malloc(sizeof(char*) * (*rows));
if (NULL == arr)
{
printf("游戏异常,已退出\n");
exit(-1);
}
// 申请每行之中列的数量
for (int i = 0; i < *rows; i++)
{
arr[i] = (char*)malloc(sizeof(char) * (*cols));
if (NULL == arr[i])
{
printf("游戏异常,已退出\n");
exit(-1);
}
}
// 返回首元素地址
return arr;
}
void release(char** arr, int rows, int cols)
{
// 先释放一级指针空间
for (int i = 0; i < rows; i++)
free(arr[i]);
// 后释放二级指针的空间
free(arr);
}
int getMine(int num)
{
switch (num)
{
case 1:
return 10;
case 2:
return 40;
case 3:
return 99;
case 4:
return myGetMine();
}
return 0;
}
void myApply(int* rows, int* cols)
{
printf("***********************************************\n");
printf("****************** 提前声明:******************\n");
printf("*自定义行和列建议至少全部都大于3,否者后果自负*\n");
printf("***********************************************\n");
printf("请输入行数:>");
scanf("%d", rows);
getchar();
if (*rows < 1)
{
*rows = 4;
printf("行数小于1,自动改成4行\n");
}
printf("请输入列数:>");
scanf("%d", cols);
getchar();
if (*cols < 1)
{
*cols = 4;
printf("列数小于1,自动改成4列\n");
}
(*rows) = (*rows) + 2;
(*cols) = (*cols) + 2;
}
int myGetMine()
{
int n = 0;
printf("***********************************************\n");
printf("****************** 提前声明:******************\n");
printf("******自定义雷的数量建议最多:行 × 列 - 9 *****\n");
printf("*****************否者后果自负******************\n");
printf("请布置雷的数量:>");
scanf("%d", &n);
getchar();
if (n < 0)
{
printf("数量小于1,改为0");
n = 0;
}
return n;
}
程序展示:
petal_20231221_221540
结语
以上便是所有内容,若文章有错误或游戏有Bug,请联系我,我会及时更改。文章来源:https://www.toymoban.com/news/detail-783543.html
谢谢观看文章来源地址https://www.toymoban.com/news/detail-783543.html
到了这里,关于C语言伪图形与键盘操作加扫雷实例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!