#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define SIZE 3 //宏
#define SIZESQR 9
long total;
typedef char board[SIZE][SIZE];//二维数组
board target = { 1,2,3,4,5,6,7,8,0 }; // 目标状态
typedef struct mynode {//结构体
board data; //存放状态
struct mynode* parent; //存放父节点地址
struct mynode* child[4]; //存放子节点地址,最多4个
struct mynode* next; //索引指针 下一个指向的是谁
int f, h, g, how; //how中记录了其父节点如何移动生成该节点
}Node, * List;
void Nodeini(Node* nodep);
int geth();
int cansolve(Node* start);
int goal(board temp);
Node* head;
Node* stack[50], * p;
int top = 0;
void Nodeini(Node* nodep)//初始化
{
memset(nodep, 0, sizeof(Node));
}
int geth(Node* nodep)//计算h值(h值为每个节点与其目标位的总和)
{
int i, j, x, y, h = 0;
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++)
for (x = 0; x < SIZE; x++)
for (y = 0; y < SIZE; y++)
if (target[x][y] == nodep->data[i][j] && target[x][y])
h += abs(x - i) + abs(y - j);
return h;
}
void push(Node* stack[], Node* p, int* top)
{
stack[(*top)++] = p;
}
Node* pop(Node* stack[], int* top)
{
return stack[--(*top)];
}
int cansolve(Node* start) // 搜索前判断是否可解,如果逆序的奇偶性相同则可解,反之则不可解
{
long i, j, k1, k2;
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
if (!start->data[i][j])
{
start->data[i][j] = SIZESQR;
k1 = (SIZE - 1 - i) + (SIZE - 1 - j);
}
if (!target[i][j])
{
target[i][j] = SIZESQR;
k2 = (SIZE - 1 - i) + (SIZE - 1 - j);
}
}
}
for (i = 0; i < SIZESQR; i++)
{
for (j = i + 1; j < SIZESQR; j++)
{
if (start->data[i / SIZE][i % SIZE] > start->data[j / SIZE][j % SIZE]) k1++;
if (target[i / SIZE][i % SIZE] > target[j / SIZE][j % SIZE]) k2++;
}
}
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
if (start->data[i][j] == SIZESQR) start->data[i][j] = 0;
if (target[i][j] == SIZESQR) target[i][j] = 0;
}
}
return (k1 % 2) == (k2 % 2) ? 1 : 0;
}
//判断temp状态是否是目标状态
int goal(board temp)
{
if (memcmp(temp, target, sizeof(target)))
return 0;
return 1;
}
//返回0所在的坐标
void getxy(Node* temp, int* x, int* y)
{
int i, j;
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
if (!temp->data[i][j])
{
*x = i;
*y = j;
}
}
}
void new_node(Node* p, int* index, int how, int x, int y, int xi, int yi)//移动操作(节点指针,子节点序号,移动方向,x,y,x偏移量,y偏移量)
{
int i, j, k;
char tmp;
i = *(index);
p->child[i] = (List)malloc(sizeof(Node));
Nodeini(p->child[i]);
for (k = 0; k < SIZE; k++)
{
for (j = 0; j < SIZE; j++)
{
p->child[i]->data[k][j] = p->data[k][j];
}
}
tmp = p->child[i]->data[x][y];
p->child[i]->data[x][y] = p->data[x + xi][y + yi];
p->child[i]->data[x + xi][y + yi] = tmp;
p->child[i]->parent = p;
p->child[i]->h = geth(p->child[i]);
p->child[i]->g = p->g + 1;
p->child[i]->how = how;
p->child[i]->f = p->child[i]->h + p->child[i]->g;
i++;
total++;
*index = i;
}
//扩展p所指向的节点
void expand(Node* p) {
int x, y, i = 0, j = 0, k = 0;
Node* temp, * lasttemp;
getxy(p, &x, &y);
//y!=0(左边有节点)并且how!=4(不是父节点右移得到的),进行左移
if (y != 0 && p->how != 4)
new_node(p, &i, 1, x, y, 0, -1);
if (y != 2 && p->how != 1)
new_node(p, &i, 4, x, y, 0, 1);
if (x != 0 && p->how != 3)
new_node(p, &i, 2, x, y, -1, 0);
if (x != 2 && p->how != 2)
new_node(p, &i, 3, x, y, 1, 0);
//插入open表
for (j = 0; j < i; j++)
{
if (!head || (head->f >= p->child[j]->f))
{
temp = head;
head = p->child[j];
p->child[j]->next = temp;
}
else
{
temp = head;
lasttemp = temp;
while (temp && (temp->f < p->child[j]->f))
{
lasttemp = temp;
temp = temp->next;
}
if (!lasttemp->next)
p->child[j]->next = NULL;
else
p->child[j]->next = temp;
lasttemp->next = p->child[j];
}
}
p->next = head;
}
void main()
{
Node begin;//初始化
Node* start, * temp, * tempn;
int a[SIZESQR];
int i, j, k, ix, jx;
char ch;
while (1)
{
total = 1;
//输入原始状态
Nodeini(&begin);
start = &begin;
printf("┏━━━━━┓\n");
printf("┃ 菜单 ┃\n");
printf("┃1.手动输入┃\n");
printf("┃2.随机生成┃\n");
printf("┃0.退出程序┃\n");
printf("┗━━━━━┛\n");
scanf(" %c", &ch);
if (ch == '0')
break;
else if (ch == '1')
{
printf("\n请输入初始状态:\n");
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
scanf("%ld", &k);
start->data[i][j] = k;
}
}
}
else
{
srand((unsigned)time(NULL)); //随机时间种子
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
k = (int)(rand()) % SIZESQR;
start->data[i][j] = k;
a[i * SIZE + j] = k;
for (k = 0; (k < i * SIZE + j) && (i + j); k++)
if (start->data[i][j] == a[k]) { j--; break; }
}
}
}
printf("\n初始状态为:\n");
printf("┏━━━┓\n");
for (i = 0; i < SIZE; i++)
{
printf("┃");
for (j = 0; j < SIZE; j++)
printf("%d ", start->data[i][j]);
printf("┃");
printf("\n");
}
printf("┗━━━┛\n");
printf("\n目标状态为:\n");
printf("┏━━━┓\n");
for (i = 0; i < SIZE; i++)
{
printf("┃");
for (j = 0; j < SIZE; j++)
printf("%d ", target[i][j]);
printf("┃");
printf("\n");
}
printf("┗━━━┛\n");
printf("是否要修改目标状态?(y/n):\n");
scanf(" %c", &ch);
if (ch == 'Y' || ch == 'y')
{
printf("请输入新的目标状态:\n");
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
scanf("%ld", &k);
target[i][j] = k;
}
}
}
//判断是否能到目标状态
if (!cansolve(start))
{
printf("This puzzle is not solvable.\n");
continue;
}
//对原始状态节点进行初始化
start->g = 0;
start->how = 0;
start->h = geth(start);
start->f = start->h + start->g;
head = start;
//加入open表,head为头指针
while (1)
{
if (goal(head->data))
break;
//将tempn(原来的head)从open表中删除
tempn = head;
head = head->next;
//扩展tempn
expand(tempn);
//对open表进行排序
i = 0;
while (tempn->child[i] && (i < 4)) i++;
}
j = -1;
top = 0;
//输出结果
while (head)
{
push(stack, head, &top);
head = head->parent;
j++;
}
while (top)
{
p = pop(stack, &top);
printf("┏━━━┓\n");
for (ix = 0; ix < SIZE; ix++)
{
printf("┃");
for (jx = 0; jx < SIZE; jx++)
printf("%d ", p->data[ix][jx]);
printf("┃");
printf("\n");
}
printf("┗━━━┛\n");
if (top)printf(" \\/ \n");
}
printf("总共%d步!\n", j);
printf("生成节点数:%8ld\n", total);
start = start->next;
while (start)
{
temp = start;
start = start->next;
free(temp);
}
printf("\n是否继续?(y/n):");
scanf(" %c", &ch);
if (ch == 'n' || ch == 'N') break;
}
}