利用JavaScript在網(wǎng)頁(yè)實(shí)現(xiàn)八數(shù)碼啟發(fā)式A*算法動(dòng)畫(huà)效果
最近人工智能課老師布置了一個(gè)八數(shù)碼實(shí)驗(yàn),網(wǎng)上看到很多八數(shù)碼的啟發(fā)式A*算法,但是大多數(shù)都是利用C或者C++在控制臺(tái)實(shí)現(xiàn)的,于是我用js在網(wǎng)頁(yè)中做了一個(gè)類似的。
首先八數(shù)碼就是一個(gè)九宮格,其中有一個(gè)空格,其他八個(gè)對(duì)應(yīng)數(shù)字1-8,

移動(dòng)空格,使得最后狀態(tài)為有序,如下圖

啟發(fā)式算法是指在求解時(shí),利用啟發(fā)函數(shù)將不符合規(guī)則的解節(jié)點(diǎn)去掉,從而縮小問(wèn)題的解空間。
A*算法是利用評(píng)價(jià)函數(shù)的啟發(fā)式算法,在本例中,利用當(dāng)前節(jié)點(diǎn)狀態(tài)與最終節(jié)點(diǎn)狀態(tài)所不同的格子數(shù)來(lái)評(píng)估節(jié)點(diǎn)的優(yōu)劣,將優(yōu)越節(jié)點(diǎn)儲(chǔ)存并在之后展開(kāi),將劣質(zhì)節(jié)點(diǎn)拋棄。
利用web實(shí)現(xiàn)這一點(diǎn)首先在html中添加九個(gè)如圖所示input文本框,背景圖片為數(shù)碼格

