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

#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;
}