基于A*算法的迷宫游戏开发

1.1 项目目标和主要内容
3×3九宫棋盘,放置数码为1 -8的8个棋牌,剩下一个空格,通过数字向空格的移动来改变棋盘的布局。

1.2 项目的主要功能
根据给定初始布局(即初始状态)和目标布局(即目标状态),如何移动棋牌才能从初始布局到达目标布局,找到合法的过程序列。
2、 项目设计
2.1 项目总体框架

2.2 系统详细设计
启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。

启发中的估价是用估价函数表示的,如:f(n) = g(n) + h(n)

其中f(n) 是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说详细点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >> g(n)时,可以省略g(n),而提高效率。
(2)
A*算法过程:
  (1). 把起点加入 open list 。
  (2). 重复如下过程:
    a. 遍历open list ,查找F值最小的节点,把它作为当前要处理的节点,然后移到close list中
    b. 对当前方格的相邻方格一一进行检查,如果它是不可抵达的或者它在close list中,忽略它。否则,做如下操作:
    □ 如果它不在open list中,把它加入open list,并且把当前方格设置为它的父亲
    □ 如果它已经在open list中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更近。如果更近,把它的父亲设置为当前方格,并重新计算它的G和F值。如果你的open list是按F值排序的话,改变后可能需要重新排序。
  c. 遇到下面情况停止搜索:
    □ 把终点加入到了 open list 中,此时路径已经找到了,或者
    □ 查找终点失败,并且open list 是空的,此时没有路径。
  (3). 从终点开始,每个方格沿着父节点移动直至起点,形成路径。

#include
#include <time.h>
using namespace std;
int const MAXSIZE = 1000;
int const MAXSTEPS = 100;
struct Node
{
int status[9];//存放数字状态
int g;//从节点到指定节点的代价
int h;//从节点到指定节点的估算成本
int o;//消耗
int zero;//0所在的位置
int step;//得到当前节点的step(1上2下3左4右)
Node* parent;//存放父亲节点
};

//目标状态
int status0[9] = { 1, 2, 3, 8, 0, 4, 7, 6, 5 };
Node Open[MAXSIZE];//用于存放那些搜索图上的叶节点,也就是已经被生成出来,但是还没被扩展的节点,用于后面的搜索操作
Node Close[MAXSIZE];//用于存放那些搜索图上的叶节点,已经被扩展的节点
int o = 0;//表示数组最后一个下标
int c = 0;
Node* node;
/*
*
1、Open中的节点按照 o 值从小到大排列。每次从Open表中取出第一个元素 n 进行扩展(最小消耗)
如果是w目标节点(即h=0),则算法找到一个解,算法结束,否则继续扩展该节点;
2、对于 w的子节点 m ,如果 m 既不在Open也不在Close, 则将 m 加入Open;
如果 m 在Close, 如果新路径耗散值大,则保持不动
如果较小,则将 m 从Close中取出放入Open中 循环此过程,直到找到最优解
/*

  • h 估算成本 (欧氏距离)
  • 计算该节点与目标节点的相同数字位置的欧氏距离
    */

int CountH(int* status) {
int h = 0;
int i, j;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
if (status0[j] == status[i])//两个数字相同的位置
break;
}
h = h + sqrt(pow((i % 3 - j % 3), 2) + pow((i / 3 - j / 3), 2));//欧式距离
}
return h;
}