頁(yè)面代碼為
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>八數(shù)碼</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獲取輸入的值,并保存在二維數(shù)組中
var startArray=[[8,1,3],[0,2,4],[7,6,5]];//初始化八數(shù)碼數(shù)組
//獲取輸入的初始狀態(tài)
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);//目標(biāo)節(jié)點(diǎn)
evaluateGraph(startGraph,endGraph);
showGraph(startGraph);
其中Graph類是用來(lái)來(lái)保存一個(gè)狀態(tài)節(jié)點(diǎn)相關(guān)數(shù)據(jù):
//節(jié)點(diǎn)類
var Graph = function(formData){
this.form=formData;
this.evalue=0;
this.udirect=0;
this.parent=null;
};
實(shí)現(xiàn)一個(gè)showGraph()函數(shù)來(lái)顯示八數(shù)碼狀態(tài):
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)";
}
}
}
利用評(píng)估函數(shù)evaluateGraph()評(píng)估當(dāng)前節(jié)點(diǎn)與目標(biāo)節(jié)點(diǎn)的差距值
//評(píng)估函數(shù)
function evaluateGraph(theGraph, endGraph){
var differ = 0;//差距數(shù)
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()函數(shù)來(lái)移動(dòng)并返回一個(gè)新節(jié)點(diǎn):
//移動(dòng)數(shù)碼組
function moveGraph(theGraph, direct){
var HasGetBlank = 0;//是否找到空格位置
var AbleMove = 1;//是否可移動(dòng)
var i, j, t_i, t_j;
//查找空格坐標(biāo)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;
//移動(dòng)空格
switch (direct)
{
case 1://上
t_i--;
if (t_i<0)
AbleMove = 0;//移動(dòng)超過(guò)邊界
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方向不能移動(dòng),返回原節(jié)點(diǎn)
if (AbleMove == 0)
{
return theGraph;
}
//向Direct方向移動(dòng),生成新節(jié)點(diǎn)
var ta=[[0,0,0],[0,0,0],[0,0,0]];
var New_graph = new Graph(ta);
for (var x = 0; x<N; x++)//復(fù)制數(shù)碼組
{
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];//交換空格和移動(dòng)方向上的數(shù)字
New_graph.form[t_i][t_j] = 0;
return New_graph;
}
最后是搜索函數(shù),通過(guò)從初始節(jié)點(diǎn)開(kāi)始一層層向下搜索,直到抵達(dá)目標(biāo)節(jié)點(diǎn),返回子節(jié)點(diǎn),從子節(jié)點(diǎn)一層層向上回溯父節(jié)點(diǎn),便可找到解路徑:
//搜索路徑
function Search(beginGraph, endGraph){
var g1, g2, g;
var Step = 0;//深度
var Direct = 0;//方向
var i;
var front=-1,rear=-1;
g1=beginGraph;//初始八數(shù)碼節(jié)點(diǎn)
while (g1)//隊(duì)列不空,從close隊(duì)列中拿出一個(gè)節(jié)點(diǎn)
{
for (i = 1; i <= 4; i++){//分別從四個(gè)方向推導(dǎo)出新子節(jié)點(diǎn)
Direct = i;
if (Direct == g1.udirect)
continue;//跳過(guò)屏蔽方向
g2=moveGraph(g1,Direct);
if (evaluateGraph(g2,g1)!=0){//數(shù)碼組是否可以移動(dòng)
evaluateGraph(g1,endGraph);
evaluateGraph(g2,endGraph);//評(píng)價(jià)新的節(jié)點(diǎn)
if (g2.evalue <= g1.evalue + 1)//利用評(píng)估值判斷是否為優(yōu)越節(jié)點(diǎn)
{ //若為優(yōu),將g2的父節(jié)點(diǎn)指向g1
g2.parent = g1;
//設(shè)置屏蔽方向,防止往回推
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;//把優(yōu)越節(jié)點(diǎn)放到close隊(duì)列
if (g2.evalue == 0)//為0則搜索完成
{
g = g2;
break;
}
}
else{g2 = null;}//拋棄劣質(zhì)節(jié)點(diǎn)
}
}
//搜索完成,繼續(xù)退出
if (typeof g !== 'undefined')
{
if (g.evalue == 0)
{
break;
}
}
Step++;//統(tǒng)計(jì)深度
if (Step>Max_Step){
alert("超過(guò)搜索深度!");
break;}
g1=Qu[++front];//從close隊(duì)列中拿出一個(gè)節(jié)點(diǎn)繼續(xù)下一輪展開(kāi)
}
return g;
}
最后將解路徑節(jié)點(diǎn)按順序壓入堆棧,每秒彈出一個(gè)節(jié)點(diǎn),顯示,形成動(dòng)畫(huà):
var top=-1;
var G;
G = Search(startGraph, endGraph);
//解序列存入堆棧
var P=G;
while (P != null)
{
top++;
St[top] = P;
P = P.parent;
}
//動(dòng)畫(huà)執(zhí)行
var si=setInterval(function () {
if (top>-1)
{
showGraph(St[top]);
top--;
}else {
clearInterval(si);
}
},1000);
}
以上所述是小編給大家介紹的利用JavaScript在網(wǎng)頁(yè)實(shí)現(xiàn)八數(shù)碼啟發(fā)式A*算法動(dòng)畫(huà)效果,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的,在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- PHP 雙鏈表(SplDoublyLinkedList)簡(jiǎn)介和使用實(shí)例
- 10分鐘教你用python動(dòng)畫(huà)演示深度優(yōu)先算法搜尋逃出迷宮的路徑
- tween.js緩動(dòng)補(bǔ)間動(dòng)畫(huà)算法示例
- JavaScript排序算法動(dòng)畫(huà)演示效果的實(shí)現(xiàn)方法
- javascript動(dòng)畫(huà)算法實(shí)例分析
- 用隊(duì)列模擬jquery的動(dòng)畫(huà)算法實(shí)例
- 看動(dòng)畫(huà)學(xué)算法之Java實(shí)現(xiàn)doublyLinkedList
相關(guān)文章
Javascript連接Access數(shù)據(jù)庫(kù)完整實(shí)例
這篇文章主要介紹了Javascript連接Access數(shù)據(jù)庫(kù)的方法,涉及javascript針對(duì)access數(shù)據(jù)庫(kù)的連接、關(guān)閉及增刪改查等常用操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08
JS中map與forEach無(wú)法跳出循環(huán)及every和some的使用
在我們平時(shí)使用習(xí)慣中,for循環(huán)里要跳出整個(gè)循環(huán)是使用break,但在數(shù)組中用forEach循環(huán)或者map如要退出整個(gè)循環(huán)使用break會(huì)報(bào)錯(cuò),使用return也不能跳出循環(huán),下面這篇文章主要介紹了關(guān)于JS中map與forEach無(wú)法跳出循環(huán)及every和some的使用的相關(guān)資料,需要的朋友可以參考下2023-05-05
JavaScript實(shí)現(xiàn)谷歌瀏覽器插件開(kāi)發(fā)的方法詳解
對(duì)于瀏覽器插件相信大家都不陌生,誰(shuí)的瀏覽器不裝幾個(gè)好用的插件呢,更是有油猴這個(gè)強(qiáng)大的神器。所以本文就來(lái)用JavaScript開(kāi)發(fā)一個(gè)谷歌瀏覽器插件,感興趣的小伙伴可以了解一下2022-11-11
JavaScript中判斷變量是數(shù)組、函數(shù)或是對(duì)象類型的方法
這篇文章主要介紹了JavaScript中判斷變量是數(shù)組、函數(shù)或是對(duì)象類型的方法,需要的朋友可以參考下2015-02-02
JavaScript實(shí)現(xiàn)的石頭剪刀布游戲源碼分享
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的石頭剪刀布游戲源碼分享,挺好玩的小游戲,關(guān)鍵在一些算法上,需要的朋友可以參考下2014-08-08
js利用for in循環(huán)獲取 一個(gè)對(duì)象的所有屬性以及值的實(shí)例
下面小編就為大家?guī)?lái)一篇js利用for in循環(huán)獲取 一個(gè)對(duì)象的所有屬性以及值的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03

