Compare commits

..

No commits in common. "main" and "sieci-neuronowe-uczenie" have entirely different histories.

11 changed files with 82 additions and 243 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
__pycache__/ __pycache__/
.DS_Store .DS_Store
.env .env
src/field

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["sbsnippets.pytorch-snippets"]
}

View File

@ -8,8 +8,6 @@ Wymagane biblioteki do pobrania:
pip install pygame pip install pygame
pip install python-dotenv pip install python-dotenv
pip install pytholog pip install pytholog
pip install torch
pip install sklearn
``` ```
Stwórz plik `.env` w głównym folderze projektu o poniższej treści: Stwórz plik `.env` w głównym folderze projektu o poniższej treści:
@ -27,6 +25,10 @@ STARTING_DIRECTION = north
START_X = 0 START_X = 0
START_Y = 0 START_Y = 0
# coordinates of destination tile
FINAL_X = 15
FINAL_Y = 15
# tiles without plants modifier # tiles without plants modifier
FREE_TILES = 2 FREE_TILES = 2
``` ```
@ -35,12 +37,9 @@ Uruchom środowisko używając komend:
``` ```
cd src cd src
python generate_field.py
python main.py python main.py
``` ```
Skrypt `generate_field.py` musi zostać przy pierwszym uruchomieniu, aby wygenerować pole. Kolejne wykonania skryptu nie są wymagane.
## 🧑‍🌾 Członkowie grupy ## 🧑‍🌾 Członkowie grupy
- Wojciech Kubicki (483780) - Wojciech Kubicki (483780)

View File

@ -1 +0,0 @@
,adam,adam-thinkpad,10.06.2024 15:05,file:///home/adam/.config/libreoffice/4;

View File

@ -1,16 +1,13 @@
import pygame import pygame
from tile import Tile from tile import Tile
from tractor import Tractor from tractor import Tractor
from ast import literal_eval
class Field: class Field:
def __init__(self): def __init__(self):
self.tiles = pygame.sprite.Group() self.tiles = pygame.sprite.Group()
with open('./field', 'r', encoding='UTF-8') as file: # TODO: enable resizing field grid from 16x16 to any size
content = file.read() for x in range(256):
tiles = literal_eval(content) self.tiles.add(Tile(x, self))
for x in range(len(tiles)):
self.tiles.add(Tile(x, self, tiles[x]))
self.tractor = Tractor(self) self.tractor = Tractor(self)

View File

