From 51bab9921dbb4a3faf1a895d2929e111769d1aec Mon Sep 17 00:00:00 2001 From: Vadzim Valchkovich Date: Fri, 26 May 2023 03:02:16 +0200 Subject: [PATCH] code refactoring, preparation to decision tree integration --- README.MD | 2 +- agent.py | 5 ++- src/Engine.py | 32 ++++++++++++++---- src/StateController.py | 2 +- src/UserController.py | 7 ++-- src/obj/Kitchen.py | 4 +++ src/obj/Object.py | 11 ++++++ src/obj/Table.py | 66 +++++++++++++++++++++++++++++++----- src/obj/TemporaryState.py | 10 ++---- src/obj/Waiter.py | 71 ++++++++++++++++++++++++++++----------- 10 files changed, 160 insertions(+), 50 deletions(-) diff --git a/README.MD b/README.MD index 1908236..2a6bec2 100644 --- a/README.MD +++ b/README.MD @@ -44,5 +44,5 @@ - [ ] **Drzewa decyzyjne: wymagania dot. trzeciego przyrostu** - [ ] Należy wykorzystać algorytm ID3 (tj. schemat indukcyjnego uczenia drzewa decyzyjnego oraz procedurę wyboru atrybutu o największym przyroście informacji) lub któreś z jego uogólnień. - [ ] Należy przygotować zbiór uczący złożony z co najmniej 200 przykładów. - - [ ] Decyzja stanowiąca cel uczenia powinna zostać opisana przynajmniej ośmioma atrybutami. + - [x] Decyzja stanowiąca cel uczenia powinna zostać opisana przynajmniej ośmioma atrybutami. - [ ] Powinna pojawić się opcja podglądu wyuczonego drzewa (np. w logach lub w pliku z graficzną reprezentacją drzewa). diff --git a/agent.py b/agent.py index 4315dbe..9553054 100644 --- a/agent.py +++ b/agent.py @@ -7,7 +7,7 @@ from src.obj.Table import Table from src.UserController import UserController from src.StateController import StateController -SCREEN_SIZE = (800, 800) +SCREEN_SIZE = [800, 800] SQUARE_SIZE = 40 waiter = Waiter([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE) @@ -26,8 +26,7 @@ for i in range(150): if (random.randint(0, 1)): objects.append(Block(pos, 0, SQUARE_SIZE, SCREEN_SIZE)) else: - objects.append(Table(pos, 0, SQUARE_SIZE, - SCREEN_SIZE, random.randint(0, 3))) + objects.append(Table(pos, 0, SQUARE_SIZE, SCREEN_SIZE)) user = UserController(waiter) state = StateController(waiter) diff --git a/src/Engine.py b/src/Engine.py index f25efa1..e020f39 100644 --- a/src/Engine.py +++ b/src/Engine.py @@ -1,6 +1,7 @@ import time import pygame from .obj.Object import Object +from .obj.Waiter import Waiter from .UserController import UserController from .StateController import StateController @@ -10,12 +11,14 @@ class Engine: def __init__(self, screen_size, square_size, user: UserController, state: StateController): pygame.display.set_caption('Waiter Agent') - self.user = user - self.state = state - self.screen_size = screen_size + self.action_clock = 0 + + self.user: Waiter = user + self.state: StateController = state + self.screen_size: list[int] = screen_size self.screen = pygame.display.set_mode(self.screen_size) - self.square_size = square_size + self.square_size: int = square_size self.num_squares = self.screen_size[0] // self.square_size self.squares = self.__init_squares_field__( self.num_squares, self.square_size) @@ -23,7 +26,7 @@ class Engine: self.objects: list[Object] = [] self.goals: list = [] - self.runnin = False + self.runnin: bool = False def __init_squares_field__(self, num_squares, square_size): squares = [] @@ -57,14 +60,29 @@ class Engine: self.state.graphsearch(self) self.user.handler(self) else: + # went path + state = self.user.obj.changeState(self.state.path.pop()) - print("Action:\t{0}\tCost:\t{1}\tCost so far: {2}".format( + self.clock_increment(state.cost) + + print("Action:\t{0}\tCost:\t{1}\tCost so far: {2}\tBattery: {3}".format( state.agent_role, state.cost, - state.cost_so_far) + state.cost_so_far, + self.user.obj.battery) ) + + # waiter interaction + + for o in self.objects: + if o.compare_pos(self.user.obj.position): + o.action(self.user.obj) + time.sleep(0.5) + def clock_increment(self, action_time): + self.action_clock += action_time + def redraw(self): self.screen.fill((255, 255, 255)) diff --git a/src/StateController.py b/src/StateController.py index a7f7c88..285cdd4 100644 --- a/src/StateController.py +++ b/src/StateController.py @@ -18,7 +18,7 @@ class StateController: def build_path(self, goal_state): total_cost = goal_state.cost self.path.append(goal_state) - while self.path[-1].parent.agent_role != "blank": + while self.path[-1].parent.agent_role not in ["blank", "waiter"]: self.path.append(self.path[-1].parent) total_cost += self.path[-1].cost diff --git a/src/UserController.py b/src/UserController.py index 5df3156..1136df3 100644 --- a/src/UserController.py +++ b/src/UserController.py @@ -11,6 +11,9 @@ class UserController: engine.quit() elif event.type == pygame.MOUSEBUTTONDOWN: pos = pygame.mouse.get_pos() - pos = (pos[0] // engine.square_size, - pos[1] // engine.square_size) + pos = [pos[0] // engine.square_size, + pos[1] // engine.square_size] + # for o in engine.objects: + # if o.compare_pos(pos): + # o.set_order(engine.action_clock) engine.appendGoalPosition(pos) diff --git a/src/obj/Kitchen.py b/src/obj/Kitchen.py index 120e5be..971f6df 100644 --- a/src/obj/Kitchen.py +++ b/src/obj/Kitchen.py @@ -4,3 +4,7 @@ from src.obj.Object import Object class Kitchen(Object): def __init__(self, position, orientation, square_size, screen_size): super().__init__("kitchen", position, orientation, square_size, screen_size) + + def action(self, waiter): + waiter.combine_orders() + waiter.recharge() diff --git a/src/obj/Object.py b/src/obj/Object.py index 82c97da..d4fdd30 100644 --- a/src/obj/Object.py +++ b/src/obj/Object.py @@ -46,3 +46,14 @@ class Object: def compare_pos(self, pos) -> bool: return self.position == pos + + def distance_to(self, pos) -> int: + x = abs(self.position[0] - pos[0]) + y = abs(self.position[1] - pos[1]) + + self.h = x + y + + return self.h + + def action(self, obj): + pass diff --git a/src/obj/Table.py b/src/obj/Table.py index 4173881..d6b58f1 100644 --- a/src/obj/Table.py +++ b/src/obj/Table.py @@ -1,14 +1,62 @@ +import random from src.obj.Object import Object class Table(Object): - def __init__(self, position, orientation, square_size, screen_size, current_role=0): - super().__init__("table", position, orientation, square_size, screen_size) - self.roles = ["table", "order", "wait", "done"] - self.current_role = current_role - self.change_role(self.roles[self.current_role]) + def __init__(self, position, orientation, square_size, screen_size): + super().__init__("order", position, orientation, square_size, screen_size) + self.order_time = 0 + self.customers = 0 + # roles = ["table", "order", "wait", "done"] - def next_role(self, waiter): - if waiter.agent_role == "waiter": - self.current_role = (self.current_role + 1) % 4 - self.change_role(self.roles[self.current_role]) + def reset(self): + self.change_role("table") + self.order_time = 0 + self.customers = 0 + + def set_order(self, current_time): + if self.agent_role == "table": + self.change_role("order") + self.customers = random.randint(1, 6) + self.order_time = current_time + + def set_wait(self): + if self.agent_role == "order": + self.change_role("wait") + + def set_done(self): + if self.agent_role == "wait": + self.change_role("done") + + def is_order(self): + return self.agent_role == "order" + + def is_wait(self): + return self.agent_role == "wait" + + def is_done(self): + return self.agent_role == "done" + + def waiting_for_dish(self): + return self.agent_role in ["wait", "done"] + + def get_customers_count(self) -> int: + return self.customers + + def get_mood(self, current_time) -> str: + if self.agent_role == "table": + return None + + diff = current_time - self.order_time + if diff < 200: + return "good" + elif diff < 400: + return "medium" + else: + return "bad" + + def action(self, waiter): + if self.is_order(): + waiter.collect_order(self) + elif self.is_done(): + waiter.deliver_dish(self) diff --git a/src/obj/TemporaryState.py b/src/obj/TemporaryState.py index 2d683c2..b04c88d 100644 --- a/src/obj/TemporaryState.py +++ b/src/obj/TemporaryState.py @@ -11,11 +11,11 @@ class TemporaryState(Waiter): copy.copy(parent.basket)) self.agent_role = action self.parent = parent - self.change_role(action) - self.apply_transformation() self.cost = cost self.cost_so_far = cost_so_far self.h = h + self.change_role(action) + self.apply_transformation() def replace(self, repl): self.cost_so_far = copy.copy(repl.cost_so_far) @@ -76,11 +76,7 @@ class TemporaryState(Waiter): self.cost = costs[obj.agent_role] def heuristic(self, goal): - x = abs(self.position[0] - goal[0]) - y = abs(self.position[1] - goal[1]) - - self.h = x + y - + self.h = self.distance_to(goal) return self.h def current_cost(self): diff --git a/src/obj/Waiter.py b/src/obj/Waiter.py index 2c4d232..04a9be4 100644 --- a/src/obj/Waiter.py +++ b/src/obj/Waiter.py @@ -3,10 +3,13 @@ from src.obj.Object import Object class Waiter(Object): - def __init__(self, position, orientation, square_size, screen_size, basket=[]): + def __init__(self, position, orientation, square_size, screen_size, basket=[], memory=[], battery=300): super().__init__("waiter", position, orientation, square_size, screen_size) - self.basket_size = 2 + self.battery = battery + self.basket_size = 4 + self.memory_size = 4 self.basket = basket + self.memory = memory self.prev_position = copy.deepcopy(self.position) self.prev_orientation = copy.copy(self.orientation) @@ -14,18 +17,56 @@ class Waiter(Object): self.position = copy.deepcopy(state.position) self.orientation = copy.copy(state.orientation) self.basket = copy.copy(state.basket) + self.battery -= state.cost return state - def dampState(self): - self.prev_position = copy.deepcopy(self.position) - self.prev_orientation = copy.copy(self.orientation) + def dish_in_basket(self, table) -> bool: + return table in self.basket - def rollbackState(self): - self.position = copy.deepcopy(self.prev_position) - self.orientation = copy.copy(self.prev_orientation) + def basket_is_full(self) -> bool: + return self.basket_size == 0 - def orders_in_basket(self) -> bool: - return self.basket + def combine_orders(self): + while not self.basket_is_full() and not self.memory_is_empty(): + dish = self.memory.pop() + dish.set_done() + self.basket.append(dish) + self.basket_size -= 1 + self.memory_size += 1 + + def deliver_dish(self, table): + if table in self.basket: + table.reset() + self.basket.remove(table) + self.basket_size += 1 + + def order_in_memory(self, table) -> bool: + return table in self.memory + + def memory_is_empty(self) -> bool: + return not self.memory + + def memory_is_full(self) -> bool: + return self.memory_size == 0 + + def collect_order(self, table): + if self.memory_is_full(): + return + if table.agent_role == "order": + table.set_wait() + self.memory.append(table) + self.memory_size -= 1 + + def battary_status(self) -> str: + if self.battery >= 200: + return "hight" + elif self.battery >= 100: + return "medium" + else: + return "low" + + def recharge(self): + self.battery = 300 def left(self): self.orientation = (self.orientation + 1) % 4 @@ -38,13 +79,3 @@ class Waiter(Object): self.position[0] += self.orientation - 2 # x (-1 or +1) else: # y (0 or 2) self.position[1] += self.orientation - 1 # y (-1 or +1) - - def collide_test(self, obj) -> bool: - out_of_range = [ - self.position[0] >= self.square_count, - self.position[1] >= self.square_count, - self.position[0] < 0, - self.position[1] < 0 - ] - - return any(out_of_range)