Przyrost 2

This commit is contained in:
Vadzim Valchkovich 2023-05-14 14:23:37 +02:00
parent 71fcd755aa
commit d6a8a7481e
11 changed files with 173 additions and 102 deletions

View File

@ -31,7 +31,7 @@
---
- [ ] Planowanie ruchu: Wymagania dot. drugiego przyrostu
- [ ] **Planowanie ruchu: Wymagania dot. drugiego przyrostu**
- [ ] Należy wykorzystać „Schemat procedury przeszukiwania grafu stanów z uwzględnieniem kosztu“
- [ ] 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.
@ -40,3 +40,9 @@
> _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).

View File

@ -1,3 +1,4 @@
import random
from src.Engine import Engine
from src.obj.Waiter import Waiter
from src.obj.Block import Block
@ -6,20 +7,31 @@ from src.obj.Table import Table
from src.UserController import UserController
from src.StateController import StateController
waiter = Waiter([0, 0], 0, 50, 450//50)
SCREEN_SIZE = (800, 800)
SQUARE_SIZE = 40
waiter = Waiter([0, 0], 0, SQUARE_SIZE, SCREEN_SIZE)
objects = [
Kitchen([0, 0], 0, 50, 450//50),
Table([3, 6], 0, 50, 450//50),
Table([2, 4], 0, 50, 450//50),
Table([1, 5], 0, 50, 450//50),
Block([3, 5], 0, 50, 450//50),
Block([1, 4], 0, 50, 450//50),
Block([2, 5], 0, 50, 450//50)
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((450, 450), 50, user, state)
engine = Engine(SCREEN_SIZE, SQUARE_SIZE, user, state)
for o in objects:
engine.subscribe(o)

View File

@ -1,3 +1,4 @@
import time
import pygame
from .obj.Object import Object
from .UserController import UserController
@ -7,6 +8,8 @@ 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
@ -18,6 +21,7 @@ class Engine:
self.num_squares, self.square_size)
self.objects: list[Object] = []
self.goals: list = []
self.runnin = False
@ -48,19 +52,18 @@ class Engine:
self.running = False
def action(self):
if not self.state.path:
if self.goals:
self.state.graphsearch(self)
self.user.handler(self)
conditionals = [
not self.user.obj.collide_test(self.user.obj),
all([not o.collide_test(self.user.obj) for o in self.objects])
]
if all(conditionals):
self.user.obj.dampState()
else:
self.user.obj.rollbackState()
self.user.obj.goal_test(self)
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))
@ -74,7 +77,13 @@ class Engine:
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)

View File

@ -1,37 +1,45 @@
from .obj.TemporaryState import TemporaryState
from queue import PriorityQueue
class StateController:
def __init__(self, istate):
self.path = []
self.explored = []
self.fringe = []
self.fringe = PriorityQueue()
self.istate = istate
self.goal = istate.position
def reset(self):
self.path.clear()
self.explored.clear()
self.fringe.clear()
self.fringe = PriorityQueue()
def build_path(self, goal_state):
total_cost = goal_state.cost
self.path.append(goal_state)
while self.path[-1].agent_role != "blank":
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): # BFS
def graphsearch(self, engine): # A*
print("Search path")
self.goal = list(engine.goals.pop())
self.reset()
self.fringe.append(TemporaryState(self.istate))
start = TemporaryState(self.istate, 0)
while self.fringe:
self.explored.append(self.fringe.pop(0))
self.fringe.put(start)
if self.explored[-1].goal_test(engine):
print("Goal!")
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)
@ -40,7 +48,6 @@ class StateController:
self.succ(self.explored[-1].left(), engine)
self.succ(self.explored[-1].right(), engine)
self.path = self.fringe
engine.redraw()
self.reset()
@ -57,4 +64,29 @@ class StateController:
elif any([o.collide_test(state) for o in engine.objects]):
return
self.fringe.append(state)
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)

View File

@ -9,12 +9,8 @@ class UserController:
for event in pygame.event.get():
if event.type == pygame.QUIT:
engine.quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.obj.front()
elif event.key == pygame.K_LEFT:
self.obj.left()
elif event.key == pygame.K_RIGHT:
self.obj.right()
elif event.key == pygame.K_SPACE:
engine.state.graphsearch(engine)
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
pos = (pos[0] // engine.square_size,
pos[1] // engine.square_size)
engine.appendGoalPosition(pos)

View File

@ -2,8 +2,8 @@ from src.obj.Object import Object
class Block(Object):
def __init__(self, position, orientation, square_size, square_count):
super().__init__("block", position, orientation, square_size, square_count)
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

View File

@ -2,19 +2,5 @@ from src.obj.Object import Object
class Kitchen(Object):
def __init__(self, position, orientation, square_size, square_count):
super().__init__("kitchen", position, orientation, square_size, square_count)
def goal_test(self, waiter) -> bool:
conditions = [
waiter.orders_in_basket(),
self.position == waiter.position
]
if all(conditions):
for table in waiter.basket:
if table.agent_role == "wait":
table.next_role(waiter)
return True
return False
def __init__(self, position, orientation, square_size, screen_size):
super().__init__("kitchen", position, orientation, square_size, screen_size)

View File

@ -2,12 +2,13 @@ import pygame
class Object:
def __init__(self, agent_role, position, orientation, square_size, square_count):
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.square_count = square_count
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))
@ -37,11 +38,11 @@ class Object:
def collide_test(self, obj) -> bool:
return False
def goal_test(self, waiter) -> 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

View File

@ -2,8 +2,8 @@ from src.obj.Object import Object
class Table(Object):
def __init__(self, position, orientation, square_size, square_count, current_role=1):
super().__init__("table", position, orientation, square_size, square_count)
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])
@ -12,13 +12,3 @@ class Table(Object):
if waiter.agent_role == "waiter":
self.current_role = (self.current_role + 1) % 4
self.change_role(self.roles[self.current_role])
def goal_test(self, waiter) -> bool:
if self.position == waiter.position:
if self.agent_role == "order":
return waiter.take_order(self)
elif self.agent_role == "done":
return waiter.drop_order(self)
return False

