利用JavaScript在网页实现八数码启发式A*算法动画效果

作者:流萤时间:2026-10-22 03:10:27浏览:130

本文介绍如何使用JavaScript在网页中实现八数码问题的启发式A*算法,并通过动画展示求解过程。文章详细讲解了HTML界面构建、状态节点类Graph的设计、评估函数evaluateGraph与移动函数moveGraph的实现,以及基于优先队列的Search搜索逻辑。最终通过定时器逐帧弹出解路径节点,实现可视化的动画效果,帮助理解A*算法在路径搜索中的应用。

八数码问题与A*算法概述

近期人工智能课程布置了一项关于八数码问题的实验任务。网络上虽有不少关于八数码启发式A*算法的资源,但多数采用C或C++在控制台环境中实现。为此,笔者尝试使用JavaScript在网页端复现了类似的功能。

八数码问题本质上是一个3x3的九宫格布局,其中包含数字1至8以及一个空格(通常用0表示)。

八数码问题与A*算法概述

通过移动空格,目标是使九宫格内的数字排列恢复为有序状态,具体目标布局参考下图。

八数码问题与A*算法概述

启发式算法的核心在于利用启发函数,在求解过程中剔除不符合规则的解节点,从而有效缩小问题的解空间。

A*算法是一种基于评价函数的启发式搜索方法。在本例中,评价函数通过计算当前节点状态与最终目标状态之间不同格子的数量来评估节点的优劣。算法会优先存储并展开评价较高的节点,同时丢弃评价较低的节点。

Web界面与数据初始化

在Web端实现该功能,首先需要在HTML文件中添加九个如图所示的input文本框,并设置背景图片以模拟数码格的外观。

八数码问题与A*算法概述

页面的基础HTML代码如下。


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>八数码</title>
 <style type="text/css">
 #result input{
  display: inline-block;
  font-family:"微软雅黑";
  font-size: 60px;
  font-weight: 900;
  text-align: center;
  width:100px;
  height:100px;
  background:url(images/0.png);
  background-size:cover;
 }
</style>
</head>
<body>
 <div id="result">
  <input type="text" id="r1">
  <input type="text" id="r2">
  <input type="text" id="r3"><br>
  <input type="text" id="r4">
  <input type="text" id="r5">
  <input type="text" id="r6"><br>
  <input type="text" id="r7">
  <input type="text" id="r8">
  <input type="text" id="r9"><br>
 </div>
 <button onclick="run()">求解</button>
</body>
</html>

接下来,利用JavaScript获取用户输入的初始状态值,并将其存储在一个二维数组中以便处理。


var startArray=[[8,1,3],[0,2,4],[7,6,5]];//初始化八数码数组
   //获取输入的初始状态
   var cpic=1;
   for(var i=0;i<N;i++){
    for(var j=0;j<N;j++){
     var rid='r'+cpic++;
     var inputValue=getId(rid).value;
     if(inputValue==""){inputValue=0;}
     startArray[i][j]=parseInt(inputValue);
     getId(rid).value="";
    }
   }
var startGraph=new Graph(startArray);
var endArray=[[ 1,2,3],[ 8,0,4 ],[ 7,6,5 ]];
var endGraph=new Graph(endArray);//目标节点
evaluateGraph(startGraph,endGraph);
showGraph(startGraph);

核心算法实现

代码中定义了一个Graph类,用于保存八数码状态节点的相关数据,包括当前状态、评估值、父节点等信息。


//节点类
  var Graph = function(formData){
   this.form=formData;
   this.evalue=0;
   this.udirect=0;
   this.parent=null;
  };

实现一个showGraph()函数,用于在网页上可视化显示当前的八数码状态。


function showGraph(graph) {
   var c=1;
   for(var i=0;i<N;i++){
    for(var j=0;j<N;j++){
     var s='r'+c++;
     getId(s).style.backgroundImage="url(images/"+graph.form[i][j]+".png)";
    }
   }
  }

利用评估函数evaluateGraph()来计算当前节点与目标节点之间的差距值,作为A*算法中f(n)的h(n)部分。


//评估函数
  function evaluateGraph(theGraph, endGraph){
   var differ = 0;//差距数
   for (var i = 0; i<N; i++)
   {
    for (var j = 0; j<N; j++)
    {
     if (theGraph.form[i][j] != endGraph.form[i][j]){differ++;}
    }
   }
   theGraph.evalue = differ;
   return differ;
  }

通过moveGraph()函数实现空格的移动,并返回一个新的节点状态。


