The Code
# -*- coding: utf-8 -*-
# @Date : 2019-12-23 20:53:33
# @Author : Flying Hu ([email protected])
# @Link : http://www.flyinghu.cn
# @Name: A Star algorithm
# @Version : 0.1
import os
import math
from random import randint
import time
# Define global variables
gz_char = '█' # define default character grid
fruit_char = '★' # Fruit displaying characters defined
self_char = '●' # define the display character itself
wall_char = '◆' # define wall display character
# Full use of top-down direction as the positive x direction (two-dimensional list row index increased direction)
# Full use of the left to right direction as the positive y direction (two-dimensional list column index increased direction)
class Map2D(object):
'' '2D map class' ''
def __init__(self, width=20, height=20):
'''initialization
Args:
width wide map
High height map
'''
self.width = width
self.height = height
self.char = gz_char # default character map
# Generate a map
self.map = [[self.char for col in range(
self.width)] for row in range(self.height)]
# Generate a map surrounding wall
self.wall = [[i, j] for j in [-1, self.width] for i in range(self.height)] + [
[j, i] for j in [-1, self.height] for i in range(-1, self.width + 1)]
def __getitem__(self, item):
'' 'In a manner key value' ''
return self.map[item]
def __setitem__(self, key, value):
'' 'In a key way the value stored in' ''
self.map[key] = value
def show(self):
'' 'Print the map in the console' ''
for row in self.map:
for c in row:
# Using the console color control, distinguish
if c == self_char:
print('\033[1;35;44m' + c + '\033[0m', end='')
elif c == wall_char:
print('\033[0;36;41m' + c + '\033[0m', end='')
elif c == fruit_char:
print('\033[1;33;40m' + c + '\033[0m', end='')
else:
print('\033[0;37;41m' + c + '\033[0m', end='')
print()
# Reset map after reset will not leave trajectory
# self.reload()
def reload(self):
'' 'Reset map' ''
self.map = [[self.char for col in range(
self.width)] for row in range(self.height)]
class AStar(object):
'' 'A Star algorithm to achieve' ''
class Node(object):
'' 'Node Class' ''
def __init__(self, x, y, parent_node=None):
self.x = x
self.y = y
self.parent = parent_node # parent
# F = G + H
# G = the cost of moving to the start point A moves from the specified checkered
# H = estimated cost from the cell to a specified end point B is. Heuristics. As used herein, Manhattan method for estimating H
self.G = 0
self.H = 0
def __init__(self, map2D):
'''initialization'''
self.map = map2D
def MinF(self):
'' 'Taken from the lowest node F in open_list
Returns:
minF Returns the minimum value of the node F in open_list
'''
# Assume a first minimum, then the minimum cycle selected node F
minF = self.open_list[0]
for node in self.open_list:
if (node.G + node.H) <= (minF.G + minF.H):
minF = node
return minF
def in_close_list(self, x, y):
'' 'Determines the coordinates are close_list
Args:
xx coordinates
yy coordinates
Return:
If the node coordinates close_list, returns the node, otherwise it returns False
'''
for node in self.close_list:
if node.x == x and node.y == y:
return node
return False
def in_open_list(self, x, y):
'' 'Determines the coordinates are open_list
Args:
xx coordinates
yy coordinates
Return:
If the node coordinates open_list, returns the node, otherwise it returns False
'''
for node in self.close_list:
if node.x == x and node.y == y:
return node
return False
def search(self, node):
'' 'Around the path search
Args:
node node search
'''
# Determines whether the changed node obstacle (obstacle or wall)
if [node.x, node.y] in self.obstacles:
# If an obstacle is ignored path
return
# Determine whether the close_list
if self.in_close_list(node.x, node.y):
# If you are already close_list in the node is ignored
return
G and H # Calculated
node.G = node.parent.G + 10
node.H = abs(self.target_node.x - node.x) + \
abs(self.target_node.y - node.y) * 10
# Determine whether the open_list
tmp = self.in_close_list(node.x, node.y)
if tmp:
# In the open_list
# Compare the current F and F in open_list
if (node.G + node.H) < (tmp.G + tmp.H):
If it is judged that the same coordinate # path F value is present in a ratio of open_list smaller value of F
tmp = node
else:
# Open_list not added to the open_list
self.open_list.append(node)
def start(self, current_position, target_positiion, obstacles):
'' 'A star computing optimal path
Args:
current_position current position coordinates
target_positiion target location coordinates
obstacles obstacle coordinate list
Returns:
path_list If the optimal path exists A star returns to the optimal path, anyway, return None
'''
# Target node
self.target_node = AStar.Node(*target_positiion)
The current node #
self.current_node = AStar.Node(*current_position)
# Open table, add the current node to the open_list
self.open_list = [self.current_node]
Close the table #
self.close_list = []
# Obstructions set of coordinates (real obstacle + wall)
self.obstacles = obstacles + self.map.wall
# A star turn on the calculation cycle
while True:
# Determines whether or reaches the end, it is determined whether the target node coordinates in the close_list
tmp = self.in_close_list(self.target_node.x, self.target_node.y)
if tmp:
# Return route
path_list = [[tmp.x, tmp.y]]
Backward Route #
while tmp.parent:
tmp = tmp.parent
path_list.append([tmp.x, tmp.y])
# Reverse list
path_list.reverse()
return path_list
if not self.open_list:
# If open_list is blank, no way out
return None
F # is selected from the shortest path node in open_list
minF = self.MinF()
# Add the currently selected node to close_list in, and removed from the open_list
self.close_list.append(minF)
self.open_list.remove(minF)
# Search path and a search of the current node as a parent node according to a fixed sequence (fixed line, in any order)
self.search(AStar.Node(minF.x - 1, minF.y, minF))
self.search(AStar.Node(minF.x, minF.y - 1, minF))
self.search(AStar.Node(minF.x + 1, minF.y, minF))
self.search(AStar.Node(minF.x, minF.y + 1, minF))
# When there is a long calculation relatively long empty, print message
print ( '\ r Diao hair, is planning path ...', end = '')
def sub_start(self, current_position, target_positiion, obstacles, num=20):
'' 'Defines a frequency and determining whether there is a road to go' ''
# Target node
self.target_node = AStar.Node(*target_positiion)
The current node #
self.current_node = AStar.Node(*current_position)
# Open table, add the current node to the open_list
self.open_list = [self.current_node]
Close the table #
self.close_list = []
# Obstructions set of coordinates (real obstacle + wall)
self.obstacles = obstacles + self.map.wall
# A star turn on the calculation cycle
for i in range(num):
# Determines whether or reaches the end, it is determined whether the target node coordinates in the close_list
tmp = self.in_close_list(self.target_node.x, self.target_node.y)
if tmp:
# Return route
path_list = [[tmp.x, tmp.y]]
Backward Route #
while tmp.parent:
tmp = tmp.parent
path_list.append([tmp.x, tmp.y])
# Reverse list
path_list.reverse()
return True
if not self.open_list:
# If open_list is blank, no way out
return False
F # is selected from the shortest path node in open_list
minF = self.MinF()
# Add the currently selected node to close_list in, and removed from the open_list
self.close_list.append(minF)
self.open_list.remove(minF)
# Search path and a search of the current node as a parent node according to a fixed sequence (fixed line, in any order)
self.search(AStar.Node(minF.x - 1, minF.y, minF))
self.search(AStar.Node(minF.x, minF.y - 1, minF))
self.search(AStar.Node(minF.x + 1, minF.y, minF))
self.search(AStar.Node(minF.x, minF.y + 1, minF))
else:
return True
class Game(object):
'' 'Game class' ''
def __init__(self, map2D, obs_num=None):
'''initialization
Args:
map2D 2D map
obs_num initialize the number of obstacles
'''
self.map = map2D
self.height = self.map.height
self.width = self.map.width
# Initialize a random direction of movement
self.direction = randint (0, 3)
# Calculate the number of obstacles according to the size of the map
# self.obs_num = int(math.sqrt(self.height * self.width))
self.obs_num obs_num else if obs_num = int (
math.sqrt(self.height * self.width))
# Initialize the starting point
self.current = [
randint (int (1/4 * (self.height - 1)),
int(3/4 * (self.height - 1))),
randint(int(1/4 * (self.width - 1)),
int(3/4 * (self.width - 1)))
] Zhengzhou gynecological hospital http://www.hnzzkd.com/
# Fruit generation target
self.gen_fruit()
# Generate obstacles
self.gen_obs()
def gen_fruit(self):
'' 'Generates fruit' ''
while True:
Fruit # randomly generated coordinate
self.fruit = [randint (0, self.height - 1),
randint(0, self.width - 1)]
# Avoid fruit and starting coordinate coincides
if self.fruit != self.current:
break
def gen_obs(self):
'' 'Generates an obstacle' ''
self.obs_list = []
for i in range(self.obs_num):
while True:
tmp = [randint(0, self.height - 1), randint(0, self.width - 1)]
# Avoid obstacles and coincides with the start or fruit
if tmp != self.current and tmp != self.fruit:
self.obs_list.append(tmp)
break
def move(self):
'' 'Is moved according to the direction of movement
0, 1, 2, 3 respectively on the left, bottom, right
'''
if self.direction == 0:
self.current = [self.current[0] - 1, self.current[1]]
elif self.direction == 1:
self.current = [self.current[0], self.current[1] - 1]
elif self.direction == 2:
self.current = [self.current[0] + 1, self.current[1]]
else:
self.current = [self.current[0], self.current[1] + 1]
def cls(self):
'' 'Empty console output' ''
os.system ( 'cis')
def load(self):
'' 'Load of fruit and obstacles' ''
# Load barrier
for row, col in self.obs_list:
self.map[row][col] = wall_char
# Fruit and load current point
row, col = self.current
self.map[row][col] = self_char
row, col = self.fruit
self.map[row][col] = fruit_char
def start(self):
'' 'Start cycle' ''
# A star turn
g = self.a_star()
# Into circulation
while True:
# Clear the console output
self.cls()
# Load
self.load()
# Print display
self.map.show()
# Determine whether to eat fruit
if self.current == self.fruit:
# Eat fruits
# Reset map
self.map.reload()
# Rebuild fruit, rebuild obstacles
self.gen_fruit()
self.gen_obs()
self.map.reload()
if next(g) is False:
# Indicates nowhere
# Reset map
self.map.reload()
continue
# Mobile
self.move()
# Movement speed control
time.sleep(0.3)
def a_star(self):
'' 'A * algorithm access pathfinding
Use python generator manner in the cycle time and again change direction
'''
# Create Object
a = AStar (self.map)
while True:
# Advance to load the map display, you can manually determine in advance whether the display is really nowhere to go
# Clear the console output
self.cls()
# Load
self.load()
# Print display
self.map.show()
# Reverse first determines whether the number of defined results in no way added to change the policy to some extent reduce the calculation time is long, the forward calculation
if a.sub_start(self.fruit, self.current, self.obs_list, 30) is False:
# Indicates nowhere
input ( 'no way, refresh the map Enter')
self.map.reload()
self.gen_fruit()
self.gen_obs()
# Returned no way signals
yield False
continue
path_list = a.start(self.current, self.fruit, self.obs_list)
if not path_list:
# Indicates nowhere
# Advance to load the map display, you can manually determine in advance whether the display is really nowhere to go
# Clear the console output
# self.cls()
# # Load
# self.load()
# # Print display
# self.map.show()
input ( 'no way, refresh the map Enter')
self.map.reload()
self.gen_fruit()
self.gen_obs()
# Returned no way signals
yield False
continue
After starting the traversal path # Comparative results running direction
for path in path_list[1:]:
if path[0] > self.current[0]:
self.direction = 2
elif path[0] < self.current[0]:
self.direction = 0
elif path[1] > self.current[1]:
self.direction = 3
else:
self.direction = 1
yield
if __name__ == "__main__":
# Initialize console size
os.system("mode con cols=80 lines=80")
# Create a map
map2D = Map2D(width=20, height=20)
# New, designated obstacle
game = Game(map2D, 150)
# turn on
game.start()