@ -1,116 +0,0 @@
from random import randint, choices, random
from kb import tractor_kb, multi_sasiedzi
import pytholog as pl
from numpy.random import choice as npchoice
def score_field(field):
score = 0
for index in range(len(field)):
neighbours = []
if index >= 16 and field[index-16] != 'water':
neighbours.append(field[index-16])
if index % 15 != 0 and field[index+1] != 'water':
neighbours.append(field[index+1])
if index < 240 and field[index+16] != 'water':
neighbours.append(field[index+16])
if index % 16 != 0 and field[index-1] != 'water':
neighbours.append(field[index-1])
mod = multi_sasiedzi(field[index], neighbours)[0]["Mul"]
if mod > 10:
print(mod, '= multi(', field[index], ', ', neighbours, ')')
score += mod
score = score / 256
return score
def choose_parents(population):
total_weights = sum(entity[0] for entity in population)
weights = [entity[0] / total_weights for entity in population]
selection = npchoice(len(population), size=2, replace=False, p=weights)
parents = [population[i] for i in selection]
return parents[0], parents[1]
def breed_and_mutate(mom, dad):
crossover_point = randint(1, len(mom[1]) - 2)
offspring = mom[1][:crossover_point] + dad[1][crossover_point:]
if len(offspring) != len(mom):
ValueError("offspring length is not equal to mom length")
if random() < 0.1:
mutation_index = randint(0, len(offspring) - 1)
while offspring[mutation_index] == 'water':
mutation_index = randint(0, len(offspring) - 1)
mutation = get_random_vegetable()
while mutation == offspring[mutation_index]:
mutation = get_random_vegetable()
offspring[mutation_index] = mutation
offspring_score = score_field(offspring)
# print('offspring score', offspring_score, 'for parents', mom[0], 'and', dad[0])
return [offspring_score, offspring]
def get_random_vegetable():
vegetables = [x['Nazwa_warzywa'] for x in tractor_kb.query(pl.Expr("warzywo(Nazwa_warzywa)"))]
return vegetables[randint(0,len(vegetables)-1)]
def genetic_algorithm(population, iterations):
population_size = len(population)
for entity in population:
entity[0] = score_field(entity[1])
for iteration in range(iterations):
population.sort(key=lambda x: x[0], reverse=True)
print('\n=====\n\n💪 Best individual in iteration', iteration, 'has a score of', population[0][0])
population = population[:population_size//2]
new_offspring = []
while len(population) + len(new_offspring) < population_size:
mom, dad = choose_parents(population)
child = breed_and_mutate(mom, dad)
new_offspring.append(child)
population.extend(new_offspring)
return population[0]
population = []
# each field has unmutable locations of water and grass tiles
water_tile_indexes = [1, 2, 3, 34, 37, 44, 45, 53, 60, 61, 69, 81, 82, 83, 84, 119, 120, 121, 136, 152, 187, 194, 202, 203, 204, 210, 219, 226, 227, 228]
grass_tile_indexes = [0, 39, 40, 56, 71, 72, 73, 86, 88, 114, 115, 130, 146, 147, 163, 164, 166, 167, 180, 181, 182, 231, 232, 233]
vegetables = [x['Nazwa_warzywa'] for x in tractor_kb.query(pl.Expr("warzywo(Nazwa_warzywa)"))]
for _ in range(100):
field = [vegetables[randint(0, 24)] for _ in range(256)]
for index in water_tile_indexes:
field[index] = "water"
for index in grass_tile_indexes:
field[index] = "grass"
# entities of the population are stored with two properties
# the first being the average score of the field
# and the second being the layout of the field
population.append([0, field])
best = genetic_algorithm(population, 20)
print('\n=====\n\nfinal field multiplier score is', best[0])
with open('field', 'w', encoding='utf-8') as file:
file.write(str(best[1]))
file.close
print('final field layout saved to file "field" in the current working directory\n')

View File

@ -370,7 +370,7 @@ tractor_kb([
"przeszkadza(burak, szpinak, 0.85)", "przeszkadza(burak, szpinak, 0.85)",
"przeszkadza(burak, ziemniak, 0.85)", "przeszkadza(burak, ziemniak, 0.85)",
"przeszkadza(cebula, fasola, 0.80)", "przeszkadza(cebula, fasola, 0.80)",
"przeszkadza(cebula, groch, 0.85)", "przeszkadza(cebula, groch, 85)",
"przeszkadza(cebula, kalafior, 0.70)", "przeszkadza(cebula, kalafior, 0.70)",
"przeszkadza(cebula, kapusta, 0.75)", "przeszkadza(cebula, kapusta, 0.75)",
"przeszkadza(cebula, marchew, 0.85)", "przeszkadza(cebula, marchew, 0.85)",

View File

@ -1,7 +1,7 @@
import pygame import pygame
from field import Field from field import Field
import os import os
from config import TILE_SIZE, TICK_RATE, FINAL_X, FINAL_Y from config import TILE_SIZE, TICK_RATE
if __name__ == "__main__": if __name__ == "__main__":
pygame.init() pygame.init()
@ -13,19 +13,10 @@ if __name__ == "__main__":
field = Field() field = Field()
running = True running = True
while running: while running:
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
running = False running = False
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
print(f"Mouse clicked at: ({x}, {y})")
grid_x = x // TILE_SIZE
grid_y = y // TILE_SIZE
field.tractor.set_new_goal((grid_x, grid_y))
field.tractor.update() field.tractor.update()
screen.fill(WHITE) screen.fill(WHITE)

View File

@ -11,26 +11,25 @@ from PIL import Image
class Tile(pygame.sprite.Sprite): class Tile(pygame.sprite.Sprite):
def __init__(self, id, field, tile_type): def __init__(self, id, field):
super().__init__() super().__init__()
self.id = id self.id = id
x = id%16 x = id%16
y = id//16 y = id//16
self.field = field self.field = field
self.set_type(tile_type)
print('tile type set as', tile_type) # temporary solution to have vegetables act as obstacles
if self.type == 'water': if random.randint(1, 10) % FREE_TILES == 0:
self.stage = 'no_plant' vegetables = tractor_kb.query(pl.Expr("warzywo(Nazwa_warzywa)"))
self.prediction = 'water' random_vegetable = vegetables[random.randint(0, len(vegetables)-1)]['Nazwa_warzywa']
self.water_level = 100
elif self.type == 'grass': if random_vegetable in {'cebula','pietruszka','bób', 'dynia','ziemniak'}:
self.stage = 'no_plant' random_vegetable = 'marchew'
self.prediction = 'grass'
self.set_type(random_vegetable)
self.water_level = random.randint(1, 5) * 10 self.water_level = random.randint(1, 5) * 10
else:
self.stage = 'planted' # wczesniej to była self.faza = 'posadzono' ale stwierdzilem ze lepiej po angielsku??? self.stage = 'planted' # wczesniej to była self.faza = 'posadzono' ale stwierdzilem ze lepiej po angielsku???
self.water_level = random.randint(1, 5) * 10
classes = [ classes = [
"bób", "brokuł", "brukselka", "burak", "cebula", "bób", "brokuł", "brukselka", "burak", "cebula",
@ -52,7 +51,20 @@ class Tile(pygame.sprite.Sprite):
]) ])
self.prediction = self.predict(model, image_transforms, self.image_path, classes) self.prediction = self.predict(model, image_transforms, self.image_path, classes)
else:
if random.randint(1, 10) % 3 == 0:
self.set_type('water')
self.water_level = 100
self.stage = 'no_plant'
self.prediction = 'water'
else:
self.set_type('grass')
self.water_level = random.randint(1, 5) * 10
self.stage = 'no_plant'
self.prediction = 'grass'
self.rect = self.image.get_rect() self.rect = self.image.get_rect()
self.rect.topleft = (x * TILE_SIZE, y * TILE_SIZE) self.rect.topleft = (x * TILE_SIZE, y * TILE_SIZE)
@ -97,6 +109,9 @@ class Tile(pygame.sprite.Sprite):
result = classes[predicted.item()] result = classes[predicted.item()]
if result == "ziemniak":
result = 'marchew'
return result return result

View File

@ -18,33 +18,30 @@ class Tractor(pygame.sprite.Sprite):
def __init__(self, field): def __init__(self, field):
super().__init__ super().__init__
self.field = field self.field = field
self.water = 50
self.image = pygame.image.load('images/tractor/east.png').convert_alpha() self.image = pygame.image.load('images/tractor/east.png').convert_alpha()
self.image = pygame.transform.scale(self.image, (TILE_SIZE, TILE_SIZE)) self.image = pygame.transform.scale(self.image, (TILE_SIZE, TILE_SIZE))
self.rect = self.image.get_rect() self.rect = self.image.get_rect()
self.direction = 'east'
self.start = (0, 0) self.direction = STARTING_DIRECTION
self.final = (0, 0) # TODO: enable tractor to start on other tile than (0,0)
self.start = (START_X, START_Y)
self.final = (FINAL_X, FINAL_Y)
print('destination @', self.final[0], self.final[1]) print('destination @', self.final[0], self.final[1])
self.rect.topleft = (self.start[0] * TILE_SIZE, self.start[1] * TILE_SIZE) self.rect.topleft = (self.start[0] * TILE_SIZE, self.start[1] * TILE_SIZE)
self.rect.topleft = (self.start[0] * TILE_SIZE, self.start[1] * TILE_SIZE) self.water = 50
self.actions = []
self.action_index = 0 # A-STAR
# came_from, total_cost = self.a_star()
# path = self.reconstruct_path(came_from)
# self.actions = self.recreate_actions(path)
# self.action_index = 0
# DECISION TREE: # DECISION TREE:
self.label_encoders = {} self.label_encoders = {}
self.load_decision_tree_model() self.load_decision_tree_model()
def set_new_goal(self, goal):
self.start = self.get_coordinates()
self.final = goal
came_from, total_cost = self.a_star()
path = self.reconstruct_path(came_from)
self.actions = self.recreate_actions(path)
self.action_index = 0
print(f"New goal set to: {self.final}")
def load_decision_tree_model(self): def load_decision_tree_model(self):
data = pd.read_csv('tree.csv') data = pd.read_csv('tree.csv')
@ -97,11 +94,13 @@ class Tractor(pygame.sprite.Sprite):
def draw(self, surface): def draw(self, surface):
surface.blit(self.image, self.rect) surface.blit(self.image, self.rect)
def get_coordinates(self): def get_coordinates(self):
x = self.rect.x // TILE_SIZE x = self.rect.x // TILE_SIZE
y = self.rect.y // TILE_SIZE y = self.rect.y // TILE_SIZE
return (x,y) return (x,y)
def move(self): def move(self):
if self.direction == "north" and self.rect.y > 0: if self.direction == "north" and self.rect.y > 0:
self.rect.y -= TILE_SIZE self.rect.y -= TILE_SIZE
@ -163,21 +162,29 @@ class Tractor(pygame.sprite.Sprite):
self.move() self.move()
else: else:
self.move() self.move()
def decision_tree(self): def update(self):
match (self.get_current_tile().type): # A STAR:
case ('grass'): # if self.action_index == len(self.actions):
action = self.make_decision() # return
case ('water'): # action = self.actions[self.action_index]
action = 'nothing'
case _: # match (action):
action = 'water' # case ('move'):
# self.move()
# case ('left'):
# self.rotate('left')
# case ('right'):
# self.rotate('right')
# DECISION TREE:
action = self.make_decision()
if (self.get_current_tile().type != 'grass' or self.get_current_tile().type == 'water'): action = 'move'
self.prev_action = action self.prev_action = action
print("Decyzja podjęta przez drzewo decyzyjne: ", action)
match (action): match (action):
case ('nothing'): case ('move'):
pass pass
#self.move_rotating() #self.move_rotating()
case ('harvest'): case ('harvest'):
@ -185,7 +192,7 @@ class Tractor(pygame.sprite.Sprite):
case ('water'): case ('water'):
self.get_current_tile().water_level += 10 self.get_current_tile().water_level += 10
case ('plant(bób)'): case ('plant(bób)'):
self.get_current_tile().set_type('bób') self.get_current_tile().set_type('marchew')
case ('plant(brokuł)'): case ('plant(brokuł)'):
self.get_current_tile().set_type('brokuł') self.get_current_tile().set_type('brokuł')
case ('plant(brukselka)'): case ('plant(brukselka)'):
@ -193,11 +200,11 @@ class Tractor(pygame.sprite.Sprite):
case ('plant(burak)'): case ('plant(burak)'):
self.get_current_tile().set_type('burak') self.get_current_tile().set_type('burak')
case ('plant(cebula)'): case ('plant(cebula)'):
self.get_current_tile().set_type('cebula') self.get_current_tile().set_type('marchew')
case ('plant(cukinia)'): case ('plant(cukinia)'):
self.get_current_tile().set_type('cukinia') self.get_current_tile().set_type('cukinia')
case ('plant(dynia)'): case ('plant(dynia)'):
self.get_current_tile().set_type('dynia') self.get_current_tile().set_type('fasola')
case ('plant(fasola)'): case ('plant(fasola)'):
self.get_current_tile().set_type('fasola') self.get_current_tile().set_type('fasola')
case ('plant(groch)'): case ('plant(groch)'):
@ -217,7 +224,7 @@ class Tractor(pygame.sprite.Sprite):
case ('plant(papryka)'): case ('plant(papryka)'):
self.get_current_tile().set_type('papryka') self.get_current_tile().set_type('papryka')
case ('plant(pietruszka)'): case ('plant(pietruszka)'):
self.get_current_tile().set_type('pietruszka') self.get_current_tile().set_type('marchew')
case ('plant(pomidor)'): case ('plant(pomidor)'):
self.get_current_tile().set_type('pomidor') self.get_current_tile().set_type('pomidor')
case ('plant(por)'): case ('plant(por)'):
@ -234,22 +241,8 @@ class Tractor(pygame.sprite.Sprite):
self.get_current_tile().set_type('szpinak') self.get_current_tile().set_type('szpinak')
case ('plant(ziemniak)'): case ('plant(ziemniak)'):
self.get_current_tile().set_type('ziemniak') self.get_current_tile().set_type('ziemniak')
self.move_2()
def update(self): #self.action_index += 1
# A STAR:
if self.action_index == len(self.actions):
return
action = self.actions[self.action_index]
match (action):
case ('move'):
self.move()
case ('left'):
self.rotate('left')
case ('right'):
self.rotate('right')
self.action_index += 1
if self.get_current_tile().type == "grass": if self.get_current_tile().type == "grass":
print("Co jest faktycznie: trawa") print("Co jest faktycznie: trawa")
@ -259,13 +252,9 @@ class Tractor(pygame.sprite.Sprite):
print("Rozpoznano: ", self.get_current_tile().prediction) print("Rozpoznano: ", self.get_current_tile().prediction)
print("Co jest faktycznie: ", self.get_current_tile().type) print("Co jest faktycznie: ", self.get_current_tile().type)
print("\n") print("\n")
if self.get_coordinates() == self.final:
self.decision_tree()
return return
def log_info(self): def log_info(self):
# print on what tile type the tractor is on # print on what tile type the tractor is on
x = self.rect.x // TILE_SIZE x = self.rect.x // TILE_SIZE
@ -378,12 +367,13 @@ class Tractor(pygame.sprite.Sprite):
if current == self.final: if current == self.final:
break break
# next_node: tuple[int, int]
for next_node in self.neighboring_nodes(coordinates=current): for next_node in self.neighboring_nodes(coordinates=current):
enter_cost = self.cost_of_entering_node(coordinates=next_node) enter_cost = self.cost_of_entering_node(coordinates=next_node)
new_cost = cost_so_far[current] + enter_cost new_cost: int = cost_so_far[current] + enter_cost
if next_node not in cost_so_far or new_cost < cost_so_far[next_node]: if next_node not in cost_so_far or new_cost < cost_so_far[next_node]:
cost_so_far[next_node] = new_cost cost_so_far[next_node] = new_cost
priority = new_cost + self.manhattan_cost(next_node) priority = new_cost + self.manhattan_cost(current)
heapq.heappush(fringe, (priority, next_node)) heapq.heappush(fringe, (priority, next_node))
came_from[next_node] = current came_from[next_node] = current

View File

@ -217,41 +217,3 @@ cebula,40,growing,szpinak,grass,kapusta,szpinak,move
cebula,50,growing,water,szpinak,kalarepa,grass,move cebula,50,growing,water,szpinak,kalarepa,grass,move
cebula,10,growing,szpinak,grass,marchew,grass,move cebula,10,growing,szpinak,grass,marchew,grass,move
cebula,20,growing,grass,cebula,burak,grass,move cebula,20,growing,grass,cebula,burak,grass,move
grass,10,no_plant,szpinak,dynia,szpinak,water,plant(seler)
grass,10,no_plant,marchew,dynia,grass,water,plant(seler)
grass,10,no_plant,water,dynia,cebula,cebula,plant(seler)
grass,10,no_plant,ogórek,dynia,cebula,grass,plant(seler)
grass,10,no_plant,szpinak,dynia,fasola,grass,plant(seler)
ziemniak,40,planted,water,grass,szpinak,grass,move
ziemniak,50,grown,szpinak,szpinak,grass,grass,move
ziemniak,10,planted,water,ziemniak,seler,water,move
ziemniak,20,planted,szpinak,szpinak,grass,seler,move
ziemniak,30,planted,grass,water,ziemniak,grass,move
ziemniak,40,planted,growing,grass,grass,burak,move
ziemniak,50,grown,growing,szpinak,water,grass,move
ziemniak,10,planted,growing,water,szpinak,marchew,move
ziemniak,20,planted,growing,szpinak,grass,kapusta,move
cukinia,50,grown,brukselka,water,grass,jarmuż,move
dynia,0,grown,burak,grass,pietruszka,kalafior,move
grass,10,grown,cebula,grass,pietruszka,kalarepa,move
grass,20,grown,cukinia,groch,pietruszka,kapusta,move
jarmuż,30,grown,grass,water,pietruszka,brokuł,move
kalafior,40,grown,fasola,pietruszka,water,brukselka,move
grass,10,grown,cebula,pietruszka,grass,kalarepa,move
grass,20,grown,cukinia,pietruszka,groch,kapusta,move
jarmuż,30,grown,grass,pietruszka,water,brokuł,move
kalafior,40,grown,fasola,kalafior,water,brukselka,move
bób,40,planted,jarmuż,grass,water,bób,move
brokuł,10,growing,kalafior,brokuł,bób,kalafior,move
brukselka,20,growing,water,brukselka,grass,bób,move
burak,30,planted,bób,burak,brukselka,bób,move
cebula,40,growing,brokuł,bób,burak,bób,move
cukinia,40,growing,grass,bób,water,grass,move
dynia,10,planted,grass,grass,cukinia,kalafior,move
grass,40,planted,jarmuż,grass,water,bób,move
grass,10,growing,kalafior,brokuł,bób,kalafior,move
grass,20,growing,water,bób,grass,bób,move
grass,30,planted,bób,burak,brukselka,bób,move
grass,40,growing,brokuł,bób,burak,bób,move
grass,40,growing,grass,bób,water,grass,move
grass,10,planted,bób,grass,cukinia,kalafior,move

1 tile_type water_level plant_stage neighbor_N neighbor_E neighbor_W neighbor_S action
217 cebula 50 growing water szpinak kalarepa grass move
218 cebula 10 growing szpinak grass marchew grass move
219 cebula 20 growing grass cebula burak grass move
grass 10 no_plant szpinak dynia szpinak water plant(seler)
grass 10 no_plant marchew dynia grass water plant(seler)
grass 10 no_plant water dynia cebula cebula plant(seler)
grass 10 no_plant ogórek dynia cebula grass plant(seler)
grass 10 no_plant szpinak dynia fasola grass plant(seler)
ziemniak 40 planted water grass szpinak grass move
ziemniak 50 grown szpinak szpinak grass grass move
ziemniak 10 planted water ziemniak seler water move
ziemniak 20 planted szpinak szpinak grass seler move
ziemniak 30 planted grass water ziemniak grass move
ziemniak 40 planted growing grass grass burak move
ziemniak 50 grown growing szpinak water grass move
ziemniak 10 planted growing water szpinak marchew move
ziemniak 20 planted growing szpinak grass kapusta move
cukinia 50 grown brukselka water grass jarmuż move
dynia 0 grown burak grass pietruszka kalafior move
grass 10 grown cebula grass pietruszka kalarepa move
grass 20 grown cukinia groch pietruszka kapusta move
jarmuż 30 grown grass water pietruszka brokuł move
kalafior 40 grown fasola pietruszka water brukselka move
grass 10 grown cebula pietruszka grass kalarepa move
grass 20 grown cukinia pietruszka groch kapusta move
jarmuż 30 grown grass pietruszka water brokuł move
kalafior 40 grown fasola kalafior water brukselka move
bób 40 planted jarmuż grass water bób move
brokuł 10 growing kalafior brokuł bób kalafior move
brukselka 20 growing water brukselka grass bób move
burak 30 planted bób burak brukselka bób move
cebula 40 growing brokuł bób burak bób move
cukinia 40 growing grass bób water grass move
dynia 10 planted grass grass cukinia kalafior move
grass 40 planted jarmuż grass water bób move
grass 10 growing kalafior brokuł bób kalafior move
grass 20 growing water bób grass bób move
grass 30 planted bób burak brukselka bób move
grass 40 growing brokuł bób burak bób move
grass 40 growing grass bób water grass move
grass 10 planted bób grass cukinia kalafior move