(1) 掌握顺序栈及其C语言的表示;
(2) 掌握入栈、出栈等基本算法的实现;
(3) 掌握顺序栈的基本应用(求解迷宫通路)。
注意:必须在Dos窗口下运行,并且以管理员身份打开Dos窗口最佳
#include
#include
#include
#include
#include "windows.h"
#define MAXSIZE 1000
#define RATIO 0.6875 // 44/64的比例
#define DFDIR -1 // 方向默认 值
#define DISTOP 8 // 迷宫距离顶端距离格数
#define PASS 0 // 通路
#define WALL 1 // 墙
#define ENTRY 2 // 入口
#define EXIT 3 // 出口
// 可走
#define WALKU 40 // ↑
#define WALKR 41 // →
#define WALKD 42 // ↓
#define WALKL 43 // ←
#define DEAD 5 // 死路
// 延时设置
int walkDelay = 10;
int dirDelay = 10;
// 迷宫大小设置
int row = 20;
int line = 20; // 这里有一个DOS窗口BUG,未解决
typedef struct {
int x, y;
int dir; // 方向
} Node;
typedef struct {
int top;
Node *data;
} SqStack;
void Map(int map[][line]); // 生成地图
void KnuthShuffle(int map[], int length); // 洗牌算法
void swapInt(int *a, int *b); // 辅助洗牌算法 交换
void PrintMap(int map[][line]); // 打印迷宫地图
boolean InitStack(SqStack *stack); // 初始化
void Walk(SqStack *stack, int in_x, int in_y, int map[][line]); // 移动迷宫
boolean Push(SqStack *stack, Node node); // 压栈
boolean IsFull(SqStack *stack); // 判栈满
boolean IsEmpty(SqStack *stack); // 判栈空
Node GetTop(SqStack *stack); // 取栈顶元素
void Pop(SqStack *stack); // 出栈 (这里用void是以下代码中保证无需判断)
void GotoXY(int x, int y); // 将光标移至屏幕 第x列,第y行 处
void WalkPath(int map[][line], int dir, int j, int k); // 走过的路径方向设置
void DeadPath(int j, int k); // 置为死路
void DirTest(int map[][line], int dir, int j, int k); // 方向试探
void HideCursor(void); // 隐藏光标
void DisplayStack(SqStack *stack); // 栈动态展示
void Color(short x); // 自定义函根据参数改变颜色
void MazeSize(int key, int map[][line]); // 迷宫大小
void DelaySet(int key, int map[][line]); // 延时设置
void UI(); // 界面设计
/**
* 顺序栈实验
* 随机迷宫问题
* 注意:请在Dos窗口↓运行
* @return 0
*/
int main() {
while (1) {
UI(); // 界面打印
srand(time(NULL));
int map[row][line];
char key;
MazeSize(key, map); // 迷宫大小
DelaySet(key, map); // 延时设置
SqStack stack;
if (!(InitStack(&stack))) {
printf("顺序栈初始化失败~~\n");
return 0;
}
int in_x, in_y;
for (int i = 0; i < row; i++) {
for (int j = 0; j < line; j++) {
if (map[i][j] == ENTRY) {
in_x = i;
in_y = j;
}
}
}
HideCursor();
DisplayStack(&stack);
Sleep(3000);
Walk(&stack, in_x, in_y, map);
if (IsEmpty(&stack)) {
GotoXY(0, row + DISTOP + 2);
printf("无路可走,死翘翘了~~\n");
}
char quit;
GotoXY(0, row + DISTOP + 4);
printf("任意键继续(q退出):");
quit = getch();
if (quit == 'q') {
break;
} else {
system("cls");
}
}
getchar();
}
void Map(int map[][line]) {
int length = (row - 2) * (line - 2); // 8 * 8 内区域
int randArr[length];
for (int i = 0; i < length; i++) {
if (i == 0) {
randArr[i++] = ENTRY;
randArr[i++] = EXIT;
}
if (i < (length * RATIO) + 2) {
randArr[i] = PASS;
} else {
randArr[i] = WALL;
}
}
KnuthShuffle(randArr, length); // 打乱 内区域
// 赋值整张地图
for (int i = 0; i < row; i++) {
for (int j = 0; j < line; j++) {
// 这里一个小技巧:只要前面四个表达式一个为假,说明未到边界赋值,保证Length不会越界
if (i != 0 && i != row - 1 && j != 0 && j != line - 1 && length--) {
map[i][j] = randArr[length];
} else {
map[i][j] = WALL;
}
}
}
}
void KnuthShuffle(int map[], int length) {
for (int i = length - 1; i >= 1; i--) {
swapInt(&map[i], &map[rand() % (i + 1)]);
}
}
void swapInt(int *a, int *b) {
int t;
t = *a;
*a = *b;
*b = t;
}
void PrintMap(int map[][line]) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < line; j++) {
GotoXY(j * 2, i + DISTOP);
switch (map[i][j]) {
case PASS:
printf(" ");
break;
case WALL:
Color(10);
printf("围");
break;
case ENTRY:
Color(9);
printf("囚");
break;
case EXIT:
Color(11);
printf("口");
break;
}
}
printf("\n");
}
}
boolean InitStack(SqStack *stack) {
stack->data = malloc(sizeof(Node) * MAXSIZE);
if (!(stack->data)) {
return FALSE;
}
stack->top = 0;
return TRUE;
}
void Walk(SqStack *stack, int in_x, int in_y, int map[][line]) {
// 起点先入栈,为后续循环做铺垫
Node node; // 生成当前位置(起点)
node.x = in_x;
node.y = in_y;
node.dir = DFDIR;
// 无方向
Push(stack, node); // 起点入栈
while (!(IsEmpty(stack))) { // 无路可走的情况,回到起点
node = GetTop(stack); // 取出当前位置
if (map[node.x][node.y] == EXIT) { // 判断当前位置是否是终点
break;
} else { // 如果不是终点,继续走
int i, j, k;
for (i = node.dir + 1; i < 4; i++) { // 判断当前位置各个方向是否可走
switch (i) {
case 0: // 上
j = node.x - 1;
k = node.y;
DirTest(map, i, node.x, node.y);
break;
case 1: // 右
j = node.x;
k = node.y + 1;
DirTest(map, i, node.x, node.y);
break;
case 2: // 下
j = node.x + 1;
k = node.y;
DirTest(map, i, node.x, node.y);
break;
case 3: // 左
j = node.x;
k = node.y - 1;
DirTest(map, i, node.x, node.y);
break;
}
if (map[j][k] == PASS || map[j][k] == EXIT) { // 判断这个方向 是否可走
Node tNode; // 生成下一个位置
tNode.x = j;
tNode.y = k;
tNode.dir = DFDIR;
stack->data[stack->top - 1].dir = i; // 记录当前位置行走方向
Push(stack, tNode); // 下一个位置入栈
if (map[node.x][node.y] != WALL && map[node.x][node.y] != EXIT
&& map[node.x][node.y] != ENTRY) {
WalkPath(map, i, node.x, node.y); // 将当前位置置为下一个要走的位置的方向
}
break; // 退出这个位置的方向查找循环
}
}
if (i == 4) { // 四个方向都判断完,但是走不通
if (!(map[node.x][node.y] == ENTRY)) { // 保证起点不被置为ENTRY
map[node.x][node.y] = DEAD; // 把当前位置,记为死路
DeadPath(node.x, node.y);
}
Pop(stack); // 当前位置出栈
}
}
}
}
boolean Push(SqStack *stack, Node node) {
if (IsFull(stack)) {
return FALSE;
}
stack->data[stack->top++] = node;
DisplayStack(stack);
return TRUE;
}
boolean IsFull(SqStack *stack) {
return stack->top == MAXSIZE;
}
boolean IsEmpty(SqStack *stack) {
return stack->top == 0;
}
Node GetTop(SqStack *stack) {
return stack->data[stack->top - 1];
}
void Pop(SqStack *stack) {
stack->top--;
DisplayStack(stack);
}
void GotoXY(int x, int y) {
COORD pos = {x, y}; // 坐标
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取句柄(标准输出句柄)
SetConsoleCursorPosition(hOut, pos); // 设置控制台光标位置
}
void WalkPath(int map[][line], int dir, int j, int k) {
GotoXY(k * 2, j + DISTOP);
Color(13);
switch (dir) {
case 0:
printf("↑");
map[j][k] = WALKU;
break;
case 1:
printf("→");
map[j][k] = WALKR;
break;
case 2:
printf("↓");
map[j][k] = WALKD;
break;
case 3:
printf("←");
map[j][k] = WALKL;
break;
}
Sleep(walkDelay);
}
void DeadPath(int j, int k) {
GotoXY(k * 2, j + DISTOP);
Color(12);
printf("X");
}
void DirTest(int map[][line], int dir, int j, int k) {
GotoXY(k * 2, j + DISTOP);
Color(15);
switch (dir) {
case 0:
printf("↑");
break;
case 1:
printf("→");
break;
case 2:
printf("↓");
break;
case 3:
printf("←");
break;
}
Sleep(dirDelay);
GotoXY(k * 2, j + DISTOP);
Color(13);
switch (map[j][k]) {
case ENTRY:
Color(9);
printf("囚");
break;
case WALKU:
printf("↑");
break;
case WALKR:
printf("→");
break;
case WALKD:
printf("↓");
break;
case WALKL:
printf("←");
break;
}
}
void HideCursor(void) {
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void DisplayStack(SqStack *stack) {
int len = row - 1;
Color(12);
GotoXY(line * 2 + 10, DISTOP);
printf("|__i__j__di__| <- top");
for (int j = 1; j <= len; j++) {
GotoXY(line * 2 + 10, DISTOP + j);
printf("|____________|\n");
}
int length = stack->top;
for (int i = 0; i < length; i++, len--) {
if (len == 0) {
len = row - 1;
for (int j = 1; j <= len; j++) {
GotoXY(line * 2 + 10, DISTOP + j);
printf("|____________|\n");
}
}
Color(11);
GotoXY(line * 2 + 10 + 3, DISTOP + len);
printf("%d", stack->data[i].x);
GotoXY(line * 2 + 10 + 7, DISTOP + len);
printf("%d", stack->data[i].y);
GotoXY(line * 2 + 10 + 10, DISTOP + len);
printf("%d", stack->data[i].dir);
}
}
void Color(short x) {
if (x >= 0 && x <= 15) { // 参数在0-15的范围颜色
SetConsoleTextAttribute( // 调用设置控制台文本属性函数(调用获取句柄函数(不理解), 不理解)
GetStdHandle(STD_OUTPUT_HANDLE), x); // 只有一个参数,改变字体颜色
} else { // 默认的颜色白色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
}
void MazeSize(int key, int map[][line]) {
do {
do {
UI();
GotoXY(0, 5);
printf("迷宫高度(a↑ d↓):%d 格", row);
key = getch();
if (key == 'a') {
row++;
}
if (key == 'd') {
row--;
}
system("cls");
Map(map); // 生成地图
PrintMap(map); // 打印初始地图
} while (key == 'a' || key == 'd');
do {
UI();
GotoXY(0, 5);
printf("迷宫高度(a↑ d↓):%d 格", row);
GotoXY(0, 6);
printf("迷宫宽度(a↑ d↓):%d 格", line);
key = getch();
if (key == 'a') {
line++;
}
if (key == 'd') {
line--;
}
system("cls"); // 清屏
Map(map); // 生成地图
PrintMap(map); // 打印初始地图
} while (key == 'a' || key == 'd');
} while (row < 4 || line < 4 || row > 20 || line > 20);
}
void DelaySet(int key, int map[][line]) {
do {
do {
UI();
GotoXY(0, 5);
printf("行走延时(a↑ d↓):%d ms", walkDelay);
key = getch();
if (key == 'a') {
walkDelay += 10;
}
if (key == 'd') {
walkDelay -= 10;
}
system("cls");
PrintMap(map);
} while (key == 'a' || key == 'd');
do {
UI();
GotoXY(0, 5);
printf("行走延时(a↑ d↓):%d ms", walkDelay);
GotoXY(0, 6);
printf("方向延时(a↑ d↓):%d ms", dirDelay);
key = getch();
if (key == 'a') {
dirDelay += 10;
}
if (key == 'd') {
dirDelay -= 10;
}
system("cls");
PrintMap(map);
} while (key == 'a' || key == 'd');
} while (dirDelay < 0 || walkDelay < 0);
UI();
GotoXY(0, 5);
printf("行走延时(a↑ d↓):%d ms", walkDelay);
GotoXY(0, 6);
printf("方向延时(a↑ d↓):%d ms", dirDelay);
}
void UI() {
GotoXY(0, 0);
Color(9);
printf(" 使用顺序栈解决迷宫通路问题 \n");
GotoXY(0, 1);
printf("==============================\n");
GotoXY(0, 2);
Color(12);
printf("X--走过的无效通路");
Color(9);
printf(" 囚--起点\n");
GotoXY(0, 3);
Color(13);
printf("O--走过的有效通路");
Color(11);
printf(" 口--终点\n");
GotoXY(0, 4);
printf("------------------------------\n");
GotoXY(0, 7);
printf("------------------------------\n");
}
/*
解析一波:
Color(0); printf("黑色\n");
Color(9); printf("蓝色\n");
Color(2); printf("绿色\n");
Color(3); printf("湖蓝色\n");
Color(4); printf("红色\n");
Color(5); printf("紫色\n");
Color(11); printf("黄色\n");
Color(5); printf("白色\n");
Color(8); printf("灰色\n");
Color(9); printf("淡蓝色\n");
Color(2); printf("淡绿色\n");
Color(11); printf("淡浅绿色\n");
Color(12); printf("淡红色\n");
Color(13); printf("淡紫色\n");
Color(14); printf("淡黄色\n");
Color(15); printf("亮白色\n");
Color(16); // 因为这里大于15,恢复默认的颜色
*/