A* search algorithm
By Jiajian Chan https://pigjian.com
by Chan Jiajian
HTML
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css">
<script src="//cdn.bootcss.com/vue/2.3.4/vue.min.js"></script>
<div id="app">
<h6>点击第一下空白方块设定起点(起点为红色方块) <div class="column red"></div>,点击第二个空白方块设置终点(再次点击重置起点)。</h6>
<h6>正确的路线起点为红边绿块 <div class="column red green"></div>,终点是蓝边绿块 <div class="column blue green"></div></h6>
<div class="container m-4">
<div class="row" v-for="items in elements">
<div class="column"
:class="matchClass(item)"
:id="item.x + '-' + item.y"
v-for="item in items" @click="setElement(item)"></div>
</div>
</div>
<button type="button" class="button" @click="setRandomObstacle()">随机障碍</button>
<button type="button" class="button" @click="clearObstacle()">清空障碍</button>
<button type="button" class="button" @click="setObstacleMode()">障碍模式</button>
<button type="button" class="button" @click="setStartAndEnd()">起始模式</button>
<h6>起始模式(默认):可设置起始点和终点 </h6>
<h6>障碍模式:点击开启后,可点击方块设置障碍物 </h6>
</div>
CSS
#app {
width: 767px;
margin: 50px auto;
}
.row {
height: 30px;
}
.column {
display: inline-block;
width: 30px;
height: 30px;
border: 1px solid #ECF0F1;
}
.black {
background-color: #2C3E50;
}
.red {
border: 1px solid #E74C3C;
background-color: #E74C3C;
}
.blue {
border: 1px solid #3498DB;
background-color: #3498DB;
}
.green {
background-color: #1ABC9C;
}
.button {
color: #1B9AF7;
background-color: #EEE;
border-color: #EEE;
font-weight: 300;
font-size: 14px;
font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
text-decoration: none;
text-align: center;
line-height: 20px;
height: 30px;
padding: 5px 20px;
margin: 10px;
display: inline-block;
appearance: none;
cursor: pointer;
border: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition-property: all;
transition-property: all;
-webkit-transition-duration: .3s;
transition-duration: .3s;
border-radius: 3px;
}
.button:hover {
color: #fff;
background-color: #1B9AF7;
border-color: #1B9AF7;
}
.button:focus {
outline: none;
}
JavaScript
new Vue({
el: '#app',
data: {
openList: [],
closeList: [],
lineLength: 10,
elements: [],
start: null,
end: null,
calculating: false,
range: {
x: 8,
y: 8
},
obstacles: [],
mode: 'start-end',
viablePath: [],
},
mounted() {
this.elements = this.createElements(this.range.y, this.range.x)
},
methods: {
// 计算 G 值
calculateG(element, length) {
if (element.previousElement == null) {
return length
} else {
return element.previousElement.g + length
}
},
// 计算 H 值
calculateH(element) {
return (Math.abs(element.x - this.end.x) + Math.abs(element.y - this.end.y)) * 10
},
// 计算 F 值
calculateF(element) {
return element.g + element.h
},
// 创建元素阵
createElements(row, column) {
let elements = []
for (let i = 0; i < row; i++) {
for (let j = 0; j < column; j++) {
elements[i] || (elements[i] = [])
let element = {
status: 1,
previousElement: null,
x: 0,
y: 0,
f: 0,
h: 0,
g: 0,
}
element.x = j
element.y = i
elements[i][j] = element
}
}
return elements
},
// 获取路径
getPath(element, target) {
element.previousElement = null
this.openList.push(element)
this.search(target)
},
// 搜索路径
search(target) {
let element = null
let find = false
let i = 0
while(this.openList.length > 0) {
// 获取 F 最小的元素
element = this.openList[0]
// 元素与目标元素相等,则退出循环
if (element.x == target.x && element.y == target.y) {
find = true
break;
}
// 上方元素
if ((element.y - 1) >= 0) {
this.checkElement(element.x, element.y - 1, element, this.lineLength)
}
// 下方元素
if ((element.y + 1) < this.range.y) {
...