C数据结构与算法——顺序栈 应用(C语言纯享版 迷宫)

实验任务

(1) 掌握顺序栈及其C语言的表示;
(2) 掌握入栈、出栈等基本算法的实现;
(3) 掌握顺序栈的基本应用(求解迷宫通路)。

实验内容

  • 使用C语言实现顺序栈的类型定义与算法函数;
  • 编写main()函数并根据需要修改、补充相关的类型定义与函数,以实现“求解迷宫通路”问题:
  • 求解迷宫通路问题描述:
    • 给定一个M×N的迷宫图,指定一个入口与一个出口;
    • 规定行走规则为:按“上右下左”优先顺序向相邻空位移动1格,用(i,j)表示迷宫中的第i行第j列的一个方块
    • 在迷宫外围加上围墙;
  • 实现指定入口和出口的固定迷宫;
  • 实现随机入口和出口的固定迷宫;
  • 实现障碍、入口和出口都随机的迷宫。

实验源码

注意:必须在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,恢复默认的颜色 */

实验结果

C数据结构与算法——顺序栈 应用(C语言纯享版 迷宫)_第1张图片

你可能感兴趣的:(C,c语言,开发语言,经验分享,数据结构,算法,学习)