Changing bfs into astar using 'Field' class #11
14
main.py
14
main.py
@ -2,10 +2,10 @@ import pygame
|
||||
import sys
|
||||
import random
|
||||
from settings import screen_height, screen_width, SIZE, SPECIES, block_size, tile, road_coords, directions
|
||||
from src.map import drawRoads, seedForFirstTime
|
||||
from src.map import drawRoads, seedForFirstTime, return_fields_list
|
||||
from src.Tractor import Tractor
|
||||
from src.Plant import Plant
|
||||
from src.bfs import BFS
|
||||
from src.bfs import Astar
|
||||
|
||||
# pygame initialization
|
||||
pygame.init()
|
||||
@ -33,18 +33,20 @@ tractor_group.add(tractor)
|
||||
#PLANTS
|
||||
plant_group = pygame.sprite.Group()
|
||||
plant_group = seedForFirstTime()
|
||||
fields = return_fields_list()
|
||||
|
||||
#
|
||||
tractor_move = pygame.USEREVENT + 1
|
||||
pygame.time.set_timer(tractor_move, 800)
|
||||
moves = []
|
||||
goal_bfs = BFS()
|
||||
goal_astar = Astar()
|
||||
destination = (random.randrange(0, 936, 36), random.randrange(0, 900, 36))
|
||||
print("Destination: ", destination)
|
||||
moves = goal_bfs.search(
|
||||
moves = goal_astar.search(
|
||||
[tractor.rect.x, tractor.rect.y, directions[tractor.rotation]], destination)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
running = True
|
||||
|
||||
@ -61,7 +63,9 @@ if __name__ == "__main__":
|
||||
running = False
|
||||
if event.type == tractor_move:
|
||||
if len(moves) != 0:
|
||||
step = moves.pop()
|
||||
moves_list = list(moves) # convert to list
|
||||
step = moves_list.pop() # pop the last element
|
||||
moves = tuple(moves_list) # convert back to tuple
|
||||
tractor.movement(step[0])
|
||||
|
||||
|
||||
|
10
src/Field.py
10
src/Field.py
@ -1,13 +1,13 @@
|
||||
from pygame.sprite import Sprite
|
||||
|
||||
class Field(Sprite):
|
||||
def __init__(self, type, row_id, col_id, image, cost, hydration_level , soil,
|
||||
def __init__(self, type, x, y, image, cost, hydration_level , soil,
|
||||
fertilizer_degree, development_degree, plant_type, fertilizer_type, to_water):
|
||||
|
||||
super().__init__()
|
||||
self.type = type
|
||||
self.row_id = row_id
|
||||
self.col_id = col_id
|
||||
self.position = (row_id, col_id)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.position = (x, y)
|
||||
self.image = image
|
||||
self.cost = cost
|
||||
|
||||
|
@ -36,7 +36,7 @@ class Tractor(pygame.sprite.Sprite):
|
||||
self.movement('F')
|
||||
|
||||
#waits between moves to avoid moving to fast
|
||||
pygame.time.wait(100)
|
||||
pygame.time.wait(30)
|
||||
|
||||
def move_forward(self):
|
||||
if self.rect.y > 0 and self.rotation == 0:
|
||||
|
38
src/bfs.py
38
src/bfs.py
@ -1,14 +1,25 @@
|
||||
import heapq
|
||||
from settings import block_size, screen_width, directions
|
||||
import copy
|
||||
from src.map import get_cost_by_type, get_type_by_position, return_fields_list
|
||||
|
||||
fields = return_fields_list()
|
||||
|
||||
class Node:
|
||||
def __init__(self, state, parent=None, action=None):
|
||||
def __init__(self, state, parent=None, action=None, g=0, h=0):
|
||||
self.state = state
|
||||
self.parent = parent
|
||||
self.action = action
|
||||
self.g = g
|
||||
self.h = h
|
||||
|
||||
class BFS:
|
||||
def f(self):
|
||||
return self.g + self.h
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.f() < other.f()
|
||||
|
||||
class Astar:
|
||||
def __init__(self):
|
||||
self.fringe = []
|
||||
self.explored = []
|
||||
@ -16,6 +27,7 @@ class BFS:
|
||||
def successor(self, state):
|
||||
pos_x, pos_y, rotation = state
|
||||
options = []
|
||||
cost = get_cost_by_type(get_type_by_position(fields, pos_x, pos_y))
|
||||
|
||||
if rotation == directions[0]:
|
||||
states = [(pos_x, pos_y - block_size, directions[0]), (pos_x, pos_y, directions[270]), (pos_x, pos_y, directions[90])]
|
||||
@ -32,7 +44,7 @@ class BFS:
|
||||
|
||||
for s, a in zip(states, actions):
|
||||
if self.valid_state(s):
|
||||
options.append((a, s))
|
||||
options.append((a, s, cost))
|
||||
|
||||
return options
|
||||
|
||||
@ -42,6 +54,9 @@ class BFS:
|
||||
return False
|
||||
return True
|
||||
|
||||
def heuristic(self, state, goal):
|
||||
return abs(state[0] - goal[0]) + abs(state[1] - goal[1])
|
||||
|
||||
def goal_path(self, elem):
|
||||
path = []
|
||||
|
||||
@ -49,27 +64,30 @@ class BFS:
|
||||
path.append([elem.action, elem.state[0], elem.state[1]])
|
||||
elem = elem.parent
|
||||
|
||||
path = path[::-1]
|
||||
return path
|
||||
|
||||
def search(self, istate, goaltest):
|
||||
x, y, rotation = istate
|
||||
start_node = Node((x, y, rotation))
|
||||
start_node = Node((x, y, rotation), None, None, 0, self.heuristic(istate, goaltest))
|
||||
|
||||
self.fringe.append(start_node)
|
||||
heapq.heappush(self.fringe, (start_node.f(), start_node))
|
||||
|
||||
while True:
|
||||
if len(self.fringe) == 0:
|
||||
return False
|
||||
|
||||
elem = self.fringe.pop(0)
|
||||
_, elem = heapq.heappop(self.fringe)
|
||||
|
||||
if elem.state[0] == goaltest[0] and elem.state[1] == goaltest[1]:
|
||||
return self.goal_path(elem)
|
||||
|
||||
self.explored.append(elem.state)
|
||||
|
||||
for (action, state) in self.successor(elem.state):
|
||||
for (action, state, cost) in self.successor(elem.state):
|
||||
if state not in self.explored:
|
||||
x = Node(state, elem, action)
|
||||
self.fringe.append(x)
|
||||
g = elem.g + cost # cost to move from parent node to current node is based on the field type.
|
||||
h = self.heuristic(state, goaltest) # manhattan distance cost
|
||||
x = Node(state, elem, action, g, h) #creating the node and pushing it into fringe
|
||||
heapq.heappush(self.fringe, (x.f(), x))
|
||||
|
||||
|
||||
|
31
src/map.py
31
src/map.py
@ -3,8 +3,33 @@ import pygame
|
||||
from settings import screen_height, screen_width, SIZE, SPECIES, block_size, tile, road_coords, fields_amount, field_size, field_height, field_width
|
||||
from src.Plant import Plant
|
||||
import random
|
||||
from src.Field import Field
|
||||
|
||||
|
||||
def get_type_by_position(fields, x, y):
|
||||
for field in fields:
|
||||
if field.x == x and field.y == y:
|
||||
return field.plant_type
|
||||
return None
|
||||
|
||||
def get_cost_by_type(plant_type):
|
||||
#plant_type == None, when field is empty.
|
||||
if plant_type == None:
|
||||
return 200
|
||||
elif plant_type == 'carrot':
|
||||
return 300
|
||||
elif plant_type == 'potato':
|
||||
return 500
|
||||
elif plant_type == 'beetroot':
|
||||
return 500
|
||||
elif plant_type == 'wheat':
|
||||
return 1000
|
||||
#else, means that field is type of road.
|
||||
else:
|
||||
return 100
|
||||
|
||||
fields = pygame.sprite.Group()
|
||||
|
||||
def drawRoads(screen):
|
||||
#drawing roads:
|
||||
road = pygame.image.load("assets/road.jpeg")
|
||||
@ -12,9 +37,11 @@ def drawRoads(screen):
|
||||
for x in road_coords:
|
||||
for block in range(26):
|
||||
screen.blit(road, (x*block_size, block * 36))
|
||||
fields.add(Field('road', x*block_size, block * 36, None, get_cost_by_type('road'), None, None, None, None, 'road', None, None))
|
||||
for y in road_coords:
|
||||
for block in range(26):
|
||||
screen.blit(road, (block * 36, y*block_size))
|
||||
fields.add(Field('road', block * 36, y*block_size, None, get_cost_by_type('road'), None, None, None, None, 'road', None, None))
|
||||
|
||||
barn_img = pygame.image.load('assets/barn.png')
|
||||
barn = pygame.transform.scale(barn_img, tile)
|
||||
@ -33,8 +60,12 @@ def seedForFirstTime():
|
||||
new_plant = Plant(plant,0, x, y)
|
||||
blocks_seeded_in_field = blocks_seeded_in_field + 1
|
||||
plant_group.add(new_plant)
|
||||
fields.add(Field('field', x-18, y-18, None, get_cost_by_type(plant), None, None, None, None, plant, None, None))
|
||||
return plant_group
|
||||
|
||||
def return_fields_list():
|
||||
return fields
|
||||
|
||||
|
||||
# to-be-done with minecraft farmland graphic xD
|
||||
# maybe this function should be in drawRoads (ofc with changed name), not separated
|
||||
|
Loading…
Reference in New Issue
Block a user