/*

  • 对新节点进行初始化赋值

  • 返回该节点
    /
    Node
    initNode(int* status, int zero, int g, Node* parent, int step)
    {
    int i;
    node = new Node;
    for (i = 0; i < 9; i++)
    {
    node->status[i] = status[i];
    }
    node->zero = zero;
    node->g = g;
    node->h = CountH(node->status);
    node->o = node->g + node->h;
    node->parent = parent;
    node->step = step;
    return node;
    }
    /*

  • 判断新生成的节点是否已经存在于FInd表或Close表中

  • 0表示二者都不存在

  • 0表示在Open表中

  • <0表示在Close表中

  • +1所在的位置
    /
    int ExistOfList(Node
    n)
    {
    int i, j;
    int h = 0; //计数是否存在
    int status[9];
    node = new Node;
    node = n;
    for (i = 0; i < 9; i++)
    {
    status[i] = node->status[i];
    }
    //判断是否在Open表
    for (i = 0; i <= o - 1; i++)
    {
    for (j = 0; j < 9; j++)
    {
    if (status[j] != Open[i].status[j])
    {
    h++;//不相同累计计数;
    }
    }
    if (h == 0) //h=0证明在表中找到该节点
    {
    return i + 1; //如果在Open表中,返回i+1
    }
    h = 0; //扫描完一个节点后重置h,为下一趟遍历服务
    }
    //判断是否在Close表中
    for (i = 0; i <= c - 1; i++)
    {
    for (j = 0; j < 9; j++)
    {
    if (status[j] != Close[i].status[j])
    {
    h++;
    }
    }

      if (h == 0)                 //h=0证明在表中找到该节点
      {
      	return -(i + 1);
      }
      h = 0;                      //扫描完一个节点后重置h
    

    }

    return 0; //存在于两表中;
    }

/*

  • 根据0的位置进行移动操作,更新该节点的状态
  • 左右移动1
  • 上下移动3
    /
    int
    LEFT(int* s, int index)
    {
    int temp, i;
    static int status[9];
    for (i = 0; i < 9; i++)
    {
    status[i] = s[i];
    }
    temp = status[index - 1];
    status[index - 1] = 0;
    status[index] = temp;
    return status;
    }

int* RIGHT(int* s, int index)
{
int temp, i;
static int status[9];
for (i = 0; i < 9; i++)
{
status[i] = s[i];
}
temp = status[index + 1];
status[index + 1] = 0;
status[index] = temp;
return status;
}

int* UP(int* s, int index)
{
int temp, i;
static int status[9];
for (i = 0; i < 9; i++)
{
status[i] = s[i];
}
temp = status[index - 3];
status[index - 3] = 0;
status[index] = temp;
return status;
}

int* DOWN(int* s, int index)
{
int temp, i;
static int status[9];
for (i = 0; i < 9; i++)
{
status[i] = s[i];
}
temp = status[index + 3];
status[index + 3] = 0;
status[index] = temp;
return status;
}

/*
*筛选新节点

  • 对于新的子节点 m ,如果 m 既不在Open也不在Close, 则将 m 加入Open;
    如果 m 在ExistOfList,说明从初始节点到 m 有路径, 如果新路径耗散值大,不进行任何操作;
    如果较小,则将 m 从Close中取出放入Open中(删除原来在Close的,将新找到的放入Open)
    /
    void opreate(Node
    n)
    {
    int i;
    int result;

    node = new Node;
    node = n;
    //第一步操作,直接加入Open
    if (node->g == 1)
    {
    Open[o] = *node;
    o++;
    return;
    }

    result = ExistOfList(node);

    if (result == 0) //如果均不在两个表中,将节点加入Open表中
    {
    Open[o] = *node;
    o++;
    }

    else if (result > 0) //存在于Open中
    {
    if (Open[result - 1].o > node->o) //若p.o小于当前位置的o,则将其替换
    {
    Open[result - 1] = *node;
    }
    }

    else if (result < 0) //存在于Close
    {
    result = -result;
    if (Close[result - 1].o > node->o) //若p.o小于当前位置的o,则将其放入Open中,并在Close中释放
    {
    Open[o] = node;
    o++;
    }
    for (i = result - 1; i <= c - 1; i++) //在Close种删除当前位置的节点
    {
    Close[i] = Close[i + 1];
    }
    c–;
    }
    }
    void change(Node
    t) {
    int* status;
    if ((t->zero) % 3 >= 1) //左移,则进行左移创造新结点
    {
    node = new Node; //创造新结点
    status = LEFT(t->status, t->zero); //通过移动得到新的状态
    node = initNode(status, t->zero - 1, (t->g) + 1, t, 1); //对新节点进行初始化,g为新的一步
    opreate(node); //判断子节点是否在Open或Close中,并进行对应的操作
    }

    if ((t->zero) % 3 <= 1)
    {
    status = RIGHT(t->status, t->zero);
    node = initNode(status, t->zero + 1, (t->g) + 1, t, 2);
    opreate(node);
    }

    if (t->zero >= 3)
    {
    node = new Node;
    status = UP(t->status, t->zero);
    node = initNode(status, t->zero - 3, (t->g) + 1, t, 3);
    opreate(node);
    }

    if (t->zero <= 5)
    {
    node = new Node;
    status = DOWN(t->status, t->zero);
    node = initNode(status, t->zero + 3, (t->g) + 1, t, 4);
    opreate(node);
    }
    }

/*

  • 根据Open找到最优解

  • 每次取出Open最小值,判断该值的估算成本的欧式距离为0,进行移动操作,加入到表中,直到h=0,得到最优解
    /
    Node
    Search()
    {
    int i, j;

    Node* t;

    while (true)
    {
    if (o == 0) //如果o=0, 没有解
    return NULL;
    t = new Node;
    //排序找到最小消耗
    for (i = o - 1; i > 0; i–)
    {
    for (j = 0; j < i; j++)
    {
    if (Open[j].o > Open[j + 1].o)
    {
    *t = Open[j + 1];
    Open[j + 1] = Open[j];
    Open[j] = *t;
    }
    }
    }

      node = new Node;
      //取最小消耗
      *node = Open[0];
    
      if (CountH(node->status) == 0)      //找到最优解
      {
      	break;
      }
      //不为最优解,将其扩展到Close表中,同时在Open中删除
      t = node;
      Close[c] = *node;
      c++;
      for (i = 0; i <= o - 1; i++)
      {
      	Open[i] = Open[i + 1];
      }
      o--;
      change(t);
    

    }
    return node;
    }

/*
*显示移动过程
/
void show_all(Node
node)
{
int status[100][9];//保存每个节点的状态;
int step = 0;
int i, j;
int total = node->g;
while (node)
{
for (i = 0; i < 9; i++)
{
status[step][i] = node->status[i];
}
step++;
node = node->parent;//向上输出
}
cout << “----------------------” << endl;
cout << “一共执行了:” << total << “步” << endl;
cout << “----------------------” << endl;
for (i = step - 1; i >= 0; i–)
{
for (j = 0; j < 9; j++)
{
cout << status[i][j];
if (j == 2 || j == 5 || j == 8)
cout << endl;
else
cout << " ";
}
cout << “----------------------” << endl;
}
}
//判断逆序数(不包含0)
int inverse_num(int a[], int n) {
int sum = 0;
int i, j;
for (i = 0;i < n;i++)
{
for (j = 0;j < i;j++)
{
if (a[i] != 0 && a[j] != 0 && a[j] > a[i])
{
sum = sum + 1;
}
}
}
return sum;
}

/*
主函数
/
int main()
{
int status[9];
int i, beginTime, endTime;
Node
p;//初始状态
Node* q;
cout << “请输入初始状态:” << endl;

for (i = 0; i < 9; i++)                    //输入初始状态
{
	cin >> status[i];
}
int s1 = inverse_num(status, 9);
int s2 = inverse_num(status0, 9);
cout << "初始状态逆序数:" << s1 << endl;
cout << "目标状态逆序数:" << s2 << endl;
if (!((s1 % 2) == (s2 % 2))) {//同奇或偶,才可达
	cout << "逆序数不同奇或偶,无解" << endl;
	return 0;
}

beginTime = clock();

for (i = 0; i < 9; i++)                    //判断0节点位置
{
	if (status[i] == 0)
		break;
}

p = initNode(status, i, 0, NULL, 0);  //获得初始节点

Open[o] = *p;                         //将初始节点放入Open中
o++;
q = Search();

if (!q)
	cout << "无解" << endl;
else
	show_all(q);

endTime = clock();
cout << "Run Time:" << endTime - beginTime << "ms" << endl;

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值