You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
7.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define N 3 // 拼图的维度这是一个3x3的拼图
typedef struct Node {
int puzzle[N][N]; // 存储拼图状态的数组
struct Node *parent; // 指向父节点的指针,用于追踪路径
int f, g, h; // A*算法中的 f, g, h 值
} Node;
// 创建新的拼图节点
Node *createNode(int puzzle[N][N])
{
Node *newnode = (Node *)malloc(sizeof(Node));
// 复制拼图到新节点
memcpy(newnode->puzzle, puzzle, sizeof(int) * N * N);
newnode->parent = NULL;
newnode->f = newnode->g = newnode->h = 0;
return newnode;
}
// 检查两个拼图状态是否相同
bool isSamePuzzle(int a[N][N], int b[N][N])
{
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (a[i][j] != b[i][j])
return false;
}
}
return true;
}
// 打印拼图状态
void printPuzzle(int puzzle[N][N])
{
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%-2d", puzzle[i][j]);
}
printf("\n");
}
printf("\n");
}
// 启发函数,计算当前状态到目标状态的估计代价
int heuristic(Node *current, Node *goal)
{
int h = 0;
// 计算不匹配的拼图块数量
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (current->puzzle[i][j] != goal->puzzle[i][j])
h++;
}
}
return h;
}
// 移动操作,生成新的拼图状态
Node *move(Node *current, int dir)
{
int key_x, key_y; // 记录空白块的位置
// 找到空白块的位置
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (current->puzzle[i][j] == 0)
{
key_x = i;
key_y = j;
break;
}
}
}
int new_x = key_x, new_y = key_y;
// 根据移动方向更新新块的位置,上下左右移动
switch (dir)
{
case 0:
new_x--;
break;
case 1:
new_x++;
break;
case 2:
new_y--;
break;
case 3:
new_y++;
break;
default:
break;
}
// 检查新位置是否在边界内
if (new_x < 0 || new_x >= N || new_y < 0 || new_y >= N)
return NULL;
// 创建新节点,复制当前拼图状态,并交换块的位置
Node *new_node = createNode(current->puzzle);
new_node->puzzle[key_x][key_y] = current->puzzle[new_x][new_y];
new_node->puzzle[new_x][new_y] = 0;
return new_node;
}
// A*算法,寻找最短路径
Node *AStar(Node *start, Node *goal)
{
Node *OPEN[1000]; // 开放列表,用于存储待探索的节点
Node *CLOSED[1000]; // 关闭列表,用于存储已探索的节点
int OPEN_SIZE = 0; // 开放列表的大小
int CLOSED_SIZE = 0;// 关闭列表的大小
OPEN[0] = start; // 将起始节点添加到开放列表
OPEN_SIZE = 1; // 开放列表的大小设置为1
CLOSED_SIZE = 0; // 关闭列表的大小设置为0
while (OPEN_SIZE > 0)
{
int min_f = OPEN[0]->f;
int min_index = 0;
// 查找开放列表中具有最小f值的节点
for (int i = 1; i < OPEN_SIZE; i++)
{
if (OPEN[i]->f < min_f)
{
min_f = OPEN[i]->f;
min_index = i;
}
}
Node *current = OPEN[min_index]; // 获取具有最小f值的节点
// 如果当前节点与目标状态匹配,表示找到解
if (isSamePuzzle(current->puzzle, goal->puzzle))
{
return current;
}
// 开放列表的大小减1表示从开放列表中移除了一个节点
OPEN_SIZE--;
// 将最小f值的节点移到开放列表的末尾以便稍后将其添加到关闭列表中。
// 这是为了优化开放列表的结构。
Node *temp = OPEN[min_index];
OPEN[min_index] = OPEN[OPEN_SIZE];
OPEN[OPEN_SIZE] = temp;
// 将当前节点添加到关闭列表关闭列表大小加1
CLOSED[CLOSED_SIZE++] = current;
int key = 0;
// 查找当前节点中空白块的位置
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
if (current->puzzle[i][j] == 0)
{
key = i * N + j;
break;
}
}
}
// 尝试四个方向的移动操作
for (int dir = 0; dir < 4; dir++)
{
Node *new_node = move(current, dir);
if (new_node != NULL && !isSamePuzzle(new_node->puzzle, current->puzzle))
{
int gNew = current->g + 1;
int hNew = heuristic(new_node, goal);
int fNew = gNew + hNew;
bool in_OPEN = false;
int open_index = -1;
// 检查新节点是否在开放列表中
for (int i = 0; i < OPEN_SIZE; i++)
{
if (isSamePuzzle(new_node->puzzle, OPEN[i]->puzzle))
{
in_OPEN = true;
open_index = i;
break;
}
}
bool in_CLOSED = false;
// 检查新节点是否在关闭列表中
for (int i = 0; i < CLOSED_SIZE; i++)
{
if (isSamePuzzle(new_node->puzzle, CLOSED[i]->puzzle))
{
in_CLOSED = true;
break;
}
}
// 如果新节点既不在开放列表也不在关闭列表中,将其添加到开放列表
if (!in_OPEN && !in_CLOSED)
{
new_node->g = gNew;
new_node->h = hNew;
new_node->f = fNew;
new_node->parent = current;
OPEN[OPEN_SIZE++] = new_node;
}
// 如果新节点已经在开放列表中,但新的 f 值更小,更新开放列表中已存在节点的信息
else if (in_OPEN && fNew < OPEN[open_index]->f)
{
OPEN[open_index]->g = gNew;
OPEN[open_index]->h = hNew;
OPEN[open_index]->f = fNew;
OPEN[open_index]->parent = current;
}
}
}
}
return NULL; // 无解
}
// 打印解路径
void printPath(Node *final)
{
if (final == NULL)
{
return;
}
printPath(final->parent); // 递归打印路径
for (int i = 0; i < N; i++)
{
for (int j = 0;j<N;j++)
{
printf("%-2d", final->puzzle[i][j]);
}
printf("\n");
}printf("------");
printf("\n");
}
int main()
{
int start_puzzle[N][N] = {{2, 8, 3}, {1, 6, 4}, {7, 0, 5}}; // 起始拼图状态
int goal_puzzle[N][N] = {{1, 2, 3}, {8, 0, 4}, {7, 6, 5}}; // 目标拼图状态
Node *start = createNode(start_puzzle); // 创建起始节点
Node *goal = createNode(goal_puzzle); // 创建目标节点
Node *final = AStar(start, goal); // 运行A*算法,寻找最短路径
if (final != NULL)
{
printf("Solution path:\n");
printPath(final); // 打印解路径
}
else
{
printf("No solution found.\n");
}
return 0;
}