View File

@ -3,15 +3,24 @@ import copy
class TemporaryState(Waiter):
def __init__(self, parent, action="blank"):
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.square_count),
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.basket = copy.copy(repl.basket)
self.parent = repl.parent
self.cost_so_far = repl.cost_so_far
def apply_transformation(self):
if self.agent_role == "left":
@ -25,13 +34,13 @@ class TemporaryState(Waiter):
self.position[1] += self.orientation - 1 # y (-1 or +1)
def left(self):
return TemporaryState(self, "left")
return TemporaryState(self, self.cost_so_far, "left", 1)
def right(self):
return TemporaryState(self, "right")
return TemporaryState(self, self.cost_so_far, "right", 1)
def front(self):
return TemporaryState(self, "front")
return TemporaryState(self, self.cost_so_far, "front", 1)
def collide_test(self) -> bool:
out_of_range = [
@ -49,3 +58,45 @@ class TemporaryState(Waiter):
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()

View File

@ -3,13 +3,19 @@ from src.obj.Object import Object
class Waiter(Object):
def __init__(self, position, orientation, square_size, square_count, basket=[]):
super().__init__("waiter", position, orientation, square_size, square_count)
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)
@ -18,21 +24,6 @@ class Waiter(Object):
self.position = copy.deepcopy(self.prev_position)
self.orientation = copy.copy(self.prev_orientation)
def take_order(self, table) -> bool:
if table.agent_role == "order":
if len(self.basket) < self.basket_size:
table.next_role(self)
self.basket.append(table)
return True
return False
def drop_order(self, table) -> bool:
if table.agent_role == "done":
self.basket.remove(table)
table.next_role(self)
return True
return False
def orders_in_basket(self) -> bool:
return self.basket
@ -57,6 +48,3 @@ class Waiter(Object):
]
return any(out_of_range)
def goal_test(self, engine):
return any([o.goal_test(self) for o in engine.objects])