co2114 2026.1.1__tar.gz → 2026.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {co2114-2026.1.1 → co2114-2026.1.2}/PKG-INFO +1 -1
  2. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/agent/environment.py +1 -1
  3. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/optimisation/planning.py +125 -41
  4. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/optimisation/things.py +9 -0
  5. {co2114-2026.1.1 → co2114-2026.1.2}/co2114.egg-info/PKG-INFO +1 -1
  6. {co2114-2026.1.1 → co2114-2026.1.2}/setup.py +1 -1
  7. {co2114-2026.1.1 → co2114-2026.1.2}/LICENSE +0 -0
  8. {co2114-2026.1.1 → co2114-2026.1.2}/README.md +0 -0
  9. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/__init__.py +0 -0
  10. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/agent/__init__.py +0 -0
  11. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/agent/things.py +0 -0
  12. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/constraints/__init__.py +0 -0
  13. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/constraints/csp/__init__.py +0 -0
  14. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/constraints/csp/util.py +0 -0
  15. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/constraints/magic.py +0 -0
  16. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/constraints/sudoku.py +0 -0
  17. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/engine.py +0 -0
  18. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/optimisation/__init__.py +0 -0
  19. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/optimisation/minimax.py +0 -0
  20. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/reasoning/__init__.py +0 -0
  21. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/reasoning/cluedo.py +0 -0
  22. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/reasoning/inference.py +0 -0
  23. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/reasoning/logic.py +0 -0
  24. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/search/__init__.py +0 -0
  25. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/search/graph.py +0 -0
  26. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/search/maze.py +0 -0
  27. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/search/things.py +0 -0
  28. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/search/util.py +0 -0
  29. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/util/__init__.py +0 -0
  30. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/util/colours.py +0 -0
  31. {co2114-2026.1.1 → co2114-2026.1.2}/co2114/util/fonts.py +0 -0
  32. {co2114-2026.1.1 → co2114-2026.1.2}/co2114.egg-info/SOURCES.txt +0 -0
  33. {co2114-2026.1.1 → co2114-2026.1.2}/co2114.egg-info/dependency_links.txt +0 -0
  34. {co2114-2026.1.1 → co2114-2026.1.2}/co2114.egg-info/requires.txt +0 -0
  35. {co2114-2026.1.1 → co2114-2026.1.2}/co2114.egg-info/top_level.txt +0 -0
  36. {co2114-2026.1.1 → co2114-2026.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: co2114
3
- Version: 2026.1.1
3
+ Version: 2026.1.2
4
4
  Summary: codebase for co2114
5
5
  Author: wil ward
6
6
  Requires-Python: >=3.12
@@ -66,7 +66,7 @@ class BaseEnvironment:
66
66
 
67
67
  for i in range(steps):
68
68
  self.__counter += 1
69
- if self.is_done: # print terination message and exit
69
+ if self.is_done: # print termination message and exit
70
70
  print(f"{self}: Simulation complete after {i} of {steps} iterations.")
71
71
  return
72
72
  self.step() # else iterate one step
@@ -4,7 +4,12 @@ from ..agent.environment import GraphicEnvironment
4
4
  from ..search.util import manhattan
5
5
  import random
6
6
 
7
- PRESET_STATES = {
7
+ from typing import override
8
+
9
+ Location = tuple[int, int]
10
+ State = None | dict[str, dict[House | Hospital, Location] | dict[str, int]]
11
+ Numeric = int | float
12
+ PRESET_STATES: dict[str, dict[str, list[Location] | int]] = {
8
13
  "empty": None,
9
14
  '0': {
10
15
  "hospitals": [(2, 4)],
@@ -41,35 +46,99 @@ PRESET_STATES = {
41
46
 
42
47
 
43
48
  class HospitalOptimiser(Optimiser, UtilityBasedAgent):
44
- def explore(self, state):
49
+ """ Hospital Optimiser Agent"""
50
+ def explore(self, state:State) -> None:
51
+ """ Move hospitals to new locations in state
52
+
53
+ :param state: new state with hospital locations
54
+ """
45
55
  if not state: return
46
56
  print(f"{self}: exploring state\n {state['hospitals']}")
47
57
  for hospital, loc in state["hospitals"].items():
48
58
  hospital.location = loc
49
59
 
50
- def utility(self, state):
51
- """ calculate distance from each hospital to houses """
60
+ @override
61
+ def utility(self, state:State) -> Numeric:
62
+ """ Calculate utility of possible state by calculating distance
63
+ of each hospital to houses
64
+
65
+ Returns negative total distance to be minimised.
66
+
67
+ :param state: current state with hospital and house locations
68
+ :return: negative total distance
69
+ """
52
70
  obj = 0
53
- houses, hospitals = state["houses"], state["hospitals"]
54
- for house in houses:
55
- dist_to_nearest = infinity # very big
56
- for hospital in hospitals:
57
- dist = manhattan(
58
- houses[house],
59
- hospitals[hospital])
60
- if dist < dist_to_nearest:
61
- dist_to_nearest = dist
62
- obj += dist_to_nearest
71
+ houses: dict[House, Location] = state["houses"] # type: ignore
72
+ hospitals: dict[Hospital, Location] = state["hospitals"] # type: ignore
73
+
74
+ for house in houses: # iterate over houses
75
+ dist_to_nearest_hospital = infinity # very big
76
+
77
+ for hospital in hospitals: # iterate over hospitals
78
+ house_loc = houses[house]
79
+ hospital_loc = hospitals[hospital]
80
+
81
+ dist = manhattan(house_loc, hospital_loc)
82
+
83
+ # calculate closest distance
84
+ if dist < dist_to_nearest_hospital:
85
+ dist_to_nearest_hospital = dist
86
+
87
+ obj += dist_to_nearest_hospital # add distance for this house
63
88
  return -obj
64
89
 
65
90
 
66
91
  class HospitalPlacement(GraphicEnvironment):
67
- def __init__(self, init=None, *args, **kwargs):
92
+ """ Hospital Placement Environment
93
+
94
+ Allows placement of hospitals and houses on a grid
95
+
96
+ Has a state consisting of hospital and house locations and bounds of the environment
97
+ """
98
+ def __init__(self,
99
+ init:dict[str, list[Location] | int] | None = None,
100
+ *args,
101
+ **kwargs) -> None:
102
+ """ Constroctor for HospitalPlacement environment
103
+
104
+ :param init: initial state dictionary with hospital and house locations and height/width of environment
105
+ :param args: additional args for GraphicEnvironment
106
+ :param kwargs: additional kwargs for GraphicEnvironment
107
+ """
68
108
  super().__init__(*args, **kwargs)
69
109
  self.initialise_state(init)
110
+
111
+
112
+ def initialise_state(self,
113
+ state_dict: dict[str, list[Location] | int]) -> None:
114
+ """ Initialise environment state from state dictionary
115
+
116
+ :param state_dict: state dictionary with hospital and house locations and height/width of environment
117
+ """
118
+ if state_dict is None: return # empty state
119
+
120
+ if "height" in state_dict:
121
+ self.height = state_dict["height"]
122
+
123
+ if "width" in state_dict:
124
+ self.width = state_dict["width"]
125
+
126
+ self.size = self.width, self.height
127
+
128
+ if "hospitals" in state_dict:
129
+ for loc in state_dict["hospitals"]:
130
+ self.add_thing(Hospital(), location=loc)
131
+
132
+ for loc in state_dict["houses"]:
133
+ self.add_thing(House(), location=loc)
134
+
70
135
 
71
136
  @property
72
- def state(self):
137
+ def state(self) -> State:
138
+ """ Attribute returning current environment state
139
+
140
+ :return: current state with hospital and house locations and bounds of environment
141
+ """
73
142
  return {
74
143
  "hospitals": {
75
144
  thing: thing.location
@@ -85,7 +154,12 @@ class HospitalPlacement(GraphicEnvironment):
85
154
  }
86
155
 
87
156
  @property
88
- def neighbours(self):
157
+ def neighbours(self) -> list[State]:
158
+ """ Generate neighbouring states by moving each hospital
159
+ in each direction by one unit if possible.
160
+
161
+ :return: list of neighbouring states
162
+ """
89
163
  neighbours = []
90
164
  for i, hospital in enumerate(self.state["hospitals"]):
91
165
  location = hospital.location
@@ -97,17 +171,31 @@ class HospitalPlacement(GraphicEnvironment):
97
171
  neighbours.append(candidate)
98
172
  return neighbours
99
173
 
100
- def is_inbounds(self, location):
174
+ def is_inbounds(self, location:Location) -> bool:
175
+ """ Checks if location is in bounds and unoccupied
176
+
177
+ :return bool: True if location is in bounds and unoccupied
178
+ """
101
179
  if not super().is_inbounds(location):
102
180
  return False
103
181
  return len(self.things_at(location)) == 0
104
182
 
105
- def add_agent(self, agent:Agent):
183
+ @override
184
+ def add_agent(self, agent:Agent) -> None:
185
+ """ Add agent to environment, overrides GraphicEnvironment method as agent has no location.
186
+
187
+ :param agent: agent to add
188
+ """
106
189
  if not isinstance(agent, Agent):
107
190
  raise TypeError(f"{self}: {agent} is not an Agent.")
108
191
  self.agents.add(agent)
109
192
 
110
- def add_thing_randomly(self, thing):
193
+ @override
194
+ def add_thing_randomly(self, thing:things.Thing) -> None:
195
+ """ Add thing to random unoccupied location in environment
196
+
197
+ :param thing: thing to add
198
+ """
111
199
  x = random.randint(self.x_start, self.x_end-1)
112
200
  y = random.randint(self.y_start, self.y_end-1)
113
201
  lim, count = 10, 0
@@ -120,35 +208,31 @@ class HospitalPlacement(GraphicEnvironment):
120
208
  y = random.randint(self.y_start, self.y_end-1)
121
209
  self.add_thing(thing, (x,y))
122
210
 
123
-
124
- def initialise_state(self, state_dict):
125
- if state_dict is None:
126
- return
127
- if "height" in state_dict:
128
- self.height = state_dict["height"]
129
- if "width" in state_dict:
130
- self.width = state_dict["width"]
131
- self.size = self.width, self.height
132
- if "hospitals" in state_dict:
133
- for loc in state_dict["hospitals"]:
134
- self.add_thing(Hospital(), location=loc)
135
- for loc in state_dict["houses"]:
136
- self.add_thing(House(), location=loc)
137
-
138
-
139
211
  @property
140
- def is_done(self):
212
+ def is_done(self) -> bool:
213
+ """ Definition of when environment is done """
141
214
  if len(self.agents) == 0: return True # if there are no agents
142
215
  return hasattr(self, "success") and self.success
143
216
 
144
- def percept(self, agent):
217
+ @override
218
+ def percept(self, agent:Agent) -> tuple[State, list[State]]:
219
+ """ Percept for agent in environment
220
+
221
+ :return tuple: current state and neighbouring states
222
+ """
145
223
  return self.state, self.neighbours
146
224
 
147
- def execute_action(self, agent, action):
148
- """ Execute an action """
225
+ @override
226
+ def execute_action(self,
227
+ agent:HospitalOptimiser,action:tuple[str,State]) -> None:
228
+ """ Execute an action
229
+
230
+ :param agent: agent performing action
231
+ :param action: action to execute, tuple of command and state. possible commands are "done" and "explore"
232
+ """
149
233
  command, state = action
150
234
  match command:
151
- case "done":
235
+ case "done": # optimisation complete
152
236
  if state:
153
237
  agent.explore(state)
154
238
  self.success = True
@@ -2,17 +2,26 @@ from ..search import things
2
2
 
3
3
  from ..util.fonts import platform
4
4
 
5
+ from typing import override
6
+
7
+ # Re-export relevant classes from things module
5
8
  Agent = things.Agent
6
9
  UtilityBasedAgent = things.UtilityBasedAgent
7
10
 
8
11
  class Hospital(things.Thing):
12
+ @override
9
13
  def __repr__(self):
14
+ """ String representation of a Hospital. """
10
15
  return "🏥" if platform != "darwin" else "+"
11
16
 
12
17
  class House(things.Thing):
18
+ @override
13
19
  def __repr__(self):
20
+ """ String representation of a House. """
14
21
  return "🏠" if platform != "darwin" else "^"
15
22
 
16
23
  class Optimiser(things.Agent):
24
+ @override
17
25
  def __repr__(self):
26
+ """ String representation of an Optimiser. """
18
27
  return "📈"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: co2114
3
- Version: 2026.1.1
3
+ Version: 2026.1.2
4
4
  Summary: codebase for co2114
5
5
  Author: wil ward
6
6
  Requires-Python: >=3.12
@@ -2,7 +2,7 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name="co2114",
5
- version="2026.1.1",
5
+ version="2026.1.2",
6
6
  description="codebase for co2114",
7
7
  author="wil ward",
8
8
  python_requires=">=3.12",
File without changes
File without changes
File without changes
File without changes
File without changes