Traktor/app/tractor.py

360 lines
12 KiB
Python

#!/usr/bin/python3
from __future__ import annotations
import random
import threading
import pygame
import os
import time
from threading import Thread
from typing import Union
from app.base_field import BaseField
from app.board import Board
from app.neural_network import NeuralNetwork
from app.utils import get_class
from app.fields import CROPS, PLANTS, Crops, Sand, Clay, Field
from config import *
from app.fields import Plant, Soil, Crops
from app.decision_tree import DecisionTree
from app.weather import Weather
class Tractor(BaseField):
def __init__(self, board: Board):
super().__init__(os.path.join(RESOURCE_DIR, f"{TRACTOR}.{PNG}"))
self.__pos_x = (int(HORIZONTAL_NUM_OF_FIELDS / 2) - 1) * FIELD_SIZE
self.__pos_y = (int(VERTICAL_NUM_OF_FIELDS / 2) - 1) * FIELD_SIZE
self.__direction = 0.0
self.__move = FIELD_SIZE
self.__board = board
self.__harvested_corps = []
self.__fuel = 10
self.__neural_network = NeuralNetwork()
self.__tree = DecisionTree()
self.__weather = Weather()
def draw(self, screen: pygame.Surface) -> None:
self.draw_field(screen, self.__pos_x + FIELD_SIZE / 2, self.__pos_y + FIELD_SIZE / 2,
is_centered=True, size=(FIELD_SIZE, FIELD_SIZE), angle=self.__direction)
# Key methods handlers
def move(self) -> None:
if self.__direction == D_EAST:
self.move_right()
elif self.__direction == D_NORTH:
self.move_up()
elif self.__direction == D_WEST:
self.move_left()
else:
self.move_down()
def move_up(self) -> None:
if self.__pos_y - self.__move >= 0:
self.__pos_y -= self.__move
def move_down(self) -> None:
if self.__pos_y + self.__move + FIELD_SIZE <= HEIGHT:
self.__pos_y += self.__move
def move_left(self) -> None:
if self.__pos_x - self.__move >= 0:
self.__pos_x -= self.__move
def move_right(self) -> None:
if self.__pos_x + self.__move + FIELD_SIZE <= WIDTH:
self.__pos_x += self.__move
def rotate_left(self) -> None:
self.__direction = (self.__direction - 90.0) % 360.0
def rotate_right(self) -> None:
self.__direction = (self.__direction + 90.0) % 360.0
def hydrate(self) -> None:
if self.check_field(Sand):
field = self.get_field_from_board()
if not field.is_hydrated and field.is_sowed:
print('Hydrate soil')
self.irrigate_sand(field)
elif self.check_field(Plant):
field = self.get_field_from_board()
if not field.is_hydrated:
print("Hydrate plant")
self.irrigate_plants(field)
def hydrate_sand(self) -> None:
if self.check_field(Sand):
field = self.get_field_from_board()
if not field.is_hydrated and field.is_sowed:
print('Hydrate soil')
self.irrigate_sand(field)
def hydrate_plant(self) -> None:
if self.check_field(Plant):
field = self.get_field_from_board()
if not field.is_hydrated:
print("Hydrate plant")
self.irrigate_plants(field)
def sow(self) -> None:
field = self.get_field_from_board()
if self.check_field(Sand) and not field.is_sowed:
print('Sow')
field.is_sowed = True
def harvest(self) -> None:
field = self.get_field_from_board()
prediction = self.__neural_network.check(field)
if prediction.capitalize() in CROPS:
print('Harvest')
field = self.get_field_from_board()
self.harvest_crops(field)
self.get_result_of_harvesting()
def fertilize(self) -> None:
if self.check_field(Clay):
print('Fertilize soil')
field = self.get_field_from_board()
self.fertilize_clay(field)
################################################################################
def fertilize_clay(self, field: Clay) -> None:
field.is_fertilized = True
self.do_action((Sand.__name__,))
def irrigate_plants(self, field: Plant) -> None:
field.is_hydrated = True
# self.do_time_action(CROPS)
self.do_action(CROPS)
def irrigate_sand(self, field: Sand) -> None:
field.is_hydrated = True
# self.do_time_action(PLANTS)
self.do_action(PLANTS)
def harvest_crops(self, field: Crops) -> None:
self.__harvested_corps.append(type(field).__name__)
self.do_action((Clay.__name__,))
def do_action(self, TYPE: tuple) -> None:
choosen_type = random.choice(TYPE)
obj = get_class("app.fields", choosen_type)
x, y = self.get_position()
self.__board.get_fields()[x][y] = obj()
def do_time_action(self, TYPE: tuple) -> None:
thread = Thread(target=self.do_time_action_handler, args=(TYPE,), daemon=True)
thread.start()
def do_time_action_handler(self, TYPE: tuple) -> None:
time.sleep(TIME_OF_GROWING)
self.do_action(TYPE)
def check_field(self, class_name: Union[type(Plant), type(Crops), type(Soil)]) -> bool:
if isinstance(self.get_field_from_board(), class_name):
return True
return False
def get_field_from_board(self) -> BaseField:
x, y = self.get_position()
return self.__board.get_fields()[x][y]
def get_field_from_board_by_positions(self, x, y) -> BaseField:
return self.__board.get_fields()[x][y]
def get_position(self) -> tuple[int, int]:
x = self.__pos_x // FIELD_SIZE
y = self.__pos_y // FIELD_SIZE
return x, y
def get_result_of_harvesting(self) -> None:
crops = set(self.__harvested_corps)
result = 0.0
for crop in crops:
amount = self.__harvested_corps.count(crop)
print(f"{amount} x {crop}")
result += amount * get_class("app.fields", crop).price
print(f"Price for collected crops: {result:.2f}")
def __str__(self) -> str:
x, y = self.get_position()
return f"Position: {x}:{y} - {type(self.__board.get_fields()[x][y]).__name__}"
def get_direction(self) -> float:
return self.__direction
def run_bot_handler(self, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
thread = threading.Thread(target=self.run_bot, args=(moves, is_running), daemon=True)
thread.start()
def run_bot(self, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
print(moves)
print(f"Length of Moves {len(moves)}") # - {3 ** len(moves)}")
while len(moves) > 0:
movement, action = moves.pop(0)
# do action
time.sleep(0.5)
self.do_action_on_fields(action)
# move
time.sleep(1)
self.move_or_rotate(movement)
time.sleep(TIME_OF_MOVING)
is_running.clear()
def do_action_on_fields(self, action: str) -> None:
print(f"Action {action}")
if action == A_FERTILIZE:
self.fertilize()
elif action == A_SOW:
self.sow()
# self.hydrate()
elif action == A_HYDRATE:
self.hydrate()
elif action == A_HARVEST:
self.harvest()
def run_auto_bot_handler(self, action: str, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
thread = threading.Thread(target=self.run_auto_bot, args=(action, moves, is_running), daemon=True)
thread.start()
def run_auto_bot(self, action_type: str, moves: list[tuple[str, str]], is_running: threading.Event) -> None:
print(moves)
# print('Auto mode')
while len(moves) > 0:
movement, action = moves.pop(0)
# move
self.move_or_rotate(movement)
time.sleep(0.7)
self.do_action_on_fields(action_type)
time.sleep(1)
is_running.clear()
def move_or_rotate(self, movement: str) -> None:
print(f"Move {movement}")
if movement == M_GO_FORWARD:
self.move()
elif movement == M_ROTATE_LEFT:
self.rotate_left()
elif movement == M_ROTATE_RIGHT:
self.rotate_right()
@staticmethod
def move_is_correct(x: int, y: int, direction: float) -> Union[(int, int), None]:
pos_x = x * FIELD_SIZE
pos_y = y * FIELD_SIZE
if direction == D_NORTH and pos_y - FIELD_SIZE >= 0:
return x, y - 1
if direction == D_SOUTH and pos_y + 2 * FIELD_SIZE <= HEIGHT:
return x, y + 1
if direction == D_WEST and pos_x - FIELD_SIZE >= 0:
return x - 1, y
if direction == D_EAST and pos_x + 2 * FIELD_SIZE <= WIDTH:
return x + 1, y
return None
@staticmethod
def fertilize_clay_succ(field: Clay, board: Board, x: int, y: int) -> Sand:
field.is_fertilized = True
return Tractor.do_action_succ(board, x, y, (Sand.__name__,))
@staticmethod
def sow_succ(field: Sand) -> Sand:
field.is_sowed = True
return field
@staticmethod
def irrigate_plants_succ(field: Plant, board: Board, x: int, y: int) -> Crops:
field.is_hydrated = True
return Tractor.do_action_succ(board, x, y, CROPS)
@staticmethod
def irrigate_sand_succ(field: Sand, board: Board, x: int, y: int) -> Plant:
field.is_hydrated = True
return Tractor.do_action_succ(board, x, y, PLANTS)
@staticmethod
def harvest_crops_succ(board: Board, x: int, y: int) -> Clay:
# Tractor.__harvested_corps.append(type(field).__name__)
return Tractor.do_action_succ(board, x, y, (Clay.__name__,))
@staticmethod
def do_action_succ(board: Board, x: int, y: int, types: tuple) -> Union[Sand, Clay, Plant, Crops]:
choosen_type = random.choice(types)
obj = get_class("app.fields", choosen_type)
board.get_fields()[x][y] = obj()
return obj()
def harvest_checked_fields_handler(self, is_running: threading.Event) -> None:
thread = threading.Thread(target=self.harvest_checked_fields, args=(is_running,), daemon=True)
thread.start()
def go_forward_is_legal_move(self) -> bool:
flag = False
if (self.__direction == D_EAST and self.__pos_y - self.__move >= 0) or \
(self.__direction == D_NORTH and self.__pos_y + self.__move + FIELD_SIZE <= HEIGHT) or \
(self.__direction == D_WEST and self.__pos_x - self.__move >= 0) or \
(self.__direction == D_SOUTH and self.__pos_x + self.__move + FIELD_SIZE <= WIDTH):
flag = True
return flag
def harvest_checked_fields(self, is_running: threading.Event) -> None:
while True:
moves = [M_GO_FORWARD, M_ROTATE_LEFT, M_ROTATE_RIGHT]
distribution = [0.6, 0.2, 0.2]
field = self.get_field_from_board()
self.__neural_network = NeuralNetwork()
prediction = self.__neural_network.check(field)
if prediction.capitalize() in CROPS:
self.harvest()
break
if not self.go_forward_is_legal_move():
moves = moves[1:]
distribution = distribution[1:]
chosen_move = random.choices(moves, distribution)
self.move_or_rotate(chosen_move[0])
time.sleep(1)
is_running.clear()
def choose_action(self) -> tuple[tuple[int, int], str]:
vectors = self.__board.convert_fields_to_vectors()
print(vectors)
coords = None
action = None
i_max = HORIZONTAL_NUM_OF_FIELDS
j_max = VERTICAL_NUM_OF_FIELDS
i_min = random.randint(0, HORIZONTAL_NUM_OF_FIELDS - 1)
j_min = random.randint(0, VERTICAL_NUM_OF_FIELDS - 1)
i = i_min
flag = True
while i < i_max and flag:
for j in range(j_min, j_max):
action = self.__tree.make_decision(self.__weather, vectors[i][j])
if action != A_DO_NOTHING:
coords = (i, j)
flag = False
break
i += 1
print(coords, action)
return coords, action