diff --git a/agent.py b/agent.py index 9553054..80c234d 100644 --- a/agent.py +++ b/agent.py @@ -11,17 +11,16 @@ SCREEN_SIZE = [800, 800] SQUARE_SIZE = 40 waiter = Waiter([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE) -objects = [ - Kitchen([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE) -] +kitchen = Kitchen([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE) +objects = [] for i in range(150): pos = [0, 0] - while any([o.compare_pos(pos) for o in objects]): - pos = [random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE), - random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE)] + while any([o.compare_pos(pos) for o in objects]) or pos == [0, 0]: + pos = [random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE - 1), + random.randint(1, SCREEN_SIZE[0]/SQUARE_SIZE - 1)] if (random.randint(0, 1)): objects.append(Block(pos, 0, SQUARE_SIZE, SCREEN_SIZE)) @@ -30,7 +29,7 @@ for i in range(150): user = UserController(waiter) state = StateController(waiter) -engine = Engine(SCREEN_SIZE, SQUARE_SIZE, user, state) +engine = Engine(SCREEN_SIZE, SQUARE_SIZE, kitchen, user, state) for o in objects: engine.subscribe(o) diff --git a/src/Engine.py b/src/Engine.py index a75a378..f648651 100644 --- a/src/Engine.py +++ b/src/Engine.py @@ -1,7 +1,9 @@ import time import pygame +from .obj.Goal import Goal from .obj.Object import Object from .obj.Waiter import Waiter +from .obj.Kitchen import Kitchen from .UserController import UserController from .StateController import StateController from .decisionTree.TreeConcept import TreeEngine @@ -10,12 +12,13 @@ from queue import PriorityQueue class Engine: - def __init__(self, screen_size, square_size, user: UserController, state: StateController): + def __init__(self, screen_size, square_size, kitchen: Kitchen, user: UserController, state: StateController): pygame.display.set_caption('Waiter Agent') self.action_clock = 0 self.tree = TreeEngine() + self.kitchen: Kitchen = kitchen self.user: Waiter = user self.state: StateController = state self.screen_size: list[int] = screen_size @@ -79,8 +82,13 @@ class Engine: # waiter interaction for o in self.objects: + if self.user.obj.chechNeighbor(o): + o.updateState(self.action_clock) if o.compare_pos(self.user.obj.position): - o.action(self.user.obj) + o.action(self.user.obj, self.action_clock) + + if self.kitchen.compare_pos(self.user.obj.position): + self.kitchen.action(self.user.obj, self.action_clock) time.sleep(0.5) @@ -97,6 +105,7 @@ class Engine: for o in self.objects: o.blit(self.screen) + self.kitchen.blit(self.screen) self.user.obj.blit(self.screen) for f in self.state.fringe.queue: @@ -105,10 +114,13 @@ class Engine: for s in self.state.path: s.blit(self.screen) + if self.goals: + self.goals[-1].blit(self.screen) + pygame.display.flip() def appendGoalPosition(self, position): - self.goals.append(position) + self.goals.append(Goal(position, self.square_size, self.screen_size)) def predict(self): @@ -117,6 +129,7 @@ class Engine: for o in self.objects: condition = o.agent_role in [ + "table", "order", "wait", "done" @@ -125,33 +138,48 @@ class Engine: if not condition or o.compare_pos(self.user.obj.position): continue - battery = self.user.obj.battary_status()[0] - distance = o.distance_to( - self.user.obj.position) // (self.num_squares // 2) - mood = o.get_mood(self.action_clock)[0] if condition else 0 - memory = self.user.obj.memory_size - dishes_held = self.user.obj.basket_size - customers = o.customers if condition else 0 - waiting_for_order = o.is_order() if condition else 0 - waiting_for_dish = o.is_done() if condition else 0 + medium_dist = (self.screen_size[0] // self.square_size) // 2 - p = self.tree.make_predict( - battery, - distance, - mood, - memory, - dishes_held, - customers, - waiting_for_order, - waiting_for_dish - ) + dataset = [ + # battery + self.user.obj.battery_status(), + # high | low | + # distance between kitchen and object + 0 if o.distance_to(self.kitchen.position) > medium_dist else 1, + # far | close | + + # mood  + o.get_mood(self.action_clock), + # undefined | good | bad | + + # basket is empty + 1 if self.user.obj.basket_is_empty() else 0, + # yes | no | + + # dish is ready + 1 if o.dish_is_ready(self.action_clock) else 0, + # yes | no | + + # dish in basket + 1 if self.user.obj.dish_in_basket(o) else 0, + # yes | no | + + # status + o.get_state_number(), + # empty | new order | waiting for dish | have a dish | + + # is actual + 1 if o.isActual() else 0, + # yes | no | + ] + + p = self.tree.make_predict(dataset) goal_queue.put((p, o.position)) - if len(goal_queue.queue): - priority, goal = goal_queue.queue.pop() - if priority: - print(goal, priority, end='\r') - self.appendGoalPosition(goal) + if goal_queue.queue: + priority, goal = goal_queue.queue[0] + if priority == 2: + self.appendGoalPosition(self.kitchen.position) else: - print(goal, priority, end='\r') + self.appendGoalPosition(goal) diff --git a/src/StateController.py b/src/StateController.py index 285cdd4..e82f23e 100644 --- a/src/StateController.py +++ b/src/StateController.py @@ -15,9 +15,10 @@ class StateController: self.explored.clear() self.fringe = PriorityQueue() - def build_path(self, goal_state): + def build_path(self, goal_state, engine): total_cost = goal_state.cost self.path.append(goal_state) + engine.goals.pop() while self.path[-1].parent.agent_role not in ["blank", "waiter"]: self.path.append(self.path[-1].parent) total_cost += self.path[-1].cost @@ -28,7 +29,7 @@ class StateController: def graphsearch(self, engine): # A* print("Search path") - self.goal = list(engine.goals.pop()) + self.goal = list(engine.goals[-1].position) self.reset() @@ -36,13 +37,10 @@ class StateController: self.fringe.put(start) - while self.fringe and not self.path: + while self.fringe.queue and not self.path: self.explored.append(self.fringe.get()) - - if self.explored[-1].position == self.goal: - goal_state = self.explored[-1] - self.reset() - return self.build_path(goal_state) + if self.goal_test(engine): + return self.succ(self.explored[-1].front(), engine) self.succ(self.explored[-1].left(), engine) @@ -51,6 +49,10 @@ class StateController: engine.redraw() self.reset() + for o in engine.objects: + o.compare_pos(engine.goals[-1].position) + o.agent_role = "block" + engine.goals.pop() print("Not found") @@ -90,3 +92,19 @@ class StateController: if state.cost_so_far < fringe.cost_so_far: fringe.replace(state) + + def goal_test(self, engine) -> bool: + if self.explored[-1].position == self.goal: + self.__is_goal__(self.explored[-1], engine) + return True + + for fringe in self.fringe.queue: + if fringe.position == self.goal: + self.__is_goal__(fringe, engine) + return True + + return False + + def __is_goal__(self, goal_state, engine): + self.reset() + self.build_path(goal_state, engine) diff --git a/src/UserController.py b/src/UserController.py index d9d6e70..35768a3 100644 --- a/src/UserController.py +++ b/src/UserController.py @@ -9,10 +9,3 @@ class UserController: for event in pygame.event.get(): if event.type == pygame.QUIT: engine.quit() - elif event.type == pygame.MOUSEBUTTONDOWN: - pos = pygame.mouse.get_pos() - 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) diff --git a/src/img/goal.png b/src/img/goal.png new file mode 100644 index 0000000..24dbf98 Binary files /dev/null and b/src/img/goal.png differ diff --git a/src/obj/Goal.py b/src/obj/Goal.py new file mode 100644 index 0000000..183598f --- /dev/null +++ b/src/obj/Goal.py @@ -0,0 +1,9 @@ +from src.obj.Object import Object + + +class Goal(Object): + def __init__(self, position, square_size, screen_size): + super().__init__("goal", position, 0, square_size, screen_size) + + def collide_test(self, waiter: Object) -> bool: + return waiter.position == self.position diff --git a/src/obj/Kitchen.py b/src/obj/Kitchen.py index 971f6df..3dd04f7 100644 --- a/src/obj/Kitchen.py +++ b/src/obj/Kitchen.py @@ -5,6 +5,6 @@ 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() + def action(self, waiter, current_time): + waiter.combine_orders(current_time) waiter.recharge() diff --git a/src/obj/Object.py b/src/obj/Object.py index d4fdd30..24dca0d 100644 --- a/src/obj/Object.py +++ b/src/obj/Object.py @@ -57,3 +57,6 @@ class Object: def action(self, obj): pass + + def updateState(self, current_time): + pass diff --git a/src/obj/Table.py b/src/obj/Table.py index c254642..d9938a6 100644 --- a/src/obj/Table.py +++ b/src/obj/Table.py @@ -4,29 +4,59 @@ from src.obj.Object import Object class Table(Object): 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"] + super().__init__("table", position, orientation, square_size, screen_size) + self.waiting_time = 0 + self.cooking_time = 0 + self.is_actual = False - def reset(self): - self.change_role("table") - self.order_time = 0 - self.customers = 0 + def isActual(self): + return self.is_actual + + def updateState(self, current_time): + if self.is_actual: + return + self.is_actual = True + # here must be neural network choise + new_role = random.choice(["table", "order", "wait", "done"]) + self.change_role(new_role, current_time) + + if self.agent_role == "table": + return + elif self.agent_role == "wait": + self.cooking_time = random.randint(0, 300) + + def dish_is_ready(self, current_time): + return current_time - self.waiting_time > self.cooking_time + + def get_state_number(self) -> int: + roles = { + "table": 0, + "order": 1, + "wait": 2, + "done": 3 + } + + return roles[self.agent_role] + + def change_role(self, new_role, current_time): + self.waiting_time = current_time + return super().change_role(new_role) + + def reset(self, current_time): + self.is_actual = False + self.change_role("table", current_time) 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 + self.change_role("order", current_time) - def set_wait(self): + def set_wait(self, current_time): if self.agent_role == "order": - self.change_role("wait") + self.change_role("wait", current_time) - def set_done(self): + def set_done(self, current_time): if self.agent_role == "wait": - self.change_role("done") + self.change_role("done", current_time) def is_order(self): return self.agent_role == "order" @@ -40,20 +70,15 @@ class Table(Object): def get_customers_count(self) -> int: return self.customers - def get_mood(self, current_time) -> str: + def get_mood(self, current_time) -> int: # перапісаць if self.agent_role == "table": - return None + return 2 # undefined - diff = current_time - self.order_time - if diff < 100: - return (0, "good") - elif diff < 200: - return (1, "medium") - else: - return (2, "bad") + diff = current_time - self.waiting_time + return 0 if diff >= 300 else 1 # 0 - bad; 1 - good - def action(self, waiter): + def action(self, waiter, current_time): if self.is_order(): - waiter.collect_order(self) + waiter.collect_order(self, current_time) elif self.is_done(): - waiter.deliver_dish(self) + waiter.deliver_dish(self, current_time) diff --git a/src/obj/Waiter.py b/src/obj/Waiter.py index fb52b48..47fab14 100644 --- a/src/obj/Waiter.py +++ b/src/obj/Waiter.py @@ -26,17 +26,20 @@ class Waiter(Object): def basket_is_full(self) -> bool: return self.basket_size == 0 - def combine_orders(self): + def basket_is_empty(self) -> bool: + return self.basket_size == 4 + + def combine_orders(self, current_time): while not self.basket_is_full() and not self.memory_is_empty(): dish = self.memory.pop() - dish.set_done() + dish.set_done(current_time) self.basket.append(dish) self.basket_size -= 1 self.memory_size += 1 - def deliver_dish(self, table): + def deliver_dish(self, table, current_time): if table in self.basket: - table.reset() + table.reset(current_time) self.basket.remove(table) self.basket_size += 1 @@ -49,21 +52,16 @@ class Waiter(Object): def memory_is_full(self) -> bool: return self.memory_size == 0 - def collect_order(self, table): + def collect_order(self, table, current_time): if self.memory_is_full(): return if table.agent_role == "order": - table.set_wait() + table.set_wait(current_time) self.memory.append(table) self.memory_size -= 1 - def battary_status(self) -> str: - if self.battery >= 200: - return (2, "hight") - elif self.battery >= 100: - return (1, "medium") - else: - return (0, "low") + def battery_status(self) -> int: + return 1 if self.battery >= 100 else 0 def recharge(self): self.battery = 300 @@ -79,3 +77,10 @@ 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 chechNeighbor(self, n, r=1): + + cond_x = abs(self.position[0] - n.position[0]) <= r + cond_y = abs(self.position[1] - n.position[1]) <= r + + return cond_x and cond_y