diff --git a/main.py b/main.py index deec3b8..99686d4 100644 --- a/main.py +++ b/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,20 +33,22 @@ 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 + running = True while running: for event in pygame.event.get(): @@ -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]) diff --git a/src/Field.py b/src/Field.py index 77b78d6..fedc537 100644 --- a/src/Field.py +++ b/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 diff --git a/src/Tractor.py b/src/Tractor.py index aff7f1d..21ff6d9 100644 --- a/src/Tractor.py +++ b/src/Tractor.py @@ -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: diff --git a/src/bfs.py b/src/bfs.py index e5bacef..edb9bdc 100644 --- a/src/bfs.py +++ b/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 + + def f(self): + return self.g + self.h + + def __lt__(self, other): + return self.f() < other.f() -class BFS: +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 @@ -41,6 +53,9 @@ class BFS: if pos_x < 0 or pos_x >= screen_width or pos_y < 0 or pos_y >= screen_width: 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) \ No newline at end of file + 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)) + + diff --git a/src/map.py b/src/map.py index 34aed87..1e479a5 100644 --- a/src/map.py +++ b/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