//移动数码组
  function moveGraph(theGraph, direct){
   var HasGetBlank = 0;//是否找到空格位置
   var AbleMove = 1;//是否可移动
   var i, j, t_i, t_j;
   //查找空格坐标i,j
   for (i = 0; i<N; i++)
   {
    for (j = 0; j<N; j++)
    {
     if (theGraph.form[i][j] == 0)
     {
      HasGetBlank = 1;
      break;
     }
    }
    if (HasGetBlank == 1)
     break;
   }
   t_i = i;
   t_j = j;
   //移动空格
   switch (direct)
   {
    case 1://上
     t_i--;
     if (t_i<0)
      AbleMove = 0;//移动超过边界
     break;
    case 2://下
     t_i++;
     if (t_i >= N)
      AbleMove = 0;
     break;
    case 3://左
     t_j--;
     if (t_j<0)
      AbleMove = 0;
     break;
    case 4://右
     t_j++;
     if (t_j >= N)
      AbleMove = 0;
     break;
   }
   //Direct方向不能移动,返回原节点
   if (AbleMove == 0)
   {
    return theGraph;
   }
   //向Direct方向移动,生成新节点
   var ta=[[0,0,0],[0,0,0],[0,0,0]];
   var New_graph = new Graph(ta);
   for (var x = 0; x<N; x++)//复制数码组
   {
    for (var y = 0; y<N; y++)
    {
     New_graph.form[x][y] = theGraph.form[x][y];
    }
   }
   //交换
   New_graph.form[i][j] = New_graph.form[t_i][t_j];//交换空格和移动方向上的数字
   New_graph.form[t_i][t_j] = 0;
   return New_graph;
  }

最后是核心的搜索函数Search()。该函数从初始节点开始,逐层向下进行搜索,直到找到目标节点。随后,通过子节点向上回溯父节点,从而构建出完整的解路径。


//搜索路径
  function Search(beginGraph, endGraph){
   var g1, g2, g;
   var Step = 0;//深度
   var Direct = 0;//方向
   var i;
   var front=-1,rear=-1;
   g1=beginGraph;//初始八数码节点
   while (g1)//队列不空,从close队列中拿出一个节点
   {
    for (i = 1; i <= 4; i++){//分别从四个方向推导出新子节点
     Direct = i;
     if (Direct == g1.udirect)
      continue;//跳过屏蔽方向
     g2=moveGraph(g1,Direct);
     if (evaluateGraph(g2,g1)!=0){//数码组是否可以移动
      evaluateGraph(g1,endGraph);
      evaluateGraph(g2,endGraph);//评价新的节点
      if (g2.evalue <= g1.evalue + 1)//利用评估值判断是否为优越节点
      { //若为优,将g2的父节点指向g1
       g2.parent = g1;
       //设置屏蔽方向,防止往回推
       switch (Direct){
        case 1://上
         g2.udirect = 2;
         break;
        case 2://下
         g2.udirect = 1;
         break;
        case 3://左
         g2.udirect = 4;
         break;
        case 4://右
         g2.udirect = 3;
         break;
       }
       Qu[++rear]=g2;//把优越节点放到close队列
       if (g2.evalue == 0)//为0则搜索完成
       {
        g = g2;
        break;
       }
      }
      else{g2 = null;}//抛弃劣质节点
     }
    }
    //搜索完成,继续退出
    if (typeof g !== 'undefined')
    {
     if (g.evalue == 0)
     {
      break;
     }
    }
    Step++;//统计深度
    if (Step>Max_Step){
     alert("超过搜索深度!");
     break;}
    g1=Qu[++front];//从close队列中拿出一个节点继续下一轮展开
   }
   return g;
  }

动画演示与总结

最后,将求得的解路径节点按顺序压入堆栈,并利用定时器每秒弹出一个节点进行显示,从而形成平滑的动画效果。


var top=-1;
   var G;
   G = Search(startGraph, endGraph);
   //解序列存入堆栈
   var P=G;
   while (P != null)
   {
    top++;
    St[top] = P;
    P = P.parent;
   }
   //动画执行
   var si=setInterval(function () {
    if (top>-1)
    {
     showGraph(St[top]);
     top--;
    }else {
     clearInterval(si);
    }
   },1000);
  }

以上介绍了利用JavaScript在网页实现八数码启发式A*算法及其动画效果的全过程。希望本文能对大家有所帮助,如有任何疑问,欢迎留言交流,小编会及时回复。感谢大家对本站的支持!

作者最新文章