Merge branch 'dungeon_master' of https://git.wmi.amu.edu.pl/s474137/automatyczny_kelner into dungeon_master
30
README.MD
@ -17,4 +17,32 @@
|
|||||||
|
|
||||||
## RUN INSTRUCTIONS
|
## RUN INSTRUCTIONS
|
||||||
|
|
||||||
pipenv run python main.py
|
pipenv run python agent.py
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [x] **Planowanie ruchu: Wymagania dot. pierwszego przyrostu**
|
||||||
|
|
||||||
|
- [x] Agent powinien dysponować co najmniej następującymi akcjami: ruch do przodu, obrót w lewo, obrót w prawo
|
||||||
|
- [x] Należy wykorzystać „Schemat procedury przeszukiwania grafu stanów“.
|
||||||
|
- [x] Należy zaimplementować strategię Breadth-First Search.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [x] **Planowanie ruchu: Wymagania dot. drugiego przyrostu**
|
||||||
|
- [x] Należy wykorzystać „Schemat procedury przeszukiwania grafu stanów z uwzględnieniem kosztu“
|
||||||
|
- [x] Należy zaimplementować strategię A\*, czyli zdefiniować funkcję wyznaczającą priorytet następników uwzględniającą zarówno koszt jak i odpowiednią heurystykę.
|
||||||
|
- [x] Agent powinien dysponować co najmniej następującymi akcjami: ruch do przodu, obrót w lewo, obrót w prawo.
|
||||||
|
- [x] Koszt wjazdu na pola poszczególnych typów powinien być zróżnicowany.
|
||||||
|
|
||||||
|
> _Przykład: Koszt wjazdu traktora na pole marchewek to 10 a koszt wjazdu na pole puste to 1._
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] **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.
|
||||||
|
- [ ] Powinna pojawić się opcja podglądu wyuczonego drzewa (np. w logach lub w pliku z graficzną reprezentacją drzewa).
|
||||||
|
39
agent.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import random
|
||||||
|
from src.Engine import Engine
|
||||||
|
from src.obj.Waiter import Waiter
|
||||||
|
from src.obj.Block import Block
|
||||||
|
from src.obj.Kitchen import Kitchen
|
||||||
|
from src.obj.Table import Table
|
||||||
|
from src.UserController import UserController
|
||||||
|
from src.StateController import StateController
|
||||||
|
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
|
||||||
|
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)]
|
||||||
|
|
||||||
|
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)))
|
||||||
|
|
||||||
|
user = UserController(waiter)
|
||||||
|
state = StateController(waiter)
|
||||||
|
engine = Engine(SCREEN_SIZE, SQUARE_SIZE, user, state)
|
||||||
|
|
||||||
|
for o in objects:
|
||||||
|
engine.subscribe(o)
|
||||||
|
|
||||||
|
engine.loop()
|
76
main.py
@ -1,76 +0,0 @@
|
|||||||
import pygame
|
|
||||||
import random
|
|
||||||
|
|
||||||
from models.Kitchen import Kitchen
|
|
||||||
from models.Table import Table
|
|
||||||
from models.Waiter import Waiter
|
|
||||||
|
|
||||||
pygame.init()
|
|
||||||
|
|
||||||
|
|
||||||
screen_size = (600, 600)
|
|
||||||
screen = pygame.display.set_mode(screen_size)
|
|
||||||
|
|
||||||
|
|
||||||
square_size = 50
|
|
||||||
num_squares = screen_size[0] // square_size
|
|
||||||
|
|
||||||
|
|
||||||
squares = []
|
|
||||||
for i in range(num_squares):
|
|
||||||
row = []
|
|
||||||
for j in range(num_squares):
|
|
||||||
square_rect = pygame.Rect(
|
|
||||||
j * square_size, i * square_size, square_size, square_size)
|
|
||||||
row.append(square_rect)
|
|
||||||
squares.append(row)
|
|
||||||
|
|
||||||
|
|
||||||
tables = [
|
|
||||||
Table(square_size, screen_size, 2, 4),
|
|
||||||
Table(square_size, screen_size, 6, 5),
|
|
||||||
Table(square_size, screen_size, 4, 2),
|
|
||||||
Table(square_size, screen_size, 5, 6),
|
|
||||||
Table(square_size, screen_size, 4, 4),
|
|
||||||
]
|
|
||||||
kitchen = Kitchen(square_size, screen_size, 0, 0)
|
|
||||||
waiter = Waiter(square_size, screen_size, 0, 0)
|
|
||||||
|
|
||||||
running = True
|
|
||||||
while running:
|
|
||||||
|
|
||||||
for event in pygame.event.get():
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
running = False
|
|
||||||
elif event.type == pygame.KEYDOWN:
|
|
||||||
if event.key == pygame.K_UP:
|
|
||||||
waiter.up()
|
|
||||||
elif event.key == pygame.K_DOWN:
|
|
||||||
waiter.down()
|
|
||||||
elif event.key == pygame.K_LEFT:
|
|
||||||
waiter.left()
|
|
||||||
elif event.key == pygame.K_RIGHT:
|
|
||||||
waiter.right()
|
|
||||||
elif event.key == pygame.K_ESCAPE:
|
|
||||||
for table in tables:
|
|
||||||
table.new_order()
|
|
||||||
|
|
||||||
screen.fill((255, 255, 255))
|
|
||||||
|
|
||||||
for row in squares:
|
|
||||||
for square_rect in row:
|
|
||||||
pygame.draw.rect(screen, (0, 0, 0), square_rect, 1)
|
|
||||||
|
|
||||||
for table in tables:
|
|
||||||
table.blit(screen)
|
|
||||||
kitchen.blit(screen)
|
|
||||||
waiter.blit(screen)
|
|
||||||
|
|
||||||
for table in tables:
|
|
||||||
if table.collision(waiter):
|
|
||||||
waiter.do_smth(table)
|
|
||||||
|
|
||||||
if kitchen.collision(waiter):
|
|
||||||
kitchen.take_orders(waiter)
|
|
||||||
|
|
||||||
pygame.display.flip()
|
|
@ -1,18 +0,0 @@
|
|||||||
import pygame
|
|
||||||
from models.Object import Object
|
|
||||||
from models.Waiter import Waiter
|
|
||||||
|
|
||||||
|
|
||||||
class Kitchen(Object):
|
|
||||||
def __init__(self, square_size, screen_size, left_square, top_square):
|
|
||||||
super().__init__(
|
|
||||||
'kitchen',
|
|
||||||
square_size,
|
|
||||||
screen_size,
|
|
||||||
left_square,
|
|
||||||
top_square
|
|
||||||
)
|
|
||||||
|
|
||||||
def take_orders(self, waiter: Waiter):
|
|
||||||
for table in waiter.get_order_list():
|
|
||||||
table.done_order()
|
|
@ -1,39 +0,0 @@
|
|||||||
import pygame
|
|
||||||
|
|
||||||
|
|
||||||
class Object:
|
|
||||||
def __init__(self, role, square_size, screen_size, left_square, top_square):
|
|
||||||
self.role = role
|
|
||||||
self.image = pygame.transform.scale(pygame.image.load(
|
|
||||||
'images/{0}.png'.format(role)), (square_size, square_size))
|
|
||||||
|
|
||||||
self.square_size = square_size
|
|
||||||
self.screen_size = screen_size
|
|
||||||
|
|
||||||
left = left_square * square_size
|
|
||||||
top = top_square * square_size
|
|
||||||
self.rect = pygame.Rect(left, top, square_size, square_size)
|
|
||||||
|
|
||||||
def up(self):
|
|
||||||
if self.rect.top > 0:
|
|
||||||
self.rect.top -= self.square_size
|
|
||||||
|
|
||||||
def down(self):
|
|
||||||
if self.rect.bottom < self.screen_size[1]:
|
|
||||||
self.rect.top += self.square_size
|
|
||||||
|
|
||||||
def left(self):
|
|
||||||
if self.rect.left > 0:
|
|
||||||
self.rect.left -= self.square_size
|
|
||||||
|
|
||||||
def right(self):
|
|
||||||
if self.rect.right < self.screen_size[0]:
|
|
||||||
self.rect.left += self.square_size
|
|
||||||
|
|
||||||
def blit(self, screen):
|
|
||||||
screen.blit(self.image, self.rect)
|
|
||||||
|
|
||||||
def collision(self, obj):
|
|
||||||
x = self.rect.left == obj.rect.left
|
|
||||||
y = self.rect.top == obj.rect.top
|
|
||||||
return x and y
|
|
@ -1,41 +0,0 @@
|
|||||||
import pygame
|
|
||||||
from models.Object import Object
|
|
||||||
|
|
||||||
|
|
||||||
class Table(Object):
|
|
||||||
def __init__(self, square_size, screen_size, left_square, top_square):
|
|
||||||
super().__init__(
|
|
||||||
'table',
|
|
||||||
square_size,
|
|
||||||
screen_size,
|
|
||||||
left_square,
|
|
||||||
top_square
|
|
||||||
)
|
|
||||||
|
|
||||||
self.state = 'table'
|
|
||||||
|
|
||||||
def new_order(self):
|
|
||||||
self.state = 'order'
|
|
||||||
self.update_pic()
|
|
||||||
|
|
||||||
def wait_order(self):
|
|
||||||
self.state = 'wait'
|
|
||||||
self.update_pic()
|
|
||||||
|
|
||||||
def done_order(self):
|
|
||||||
self.state = 'done'
|
|
||||||
self.update_pic()
|
|
||||||
|
|
||||||
def reset_order(self):
|
|
||||||
self.state = 'table'
|
|
||||||
self.update_pic()
|
|
||||||
|
|
||||||
def is_order(self) -> bool:
|
|
||||||
return self.state == 'order'
|
|
||||||
|
|
||||||
def is_done(self) -> bool:
|
|
||||||
return self.state == 'done'
|
|
||||||
|
|
||||||
def update_pic(self):
|
|
||||||
self.image = pygame.transform.scale(pygame.image.load(
|
|
||||||
'images/{0}.png'.format(self.state)), (self.square_size, self.square_size))
|
|
@ -1,44 +0,0 @@
|
|||||||
import pygame
|
|
||||||
from models.Object import Object
|
|
||||||
from models.Table import Table
|
|
||||||
|
|
||||||
|
|
||||||
class Waiter(Object):
|
|
||||||
def __init__(self, square_size, screen_size, left_square, top_square):
|
|
||||||
super().__init__(
|
|
||||||
'waiter',
|
|
||||||
square_size,
|
|
||||||
screen_size,
|
|
||||||
left_square,
|
|
||||||
top_square
|
|
||||||
)
|
|
||||||
self.orders_limit = 3
|
|
||||||
self.orders_list = []
|
|
||||||
|
|
||||||
def do_smth(self, table: Table):
|
|
||||||
if table.is_order():
|
|
||||||
self.take_order(table)
|
|
||||||
elif table.is_done():
|
|
||||||
self.deliver_order(table)
|
|
||||||
|
|
||||||
def take_order(self, table: Table):
|
|
||||||
if self.orders_limit <= 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not table.is_order():
|
|
||||||
return
|
|
||||||
|
|
||||||
self.orders_limit -= 1
|
|
||||||
self.orders_list.append(table)
|
|
||||||
|
|
||||||
table.wait_order()
|
|
||||||
|
|
||||||
def deliver_order(self, table: Table):
|
|
||||||
if table.is_done() and table in self.orders_list:
|
|
||||||
self.orders_limit += 1
|
|
||||||
self.orders_list.remove(table)
|
|
||||||
|
|
||||||
table.reset_order()
|
|
||||||
|
|
||||||
def get_order_list(self) -> list[Table]:
|
|
||||||
return self.orders_list
|
|
89
src/Engine.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import time
|
||||||
|
import pygame
|
||||||
|
from .obj.Object import Object
|
||||||
|
from .UserController import UserController
|
||||||
|
from .StateController import StateController
|
||||||
|
|
||||||
|
|
||||||
|
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.screen = pygame.display.set_mode(self.screen_size)
|
||||||
|
|
||||||
|
self.square_size = square_size
|
||||||
|
self.num_squares = self.screen_size[0] // self.square_size
|
||||||
|
self.squares = self.__init_squares_field__(
|
||||||
|
self.num_squares, self.square_size)
|
||||||
|
|
||||||
|
self.objects: list[Object] = []
|
||||||
|
self.goals: list = []
|
||||||
|
|
||||||
|
self.runnin = False
|
||||||
|
|
||||||
|
def __init_squares_field__(self, num_squares, square_size):
|
||||||
|
squares = []
|
||||||
|
for i in range(num_squares):
|
||||||
|
row = []
|
||||||
|
for j in range(num_squares):
|
||||||
|
square_rect = pygame.Rect(
|
||||||
|
j * square_size, i * square_size,
|
||||||
|
square_size, square_size)
|
||||||
|
row.append(square_rect)
|
||||||
|
squares.append(row)
|
||||||
|
|
||||||
|
return squares
|
||||||
|
|
||||||
|
def subscribe(self, object: Object):
|
||||||
|
self.objects.append(object)
|
||||||
|
|
||||||
|
def loop(self):
|
||||||
|
self.running = True
|
||||||
|
while self.running:
|
||||||
|
|
||||||
|
self.action()
|
||||||
|
self.redraw()
|
||||||
|
|
||||||
|
def quit(self):
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def action(self):
|
||||||
|
if not self.state.path:
|
||||||
|
if self.goals:
|
||||||
|
self.state.graphsearch(self)
|
||||||
|
self.user.handler(self)
|
||||||
|
else:
|
||||||
|
state = self.user.obj.changeState(self.state.path.pop())
|
||||||
|
print("Action:\t{0}\tCost:\t{1}\tCost so far: {2}".format(
|
||||||
|
state.agent_role,
|
||||||
|
state.cost,
|
||||||
|
state.cost_so_far)
|
||||||
|
)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
def redraw(self):
|
||||||
|
self.screen.fill((255, 255, 255))
|
||||||
|
|
||||||
|
for row in self.squares:
|
||||||
|
for square_rect in row:
|
||||||
|
pygame.draw.rect(self.screen, (0, 0, 0), square_rect, 1)
|
||||||
|
|
||||||
|
for o in self.objects:
|
||||||
|
o.blit(self.screen)
|
||||||
|
|
||||||
|
self.user.obj.blit(self.screen)
|
||||||
|
|
||||||
|
for f in self.state.fringe.queue:
|
||||||
|
f.blit(self.screen)
|
||||||
|
|
||||||
|
for s in self.state.path:
|
||||||
|
s.blit(self.screen)
|
||||||
|
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
def appendGoalPosition(self, position):
|
||||||
|
self.goals.append(position)
|
92
src/StateController.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
from .obj.TemporaryState import TemporaryState
|
||||||
|
from queue import PriorityQueue
|
||||||
|
|
||||||
|
|
||||||
|
class StateController:
|
||||||
|
def __init__(self, istate):
|
||||||
|
self.path = []
|
||||||
|
self.explored = []
|
||||||
|
self.fringe = PriorityQueue()
|
||||||
|
self.istate = istate
|
||||||
|
self.goal = istate.position
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.path.clear()
|
||||||
|
self.explored.clear()
|
||||||
|
self.fringe = PriorityQueue()
|
||||||
|
|
||||||
|
def build_path(self, goal_state):
|
||||||
|
total_cost = goal_state.cost
|
||||||
|
self.path.append(goal_state)
|
||||||
|
while self.path[-1].parent.agent_role != "blank":
|
||||||
|
self.path.append(self.path[-1].parent)
|
||||||
|
total_cost += self.path[-1].cost
|
||||||
|
|
||||||
|
print("Total path cost:\t{0}".format(total_cost))
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
def graphsearch(self, engine): # A*
|
||||||
|
print("Search path")
|
||||||
|
|
||||||
|
self.goal = list(engine.goals.pop())
|
||||||
|
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
start = TemporaryState(self.istate, 0)
|
||||||
|
|
||||||
|
self.fringe.put(start)
|
||||||
|
|
||||||
|
while self.fringe 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)
|
||||||
|
|
||||||
|
self.succ(self.explored[-1].front(), engine)
|
||||||
|
self.succ(self.explored[-1].left(), engine)
|
||||||
|
self.succ(self.explored[-1].right(), engine)
|
||||||
|
|
||||||
|
engine.redraw()
|
||||||
|
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
print("Not found")
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def succ(self, state, engine):
|
||||||
|
if state.collide_test():
|
||||||
|
return
|
||||||
|
elif any(e.compare(state) for e in self.explored):
|
||||||
|
return
|
||||||
|
elif any([o.collide_test(state) for o in engine.objects]):
|
||||||
|
return
|
||||||
|
|
||||||
|
for o in engine.objects:
|
||||||
|
if state.cost != 1:
|
||||||
|
break
|
||||||
|
if o.position == state.position:
|
||||||
|
state.change_cost(o)
|
||||||
|
|
||||||
|
state.cost_so_far = self.explored[-1].cost_so_far + state.cost
|
||||||
|
|
||||||
|
in_explored = any([state.compare(s) for s in self.explored]
|
||||||
|
)
|
||||||
|
|
||||||
|
in_frige = any([state.compare(f) for f in self.fringe.queue])
|
||||||
|
|
||||||
|
if not in_explored and not in_frige:
|
||||||
|
state.heuristic(self.goal)
|
||||||
|
self.fringe.put(state)
|
||||||
|
|
||||||
|
elif in_frige:
|
||||||
|
fringe = state
|
||||||
|
for f in self.fringe.queue:
|
||||||
|
if state.compare(f):
|
||||||
|
fringe = f
|
||||||
|
break
|
||||||
|
|
||||||
|
if state.cost_so_far < fringe.cost_so_far:
|
||||||
|
fringe.replace(state)
|
16
src/UserController.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import pygame
|
||||||
|
|
||||||
|
|
||||||
|
class UserController:
|
||||||
|
def __init__(self, usrObj):
|
||||||
|
self.obj = usrObj
|
||||||
|
|
||||||
|
def handler(self, engine):
|
||||||
|
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)
|
||||||
|
engine.appendGoalPosition(pos)
|
BIN
src/img/blank.png
Normal file
After Width: | Height: | Size: 142 B |
BIN
src/img/block.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
BIN
src/img/front.png
Normal file
After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
BIN
src/img/left.png
Normal file
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
src/img/right.png
Normal file
After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
9
src/obj/Block.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from src.obj.Object import Object
|
||||||
|
|
||||||
|
|
||||||
|
class Block(Object):
|
||||||
|
def __init__(self, position, orientation, square_size, screen_size):
|
||||||
|
super().__init__("block", position, orientation, square_size, screen_size)
|
||||||
|
|
||||||
|
def collide_test(self, waiter: Object) -> bool:
|
||||||
|
return waiter.position == self.position
|
6
src/obj/Kitchen.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
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)
|
48
src/obj/Object.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import pygame
|
||||||
|
|
||||||
|
|
||||||
|
class Object:
|
||||||
|
def __init__(self, agent_role, position, orientation, square_size, screen_size):
|
||||||
|
self.agent_role = agent_role
|
||||||
|
self.position = position
|
||||||
|
self.orientation = orientation % 4
|
||||||
|
self.square_size = square_size
|
||||||
|
self.screen_size = screen_size
|
||||||
|
self.square_count = screen_size[0] // square_size
|
||||||
|
|
||||||
|
self.image = pygame.image.load(
|
||||||
|
'src/img/{0}.png'.format(self.agent_role))
|
||||||
|
self.image = pygame.transform.scale(
|
||||||
|
self.image, (self.square_size, self.square_size))
|
||||||
|
|
||||||
|
self.rect = pygame.Rect(position[0] * square_size,
|
||||||
|
position[1] * square_size,
|
||||||
|
square_size, square_size)
|
||||||
|
|
||||||
|
def change_role(self, new_role):
|
||||||
|
self.agent_role = new_role
|
||||||
|
self.image = pygame.image.load('src/img/{0}.png'.format(new_role))
|
||||||
|
self.image = pygame.transform.scale(
|
||||||
|
self.image, (self.square_size, self.square_size))
|
||||||
|
|
||||||
|
def get_angle(self):
|
||||||
|
'''
|
||||||
|
orientation = 0 -> up\n
|
||||||
|
orientation = 1 -> left\n
|
||||||
|
orientation = 2 -> down\n
|
||||||
|
orientation = 3 -> right\n
|
||||||
|
'''
|
||||||
|
|
||||||
|
return self.orientation * 90
|
||||||
|
|
||||||
|
def collide_test(self, obj) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def blit(self, screen: pygame.Surface):
|
||||||
|
image = pygame.transform.rotate(self.image, self.get_angle())
|
||||||
|
self.rect.x = self.position[0] * self.square_size
|
||||||
|
self.rect.y = self.position[1] * self.square_size
|
||||||
|
screen.blit(image, self.rect)
|
||||||
|
|
||||||
|
def compare_pos(self, pos) -> bool:
|
||||||
|
return self.position == pos
|
14
src/obj/Table.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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 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])
|
102
src/obj/TemporaryState.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
from src.obj.Waiter import Waiter
|
||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
|
class TemporaryState(Waiter):
|
||||||
|
def __init__(self, parent, cost_so_far, action="blank", cost=0, h=0):
|
||||||
|
super().__init__(copy.deepcopy(parent.position),
|
||||||
|
copy.copy(parent.orientation),
|
||||||
|
copy.copy(parent.square_size),
|
||||||
|
copy.copy(parent.screen_size),
|
||||||
|
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
|
||||||
|
|
||||||
|
def replace(self, repl):
|
||||||
|
self.cost_so_far = copy.copy(repl.cost_so_far)
|
||||||
|
self.basket = copy.copy(repl.basket)
|
||||||
|
self.parent = repl.parent
|
||||||
|
|
||||||
|
def apply_transformation(self):
|
||||||
|
if self.agent_role == "left":
|
||||||
|
self.orientation = (self.orientation + 1) % 4
|
||||||
|
elif self.agent_role == "right":
|
||||||
|
self.orientation = (self.orientation - 1) % 4
|
||||||
|
elif self.agent_role == "front":
|
||||||
|
if self.orientation % 2: # x (1 or 3)
|
||||||
|
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 left(self):
|
||||||
|
return TemporaryState(self, self.cost_so_far, "left", 1)
|
||||||
|
|
||||||
|
def right(self):
|
||||||
|
return TemporaryState(self, self.cost_so_far, "right", 1)
|
||||||
|
|
||||||
|
def front(self):
|
||||||
|
return TemporaryState(self, self.cost_so_far, "front", 1)
|
||||||
|
|
||||||
|
def collide_test(self) -> 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)
|
||||||
|
|
||||||
|
def compare(self, state) -> bool:
|
||||||
|
conditions = [
|
||||||
|
self.position == state.position,
|
||||||
|
self.orientation == state.orientation
|
||||||
|
]
|
||||||
|
return all(conditions)
|
||||||
|
|
||||||
|
def change_cost(self, obj):
|
||||||
|
self.cost = 1 # default cost
|
||||||
|
|
||||||
|
if self.agent_role == "front":
|
||||||
|
|
||||||
|
costs = {
|
||||||
|
"kitchen": 5,
|
||||||
|
"table": 5,
|
||||||
|
"order": 20,
|
||||||
|
"wait": 10,
|
||||||
|
"done": 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.agent_role in costs.keys():
|
||||||
|
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
|
||||||
|
|
||||||
|
return self.h
|
||||||
|
|
||||||
|
def current_cost(self):
|
||||||
|
return self.cost_so_far + self.h
|
||||||
|
|
||||||
|
def __eq__(self, __value) -> bool:
|
||||||
|
return self.current_cost() == __value.current_cost()
|
||||||
|
|
||||||
|
def __lt__(self, __value) -> bool:
|
||||||
|
return self.current_cost() < __value.current_cost()
|
||||||
|
|
||||||
|
def __le__(self, __value) -> bool:
|
||||||
|
return self.current_cost() <= __value.current_cost()
|
||||||
|
|
||||||
|
def __gt__(self, __value) -> bool:
|
||||||
|
return self.current_cost() > __value.current_cost()
|
||||||
|
|
||||||
|
def __ge__(self, __value) -> bool:
|
||||||
|
return self.current_cost() >= __value.current_cost()
|
50
src/obj/Waiter.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import copy
|
||||||
|
from src.obj.Object import Object
|
||||||
|
|
||||||
|
|
||||||
|
class Waiter(Object):
|
||||||
|
def __init__(self, position, orientation, square_size, screen_size, basket=[]):
|
||||||
|
super().__init__("waiter", position, orientation, square_size, screen_size)
|
||||||
|
self.basket_size = 2
|
||||||
|
self.basket = basket
|
||||||
|
self.prev_position = copy.deepcopy(self.position)
|
||||||
|
self.prev_orientation = copy.copy(self.orientation)
|
||||||
|
|
||||||
|
def changeState(self, state):
|
||||||
|
self.position = copy.deepcopy(state.position)
|
||||||
|
self.orientation = copy.copy(state.orientation)
|
||||||
|
self.basket = copy.copy(state.basket)
|
||||||
|
return state
|
||||||
|
|
||||||
|
def dampState(self):
|
||||||
|
self.prev_position = copy.deepcopy(self.position)
|
||||||
|
self.prev_orientation = copy.copy(self.orientation)
|
||||||
|
|
||||||
|
def rollbackState(self):
|
||||||
|
self.position = copy.deepcopy(self.prev_position)
|
||||||
|
self.orientation = copy.copy(self.prev_orientation)
|
||||||
|
|
||||||
|
def orders_in_basket(self) -> bool:
|
||||||
|
return self.basket
|
||||||
|
|
||||||
|
def left(self):
|
||||||
|
self.orientation = (self.orientation + 1) % 4
|
||||||
|
|
||||||
|
def right(self):
|
||||||
|
self.orientation = (self.orientation - 1) % 4
|
||||||
|
|
||||||
|
def front(self):
|
||||||
|
if self.orientation % 2: # x (1 or 3)
|
||||||
|
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)
|