full refactor and upgrade code to first part of project
24
README.MD
@ -17,4 +17,26 @@
|
||||
|
||||
## 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.
|
||||
|
||||
---
|
||||
|
||||
- [ ] 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.
|
||||
- [ ] 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._
|
||||
|
||||
---
|
||||
|
27
agent.py
Normal file
@ -0,0 +1,27 @@
|
||||
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
|
||||
|
||||
waiter = Waiter([0, 0], 0, 50, 450//50)
|
||||
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)
|
||||
]
|
||||
|
||||
user = UserController(waiter)
|
||||
state = StateController(waiter)
|
||||
engine = Engine((450, 450), 50, 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
|
80
src/Engine.py
Normal file
@ -0,0 +1,80 @@
|
||||
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):
|
||||
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.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):
|
||||
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)
|
||||
|
||||
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 s in self.state.path:
|
||||
s.blit(self.screen)
|
||||
|
||||
pygame.display.flip()
|
60
src/StateController.py
Normal file
@ -0,0 +1,60 @@
|
||||
from .obj.TemporaryState import TemporaryState
|
||||
|
||||
|
||||
class StateController:
|
||||
def __init__(self, istate):
|
||||
self.path = []
|
||||
self.explored = []
|
||||
self.fringe = []
|
||||
self.istate = istate
|
||||
|
||||
def reset(self):
|
||||
self.path.clear()
|
||||
self.explored.clear()
|
||||
self.fringe.clear()
|
||||
|
||||
def build_path(self, goal_state):
|
||||
self.path.append(goal_state)
|
||||
while self.path[-1].agent_role != "blank":
|
||||
self.path.append(self.path[-1].parent)
|
||||
|
||||
return self.path
|
||||
|
||||
def graphsearch(self, engine): # BFS
|
||||
print("Search path")
|
||||
|
||||
self.reset()
|
||||
|
||||
self.fringe.append(TemporaryState(self.istate))
|
||||
|
||||
while self.fringe:
|
||||
self.explored.append(self.fringe.pop(0))
|
||||
|
||||
if self.explored[-1].goal_test(engine):
|
||||
print("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)
|
||||
|
||||
self.path = self.fringe
|
||||
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
|
||||
|
||||
self.fringe.append(state)
|
20
src/UserController.py
Normal file
@ -0,0 +1,20 @@
|
||||
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.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)
|
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, square_count):
|
||||
super().__init__("block", position, orientation, square_size, square_count)
|
||||
|
||||
def collide_test(self, waiter: Object) -> bool:
|
||||
return waiter.position == self.position
|
20
src/obj/Kitchen.py
Normal file
@ -0,0 +1,20 @@
|
||||
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
|
47
src/obj/Object.py
Normal file
@ -0,0 +1,47 @@
|
||||
import pygame
|
||||
|
||||
|
||||
class Object:
|
||||
def __init__(self, agent_role, position, orientation, square_size, square_count):
|
||||
self.agent_role = agent_role
|
||||
self.position = position
|
||||
self.orientation = orientation % 4
|
||||
self.square_size = square_size
|
||||
self.square_count = square_count
|
||||
|
||||
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 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)
|
24
src/obj/Table.py
Normal file
@ -0,0 +1,24 @@
|
||||
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)
|
||||
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])
|
||||
|
||||
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
|
51
src/obj/TemporaryState.py
Normal file
@ -0,0 +1,51 @@
|
||||
from src.obj.Waiter import Waiter
|
||||
import copy
|
||||
|
||||
|
||||
class TemporaryState(Waiter):
|
||||
def __init__(self, parent, action="blank"):
|
||||
super().__init__(copy.deepcopy(parent.position),
|
||||
copy.copy(parent.orientation),
|
||||
copy.copy(parent.square_size),
|
||||
copy.copy(parent.square_count),
|
||||
copy.copy(parent.basket))
|
||||
self.parent = parent
|
||||
self.change_role(action)
|
||||
self.apply_transformation()
|
||||
|
||||
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, "left")
|
||||
|
||||
def right(self):
|
||||
return TemporaryState(self, "right")
|
||||
|
||||
def front(self):
|
||||
return TemporaryState(self, "front")
|
||||
|
||||
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)
|
62
src/obj/Waiter.py
Normal file
@ -0,0 +1,62 @@
|
||||
import copy
|
||||
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)
|
||||
self.basket_size = 2
|
||||
self.basket = basket
|
||||
self.prev_position = copy.deepcopy(self.position)
|
||||
self.prev_orientation = copy.copy(self.orientation)
|
||||
|
||||
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 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
|
||||
|
||||
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)
|
||||
|
||||
def goal_test(self, engine):
|
||||
return any([o.goal_test(self) for o in engine.objects])
|