A* implementation
This commit is contained in:
parent
5a40f4eb81
commit
976a6ebfc2
120
Astar.py
Normal file
120
Astar.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
class Node:
|
||||||
|
"""A node class for A* Pathfinding"""
|
||||||
|
|
||||||
|
def __init__(self, parent=None, position=None):
|
||||||
|
self.parent = parent
|
||||||
|
self.position = position
|
||||||
|
|
||||||
|
self.g = 0
|
||||||
|
self.h = 0
|
||||||
|
self.f = 0
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.position == other.position
|
||||||
|
|
||||||
|
|
||||||
|
def astar(maze, start, end):
|
||||||
|
"""Returns a list of tuples as a path from the given start to the given end in the given maze"""
|
||||||
|
|
||||||
|
# Create start and end node
|
||||||
|
start_node = Node(None, start)
|
||||||
|
start_node.g = start_node.h = start_node.f = 0
|
||||||
|
end_node = Node(None, end)
|
||||||
|
end_node.g = end_node.h = end_node.f = 0
|
||||||
|
|
||||||
|
# Initialize both open and closed list
|
||||||
|
open_list = []
|
||||||
|
closed_list = []
|
||||||
|
|
||||||
|
# Add the start node
|
||||||
|
open_list.append(start_node)
|
||||||
|
|
||||||
|
# Loop until you find the end
|
||||||
|
while len(open_list) > 0:
|
||||||
|
|
||||||
|
# Get the current node
|
||||||
|
current_node = open_list[0]
|
||||||
|
current_index = 0
|
||||||
|
for index, item in enumerate(open_list):
|
||||||
|
if item.f < current_node.f:
|
||||||
|
current_node = item
|
||||||
|
current_index = index
|
||||||
|
|
||||||
|
# Pop current off open list, add to closed list
|
||||||
|
open_list.pop(current_index)
|
||||||
|
closed_list.append(current_node)
|
||||||
|
|
||||||
|
# Found the goal
|
||||||
|
if current_node == end_node:
|
||||||
|
path = []
|
||||||
|
current = current_node
|
||||||
|
while current is not None:
|
||||||
|
path.append(current.position)
|
||||||
|
current = current.parent
|
||||||
|
return path[::-1] # Return reversed path
|
||||||
|
|
||||||
|
# Generate children
|
||||||
|
children = []
|
||||||
|
for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0)]: # Adjacent squares
|
||||||
|
|
||||||
|
# Get node position
|
||||||
|
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])
|
||||||
|
|
||||||
|
# Make sure within range
|
||||||
|
if node_position[0] > (len(maze) - 1) or node_position[0] < 0 or node_position[1] > (len(maze[len(maze)-1]) -1) or node_position[1] < 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Make sure walkable terrain
|
||||||
|
if maze[node_position[0]][node_position[1]] != 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Create new node
|
||||||
|
new_node = Node(current_node, node_position)
|
||||||
|
|
||||||
|
# Append
|
||||||
|
children.append(new_node)
|
||||||
|
|
||||||
|
# Loop through children
|
||||||
|
for child in children:
|
||||||
|
|
||||||
|
# Child is on the closed list
|
||||||
|
for closed_child in closed_list:
|
||||||
|
if child == closed_child:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Create the f, g, and h values
|
||||||
|
child.g = current_node.g + 1
|
||||||
|
child.h = ((child.position[0] - end_node.position[0]) ** 2) + ((child.position[1] - end_node.position[1]) ** 2)
|
||||||
|
child.f = child.g + child.h
|
||||||
|
|
||||||
|
# Child is already in the open list
|
||||||
|
for open_node in open_list:
|
||||||
|
if child == open_node and child.g > open_node.g:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add the child to the open list
|
||||||
|
open_list.append(child)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
maze = [[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
|
||||||
|
|
||||||
|
start = (0, 0)
|
||||||
|
end = (7, 6)
|
||||||
|
|
||||||
|
path = astar(maze, start, end)
|
||||||
|
print(path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
43
agent.py
Normal file
43
agent.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import pygame as pg
|
||||||
|
import sys
|
||||||
|
import numpy as np
|
||||||
|
from tile import Tile
|
||||||
|
from config import *
|
||||||
|
|
||||||
|
|
||||||
|
class Agent:
|
||||||
|
def __init__(self):
|
||||||
|
self.prev = (4, 4)
|
||||||
|
self.pos = (4, 4)
|
||||||
|
self.dir = "E"
|
||||||
|
|
||||||
|
def turn(self, a, b):
|
||||||
|
x_mov = a[0] - b[0]
|
||||||
|
y_mov = a[1] - b[1]
|
||||||
|
if (x_mov, y_mov) == (0, 1):
|
||||||
|
self.dir = "S"
|
||||||
|
if (x_mov, y_mov) == (0, -1):
|
||||||
|
self.dir = "N"
|
||||||
|
if (x_mov, y_mov) == (1, 0):
|
||||||
|
self.dir = "E"
|
||||||
|
if (x_mov, y_mov) == (-1, 0):
|
||||||
|
self.dir = "W"
|
||||||
|
|
||||||
|
def display(self, step=None):
|
||||||
|
pg.time.delay(150)
|
||||||
|
agent_img = pg.image.load('tractor.png').convert_alpha()
|
||||||
|
if step is not None:
|
||||||
|
self.prev = self.pos
|
||||||
|
self.pos = step
|
||||||
|
self.turn(self.pos, self.prev)
|
||||||
|
if self.dir == "N":
|
||||||
|
angle = 270
|
||||||
|
elif self.dir == "E":
|
||||||
|
angle = 180
|
||||||
|
elif self.dir == "S":
|
||||||
|
angle = 90
|
||||||
|
elif self.dir == "W":
|
||||||
|
angle = 0
|
||||||
|
agent_img = pg.transform.rotate(agent_img, angle)
|
||||||
|
screen.blit(agent_img, (self.pos[0] * tile_size, self.pos[1] * tile_size))
|
||||||
|
pg.display.flip()
|
BIN
barn.png
BIN
barn.png
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.4 KiB |
60
config.py
60
config.py
@ -1,38 +1,43 @@
|
|||||||
import pygame as pg
|
import pygame as pg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
screen_height = 560
|
screen_height = 890
|
||||||
screen_width = 700
|
screen_width = 720
|
||||||
tile_size = 80
|
tile_size = 80
|
||||||
|
|
||||||
screen = pg.display.set_mode([screen_width, screen_height])
|
screen = pg.display.set_mode([screen_width, screen_height])
|
||||||
|
black_surface = pg.Surface((720, 170))
|
||||||
|
black_surface.fill((0, 0, 0))
|
||||||
|
|
||||||
# agent
|
# # agent
|
||||||
agent_img = pg.image.load('tractor.png').convert()
|
# agent_img = pg.image.load('tractor.png').convert_alpha()
|
||||||
|
|
||||||
# background
|
# background
|
||||||
road = 0
|
road = 0
|
||||||
ground = 1
|
ground = 1
|
||||||
|
wall = 2
|
||||||
|
|
||||||
bg_textures = {
|
bg_textures = {
|
||||||
road: pg.image.load('road.png').convert(),
|
road: pg.image.load('road.png').convert(),
|
||||||
ground: pg.image.load('ground.png').convert()}
|
ground: pg.image.load('ground.png').convert(),
|
||||||
|
wall: pg.image.load('wall.png').convert()}
|
||||||
|
|
||||||
background = np.array([[1, 1, 1, 0, 1, 1, 1],
|
background = np.array([[1, 1, 1, 0, 0, 0, 1, 1, 1],
|
||||||
[1, 1, 1, 0, 1, 1, 1],
|
[1, 1, 1, 2, 0, 2, 1, 1, 1],
|
||||||
[1, 1, 1, 0, 1, 1, 1],
|
[1, 1, 1, 2, 0, 2, 1, 1, 1],
|
||||||
[0, 0, 0, 0, 0, 0, 0],
|
[0, 2, 2, 2, 0, 2, 2, 2, 0],
|
||||||
[1, 1, 1, 0, 1, 1, 1],
|
[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
[1, 1, 1, 0, 1, 1, 1],
|
[0, 2, 2, 2, 0, 2, 2, 2, 0],
|
||||||
[1, 1, 1, 0, 1, 1, 1]])
|
[1, 1, 1, 2, 0, 2, 1, 1, 1],
|
||||||
|
[1, 1, 1, 2, 0, 2, 1, 1, 1],
|
||||||
|
[1, 1, 1, 0, 0, 0, 1, 1, 1]])
|
||||||
|
|
||||||
# objects
|
# objects
|
||||||
rose = 2
|
rose = 3
|
||||||
tulip = 3
|
tulip = 4
|
||||||
orchid = 4
|
orchid = 5
|
||||||
sunflower = 5
|
sunflower = 6
|
||||||
barn = 6
|
barn = 7
|
||||||
disease = 7
|
|
||||||
|
|
||||||
ob_textures = {
|
ob_textures = {
|
||||||
0: pg.image.load('none.png').convert_alpha(),
|
0: pg.image.load('none.png').convert_alpha(),
|
||||||
@ -40,13 +45,14 @@ ob_textures = {
|
|||||||
tulip: pg.image.load('tulip.png').convert_alpha(),
|
tulip: pg.image.load('tulip.png').convert_alpha(),
|
||||||
orchid: pg.image.load('orchid.png').convert_alpha(),
|
orchid: pg.image.load('orchid.png').convert_alpha(),
|
||||||
sunflower: pg.image.load('sunflower.png').convert_alpha(),
|
sunflower: pg.image.load('sunflower.png').convert_alpha(),
|
||||||
barn: pg.image.load('barn.png').convert(),
|
barn: pg.image.load('barn.png').convert()}
|
||||||
disease: pg.image.load('disease.png').convert_alpha()}
|
|
||||||
|
|
||||||
objects = np.array([[2, 2, 2, 0, 3, 3, 3],
|
objects = np.array([[3, 3, 3, 0, 0, 0, 4, 4, 4],
|
||||||
[2, 2, 2, 0, 3, 3, 3],
|
[3, 3, 3, 0, 0, 0, 4, 4, 4],
|
||||||
[2, 2, 2, 0, 3, 3, 3],
|
[3, 3, 3, 0, 0, 0, 4, 4, 4],
|
||||||
[0, 0, 0, 6, 0, 0, 0],
|
[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
[4, 4, 4, 0, 5, 5, 5],
|
[0, 0, 0, 0, 7, 0, 0, 0, 0],
|
||||||
[4, 4, 4, 0, 5, 5, 5],
|
[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
[4, 4, 4, 0, 5, 5, 5]])
|
[5, 5, 5, 0, 0, 0, 6, 6, 6],
|
||||||
|
[5, 5, 5, 0, 0, 0, 6, 6, 6],
|
||||||
|
[5, 5, 5, 0, 0, 0, 6, 6, 6]])
|
||||||
|
124
main.py
124
main.py
@ -3,36 +3,128 @@ import sys
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from tile import Tile
|
from tile import Tile
|
||||||
from config import *
|
from config import *
|
||||||
|
from agent import Agent
|
||||||
|
from Astar import *
|
||||||
|
|
||||||
done = False
|
done = False
|
||||||
|
|
||||||
clock = pg.time.Clock()
|
clock = pg.time.Clock()
|
||||||
pg.display.set_caption('Intelligent Tractor')
|
pg.display.set_caption('Intelligent Tractor')
|
||||||
tiles = []
|
pg.init()
|
||||||
|
pg.font.init()
|
||||||
for y in range(0, 7):
|
|
||||||
for x in range(0, 7):
|
|
||||||
t = Tile(background[x][y], objects[x][y], x, y)
|
|
||||||
tiles.append(t)
|
|
||||||
|
|
||||||
|
|
||||||
def display_background():
|
class Game:
|
||||||
for tile in tiles:
|
def __init__(self):
|
||||||
screen.blit(bg_textures[tile.ground], (tile.x * tile_size, tile.y * tile_size))
|
self.tiles = []
|
||||||
|
self.water_lvl = []
|
||||||
|
self.day = 1
|
||||||
|
|
||||||
|
for y in range(0, 9):
|
||||||
|
for x in range(0, 9):
|
||||||
|
pos = (x, y)
|
||||||
|
t = Tile(background[x][y], objects[x][y], pos)
|
||||||
|
t.water_usage()
|
||||||
|
self.tiles.append(t)
|
||||||
|
|
||||||
|
def display_background(self):
|
||||||
|
for tile in self.tiles:
|
||||||
|
screen.blit(bg_textures[tile.ground], (tile.x * tile_size, tile.y * tile_size))
|
||||||
|
|
||||||
|
def display_objects(self):
|
||||||
|
for tile in self.tiles:
|
||||||
|
screen.blit(ob_textures[tile.object], (tile.x * tile_size, tile.y * tile_size))
|
||||||
|
|
||||||
|
def display_legend(self):
|
||||||
|
screen.blit(black_surface, (0, 720))
|
||||||
|
self.water_lvl = []
|
||||||
|
for tile in self.tiles:
|
||||||
|
if tile.pos == (1, 1):
|
||||||
|
rose_w = tile.w
|
||||||
|
self.water_lvl.append((rose_w, (0, 3)))
|
||||||
|
if tile.pos == (7, 1):
|
||||||
|
orchid_w = tile.w
|
||||||
|
self.water_lvl.append((orchid_w, (5, 0)))
|
||||||
|
if tile.pos == (1, 7):
|
||||||
|
tulip_w = tile.w
|
||||||
|
self.water_lvl.append((tulip_w, (3, 8)))
|
||||||
|
if tile.pos == (7, 7):
|
||||||
|
sunflower_w = tile.w
|
||||||
|
self.water_lvl.append((sunflower_w, (8, 5)))
|
||||||
|
font = pg.font.SysFont('ubuntumono', 40)
|
||||||
|
text = font.render("water levels", 1, (255, 255, 255))
|
||||||
|
text1 = font.render("roses: " + str(rose_w) + "%", 1, (255, 255, 255))
|
||||||
|
text2 = font.render("orchids: " + str(orchid_w) + "%", 1, (255, 255, 255))
|
||||||
|
text3 = font.render("tulips: " + str(tulip_w) + "%", 1, (255, 255, 255))
|
||||||
|
text4 = font.render("sunflowers: " + str(sunflower_w) + "%", 1, (255, 255, 255))
|
||||||
|
text5 = font.render("day: " + str(self.day), 1, (255, 255, 255))
|
||||||
|
screen.blit(text, (10, 720))
|
||||||
|
screen.blit(text1, (10, 760))
|
||||||
|
screen.blit(text2, (360, 760))
|
||||||
|
screen.blit(text3, (10, 800))
|
||||||
|
screen.blit(text4, (360, 800))
|
||||||
|
screen.blit(text5, (10, 840))
|
||||||
|
|
||||||
|
def water_target(self):
|
||||||
|
o = None
|
||||||
|
for lvl in self.water_lvl:
|
||||||
|
if lvl[0] < 10:
|
||||||
|
o = lvl[1]
|
||||||
|
break
|
||||||
|
return o
|
||||||
|
|
||||||
|
def watering(self, plants):
|
||||||
|
if plants == (0, 3):
|
||||||
|
for tile in self.tiles:
|
||||||
|
if tile.object == rose:
|
||||||
|
tile.w = 100
|
||||||
|
if plants == (5, 0):
|
||||||
|
for tile in self.tiles:
|
||||||
|
if tile.object == orchid:
|
||||||
|
tile.w = 100
|
||||||
|
if plants == (3, 8):
|
||||||
|
for tile in self.tiles:
|
||||||
|
if tile.object == tulip:
|
||||||
|
tile.w = 100
|
||||||
|
if plants == (8, 5):
|
||||||
|
for tile in self.tiles:
|
||||||
|
if tile.object == sunflower:
|
||||||
|
tile.w = 100
|
||||||
|
|
||||||
|
def add_day(self):
|
||||||
|
pg.time.delay(1000)
|
||||||
|
self.day = self.day + 1
|
||||||
|
for tile in self.tiles:
|
||||||
|
tile.w = tile.w - tile.wu
|
||||||
|
if tile.w < 0:
|
||||||
|
tile.w = 0
|
||||||
|
|
||||||
|
|
||||||
def display_objects():
|
def game_loop():
|
||||||
for tile in tiles:
|
path = None
|
||||||
screen.blit(ob_textures[tile.object], (tile.x * tile_size, tile.y * tile_size))
|
g.display_background()
|
||||||
pg.display.flip()
|
g.display_objects()
|
||||||
|
g.display_legend()
|
||||||
|
if g.water_target() is None:
|
||||||
|
g.add_day()
|
||||||
|
else:
|
||||||
|
path = astar(background, a.pos, g.water_target())
|
||||||
|
if path is not None:
|
||||||
|
if len(path) == 1:
|
||||||
|
g.watering(path[0])
|
||||||
|
path = None
|
||||||
|
else:
|
||||||
|
a.display(path[1])
|
||||||
|
a.display()
|
||||||
|
clock.tick(60)
|
||||||
|
|
||||||
|
|
||||||
|
g = Game()
|
||||||
|
a = Agent()
|
||||||
while not done:
|
while not done:
|
||||||
for event in pg.event.get():
|
for event in pg.event.get():
|
||||||
if event.type == pg.QUIT:
|
if event.type == pg.QUIT:
|
||||||
done = True
|
done = True
|
||||||
display_background()
|
game_loop()
|
||||||
display_objects()
|
|
||||||
clock.tick(60)
|
|
||||||
|
|
||||||
pg.quit()
|
pg.quit()
|
BIN
road.png
BIN
road.png
Binary file not shown.
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 284 B |
23
tile.py
23
tile.py
@ -1,7 +1,22 @@
|
|||||||
|
from config import *
|
||||||
|
|
||||||
|
|
||||||
class Tile:
|
class Tile:
|
||||||
def __init__(self, a, b, x, y):
|
def __init__(self, a, b, pos, parent=None):
|
||||||
self.ground = a
|
self.ground = a
|
||||||
self.object = b
|
self.object = b
|
||||||
self.x = x
|
self.pos = pos
|
||||||
self.y = y
|
self.x = self.pos[0]
|
||||||
self.h = 0
|
self.y = self.pos[1]
|
||||||
|
self.w = 100
|
||||||
|
self.wu = 0
|
||||||
|
|
||||||
|
def water_usage(self):
|
||||||
|
if self.object == rose:
|
||||||
|
self.wu = 30
|
||||||
|
elif self.object == tulip:
|
||||||
|
self.wu = 10
|
||||||
|
elif self.object == orchid:
|
||||||
|
self.wu = 20
|
||||||
|
elif self.object == sunflower:
|
||||||
|
self.wu = 40
|
||||||
|
Loading…
Reference in New Issue
Block a user