diff --git a/.idea/agent-aspirateur.iml b/.idea/agent-aspirateur.iml
index 9095021..c025632 100644
--- a/.idea/agent-aspirateur.iml
+++ b/.idea/agent-aspirateur.iml
@@ -4,7 +4,7 @@
-
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 13fecf3..ae34238 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/hoover-agent/__main__.py b/hoover-agent/__main__.py
deleted file mode 100644
index 40ea1e0..0000000
--- a/hoover-agent/__main__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from environment import Agent, Environment
-
-print("Hello world!")
diff --git a/hoover-agent/environment.py b/hoover-agent/environment.py
deleted file mode 100644
index fe7c9f7..0000000
--- a/hoover-agent/environment.py
+++ /dev/null
@@ -1,172 +0,0 @@
-from numpy import sqrt, power
-import random
-
-
-class Thing:
- """This represents any physical object that can appear in an Environment.
- You subclass Thing to get the things you want. Each thing can have a
- .__name__ slot (used for output only)."""
-
- def __repr__(self):
- return '<{}>'.format(getattr(self, '__name__', self.__class__.__name__))
-
- def show_state(self):
- """Display the agent's internal state. Subclasses should override."""
- print("I don't know how to show_state.")
-
- def display(self, canvas, x, y, width, height):
- """Display an image of this Thing on the canvas."""
- # Do we need this?
- pass
-
-
-class Dirt(Thing):
- pass
-
-
-class Jewel(Thing):
- pass
-
-
-class Agent(Thing):
- """An Agent is a subclass of Thing with one required instance attribute
- (aka slot), .program, which should hold a function that takes one argument,
- the percept, and returns an action. (What counts as a percept or action
- will depend on the specific environment in which the agent exists.)
- Note that 'program' is a slot, not a method. If it were a method, then the
- program could 'cheat' and look at aspects of the agent. It's not supposed
- to do that: the program can only look at the percepts. An agent program
- that needs a model of the world (and of the agent itself) will have to
- build and maintain its own model. There is an optional slot, .performance,
- which is a number giving the performance measure of the agent in its
- environment."""
-
- def __init__(self, program=None):
- self.alive = True
- self.bump = False
- self.holding = []
- self.performance = 0
- if program is None:
- print("Can't find a valid program for {}, falling back to default.".format(self.__class__.__name__))
-
- def program(percept):
- return eval(input('Percept={}; action? '.format(percept)))
-
- self.program = program
-
- def can_grab(self, thing):
- """Return True if this agent can grab this thing.
- Override for appropriate subclasses of Agent and Thing."""
- return False
-
-
-class Environment:
- """Abstract class representing an Environment. 'Real' Environment classes
- inherit from this. Your Environment will typically need to implement:
- percept: Define the percept that an agent sees.
- execute_action: Define the effects of executing an action.
- Also update the agent.performance slot.
- The environment keeps a list of .things and .agents (which is a subset
- of .things). Each agent has a .performance slot, initialized to 0.
- Each thing has a .location slot, even though some environments may not
- need this."""
-
- def __init__(self, width=5, height=5):
- self.things = []
- self.agents = []
- self.width = width
- self.height = height
-
- perceptible_distance = 1
-
- def things_near(self, location, radius=None):
- radius = self.perceptible_distance if radius is None else self.perceptible_distance
- return [thing for thing in self.things
- if sqrt(power(location[0]-thing.location[0], 2) + power(location[1]-thing.location[1], 2)) <= radius]
-
- def thing_classes(self):
- return [Dirt, Jewel, Agent] # List of classes that can go into environment
-
- def percept(self, agent):
- """Return the percept that the agent sees at this point."""
- return 'Dirty' if self.some_things_at(agent.location, Dirt) else 'Clean'
-
- def execute_action(self, agent, action):
- """Change the world to reflect this action. (Implement this.)"""
- raise NotImplementedError
-
- def default_location(self, thing):
- """Default location to place a new thing with unspecified location."""
- location = self.random_location_inbounds()
- while self.some_things_at(location, Thing):
- location = self.random_location_inbounds()
- return location
-
- def is_inbounds(self, location):
- """Checks to make sure that the location is inbounds (within walls if we have walls)"""
- x, y = location
- return not (x < 0 or x > self.width or y < 0 or y > self.height)
-
- def random_location_inbounds(self, exclude=None):
- """Returns a random location that is inbounds."""
- location = (random.randint(0, self.width),
- random.randint(0, self.height))
- if exclude is not None:
- while location == exclude:
- location = (random.randint(0, self.width),
- random.randint(0, self.height))
- return location
-
- def exogenous_change(self):
- """If there is spontaneous change in the world, override this."""
- pass
-
- def step(self):
- """Run the environment for one time step. If the
- actions and exogenous changes are independent, this method will
- do. If there are interactions between them, you'll need to
- override this method."""
- actions = []
- for agent in self.agents:
- if agent.alive:
- actions.append(agent.program(self.percept(agent)))
- else:
- actions.append("")
- for (agent, action) in zip(self.agents, actions):
- self.execute_action(agent, action)
- self.exogenous_change()
-
- def run(self, steps=1000):
- """Run the Environment for given number of time steps."""
- for step in range(steps):
- self.step()
-
- def list_things_at(self, location, tclass=Thing):
- """Return all things exactly at a given location."""
- return [thing for thing in self.things
- if all(x == y for x, y in zip(thing.location, location)) and isinstance(thing, tclass)]
-
- def some_things_at(self, location, tclass=Thing):
- """Return true if at least one of the things at location
- is an instance of class tclass (or a subclass)."""
- return self.list_things_at(location, tclass) != []
-
- def add_thing(self, thing, location=None):
- """Add a thing to the environment, setting its location. For
- convenience, if thing is an agent program we make a new agent
- for it. (Shouldn't need to override this.)"""
- if not isinstance(thing, Thing):
- thing = Agent(thing)
- if thing in self.things:
- print("Can't add the same thing twice")
- else:
- thing.location = location if location is not None else self.default_location(thing)
- self.things.append(thing)
- if isinstance(thing, Agent):
- thing.performance = 0
- self.agents.append(thing)
-
- def delete_thing(self, thing):
- """Remove a thing from the environment."""
- if thing in self.agents:
- self.agents.remove(thing)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..b14d285
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+PyQt5~=5.15.4
\ No newline at end of file
diff --git a/setup.py b/setup.py
index a37c2cd..ba947a2 100644
--- a/setup.py
+++ b/setup.py
@@ -6,8 +6,6 @@
author='Killian Mahé',
author_email='killian.mahe1@uqac.ca',
packages=[
- 'pretty-tables',
- 'colorama',
- 'numpy',
+ 'PyQt5',
]
)
diff --git a/hoover-agent/__init__.py b/vacuum-agent/__init__.py
similarity index 100%
rename from hoover-agent/__init__.py
rename to vacuum-agent/__init__.py
diff --git a/vacuum-agent/__main__.py b/vacuum-agent/__main__.py
new file mode 100644
index 0000000..62d417d
--- /dev/null
+++ b/vacuum-agent/__main__.py
@@ -0,0 +1,116 @@
+from threading import Thread
+import sys
+
+from agent import Environment, Dirt, Jewel, Position
+
+from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView
+from PyQt5.QtCore import QThread, QRectF, Qt, QPointF
+from PyQt5.QtGui import QBrush, QPolygonF
+
+
+class EnvironmentThread(QThread):
+
+ def __init__(self, environment: Environment):
+ QThread.__init__(self)
+ self.environment = environment
+
+ def run(self):
+ self.environment.run()
+
+
+class AgentThread(QThread):
+
+ def run(self):
+ pass
+
+
+def convert_position(position: Position):
+ return Position(100*position.x + 50, 100*position.y + 50)
+
+
+class Window(QMainWindow):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.central_widget = self.centralWidget()
+ self.scene = QGraphicsScene()
+ self.view = QGraphicsView(self.scene)
+
+ self.environment = Environment()
+ self.environment.thing_spawn.connect(self.spawn_handler)
+ self.environment.thing_deleted.connect(self.deleted_thing_handler)
+
+ self.env_thread = EnvironmentThread(self.environment)
+ self.env_thread.finished.connect(app.exit)
+ self.env_thread.start()
+
+ self.dirt_rects = []
+ self.jewel_rects = []
+
+ self.setup_ui()
+ self.view.show()
+
+ def spawn_handler(self, thing):
+ if isinstance(thing, Dirt):
+ self.draw_dirt(thing.position)
+ elif isinstance(thing, Jewel):
+ self.draw_jewel(thing.position)
+
+ def deleted_thing_handler(self, thing):
+ position = convert_position(thing.position)
+ if isinstance(thing, Dirt):
+ for i in range(0, len(self.dirt_rects)):
+ if self.dirt_rects[i].contains(QPointF(position.x, position.y)):
+ self.scene.removeItem(self.dirt_rects.pop(i))
+ return
+ elif isinstance(thing, Jewel):
+ for i in range(0, len(self.jewel_rects)):
+ if self.jewel_rects[i].contains(QPointF(position.x, position.y)):
+ self.scene.removeItem(self.jewel_rects.pop(i))
+ return
+
+ def draw_jewel(self, position: Position):
+ position = convert_position(position)
+ brush = QBrush()
+ brush.setStyle(Qt.SolidPattern)
+ brush.setColor(Qt.cyan)
+
+ self.jewel_rects.append(self.scene.addPolygon(QPolygonF([
+ QPointF(position.x - 10, position.y),
+ QPointF(position.x, position.y - 10),
+ QPointF(position.x + 10, position.y),
+ QPointF(position.x, position.y + 20)
+ ]), brush=brush))
+
+ def draw_dirt(self, position: Position):
+ position = convert_position(position)
+ brush = QBrush()
+ brush.setStyle(Qt.Dense5Pattern)
+ brush.setColor(Qt.gray)
+ self.dirt_rects.append(self.scene.addRect(QRectF(position.x - 50, position.y - 50, 100, 100),
+ brush=brush))
+
+ def setup_ui(self):
+ self.setWindowTitle("Vacuum Agent")
+ self.resize(1000, 800)
+ self.setCentralWidget(self.view)
+
+ # Build scene
+ self.scene.addRect(QRectF(0, 0, 500, 500))
+
+ for y in range(0, self.environment.y_max):
+ for x in range(0, self.environment.x_max):
+ self.scene.addRect(QRectF(100 * x, 100 * y, 100, 100))
+
+
+app = QApplication(sys.argv)
+win = Window()
+win.show()
+sys.exit(app.exec())
+
+# environment = Environment()
+# agent = Agent()
+# environment.agent = agent
+#
+# env_thread = Thread(environment.run())
+# env_thread.run()
diff --git a/vacuum-agent/agent.py b/vacuum-agent/agent.py
new file mode 100644
index 0000000..7ef33c9
--- /dev/null
+++ b/vacuum-agent/agent.py
@@ -0,0 +1,136 @@
+from random import random, randint
+from time import sleep
+
+from PyQt5.QtCore import QObject, pyqtSignal
+
+from logic import State
+
+
+class Position:
+
+ def __init__(self, x=0, y=0):
+ self.x = x
+ self.y = y
+
+ def __eq__(self, other):
+ if not isinstance(other, Position):
+ raise NotImplementedError
+ if self.x == other.x and self.y == other.y:
+ return True
+ return False
+
+ def __str__(self):
+ return "(%s,%s)" % (self.x, self.y)
+
+
+class Thing:
+
+ def __init__(self, position: Position = None, x=0, y=0):
+ if position and isinstance(position, Position):
+ self.position = position
+ else:
+ self.position = Position(x, y)
+
+
+class Dirt(Thing):
+ pass
+
+
+class Jewel(Thing):
+ pass
+
+
+class Environment(State, QObject):
+
+ thing_spawn = pyqtSignal('PyQt_PyObject')
+ thing_deleted = pyqtSignal('PyQt_PyObject')
+
+ def __init__(self):
+ QObject.__init__(self)
+ self.things = []
+ self.agent = None
+ self.x_max = 5
+ self.y_max = 5
+ self.dirt_probability = 0.01
+ self.jewel_probability = 0.001
+
+ def __eq__(self, other):
+ if isinstance(other, Environment):
+ return self.__hash__() == other.__hash__()
+ raise NotImplementedError
+
+ def __hash__(self):
+ return hash({
+ "things": self.things,
+ "agent": self.agent
+ })
+
+ def __str__(self):
+ pass
+
+ def run(self):
+ while True:
+ if random() <= self.dirt_probability:
+ self.thing_spawn.emit(self.generate_dirt())
+ if random() <= self.jewel_probability:
+ self.thing_spawn.emit(self.generate_jewel())
+ sleep(0.005)
+
+ def something_at(self, location, thing_class=None):
+ if issubclass(thing_class, Agent) and self.agent.position == location:
+ return self.agent
+ if issubclass(thing_class, Thing):
+ for thing in self.things:
+ if thing.position == location and isinstance(thing, thing_class):
+ return thing
+ return False
+ else:
+ raise NotImplementedError
+
+ def random_location(self):
+ x = randint(0, self.x_max - 1)
+ y = randint(0, self.y_max - 1)
+ return Position(x, y)
+
+ def generate_dirt(self):
+ position = self.random_location()
+ while self.something_at(position, Dirt):
+ position = self.random_location()
+ return self.add_thing(Dirt(position))
+
+ def add_thing(self, thing):
+ if isinstance(thing, Thing):
+ self.things.append(thing)
+ return thing
+ raise NotImplementedError
+
+ def delete_thing(self, index):
+ if index < len(self.things):
+ self.thing_deleted.emit(self.things.pop(0))
+
+ def generate_jewel(self):
+ position = self.random_location()
+ while self.something_at(position, Jewel):
+ position = self.random_location()
+ return self.add_thing(Jewel(position))
+
+
+class Agent:
+
+ def __init__(self, position=Position(0, 0)):
+ self.position = position
+
+ def run(self):
+ pass
+
+ def observe_environment_with_all_my_sensors(self):
+ pass
+
+ def update_my_state(self):
+ pass
+
+ def choose_an_action(self):
+ pass
+
+ def just_do_it(self):
+ pass
diff --git a/hoover-agent/logic.py b/vacuum-agent/logic.py
similarity index 100%
rename from hoover-agent/logic.py
rename to vacuum-agent/logic.py