Coverage for src/gncpy/game_engine/components.py: 0%
98 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-13 06:15 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-13 06:15 +0000
1"""Defines components for use by game entities."""
2import os
3import pathlib
4import numpy as np
5import pygame
7os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
8import serums.models as smodels # noqa
11library_asset_dir = os.path.join(
12 pathlib.Path(__file__).parent.parent.resolve(), "games", "assets"
13)
14"""Directory containing common game assets."""
17class CShape:
18 """Contains properties related to the drawn shape.
20 Attributes
21 ----------
22 type : string
23 Type of shape to create.
24 shape : pygame object
25 Shape to render.
26 color : tuple
27 RGB triplet for the color in range [0, 255].
28 zorder : int
29 Determines draw order, lower numbers are drawn first.
30 """
32 __slots__ = "type", "shape", "color", "zorder"
34 def __init__(
35 self, s_type=None, w=None, h=None, color=None, zorder=None, fpath=None
36 ):
37 """Initialize an object.
39 Parameters
40 ----------
41 s_type : string
42 Type of shape to create. Options are :code:`'rect'`, or
43 :code:`'sprite'`.
44 w : int
45 Width in pixels. Must be specified if type :code:`'rect'`, optional
46 for sprites (will scale image to this width).
47 h : int
48 Height in pixels. Must be specified if type :code:`'rect'`, optional
49 for sprites (will sacle image to this height).
50 color : tuple
51 RGB triplet for the color in range [0, 255]. Only used by type
52 :code:`'rect'`.
53 zorder : int
54 Determines draw order, lower numbers are drawn first.
55 """
56 self.type = s_type.lower()
57 if self.type == "rect":
58 self.shape = pygame.Rect((0, 0), (w, h))
60 elif self.type == "sprite":
61 if fpath is None:
62 raise RuntimeError(
63 "File path cannot be None for type {}".format(s_type)
64 )
65 succ = os.path.isfile(fpath)
66 if not succ:
67 fp = os.path.join(library_asset_dir, fpath)
68 succ = os.path.isfile(fp)
69 else:
70 fp = fpath
72 if not succ:
73 raise FileNotFoundError("Failed to find file {}".format(fpath))
75 img = pygame.image.load(fp)
76 if fp[-3:] == "png":
77 img = img.convert_alpha()
78 else:
79 img = img.convert()
81 if w is not None and h is not None:
82 self.shape = pygame.transform.scale(img, (w, h))
83 else:
84 self.shape = img
86 else:
87 raise NotImplementedError("Shape type {} not implemented".format(s_type))
89 self.color = color
90 self.zorder = zorder
93class CTransform:
94 """Contains properties relating to the spatial components.
96 Attributes
97 ----------
98 pos : 2 x 1 numpy array
99 position of the center in pixels
100 last_pos : 2 x 1 numpy array
101 last position of the center in pixels.
102 vel 2 x 1 numpy array
103 velocity of the center in pixels per timestep
104 """
106 __slots__ = "pos", "last_pos", "vel"
108 def __init__(self):
109 self.pos = np.nan * np.ones((2, 1))
110 self.last_pos = np.nan * np.ones((2, 1))
111 self.vel = np.nan * np.ones((2, 1))
114class CCollision:
115 """Handles the bounding box used for collisions.
117 Attributes
118 ---------
119 aabb : pygame Rect
120 Rectangle representing the axis aligned bounding box.
121 """
123 __slots__ = "aabb"
125 def __init__(self, w=None, h=None):
126 if w is not None and h is not None:
127 self.aabb = pygame.Rect((0, 0), (w, h))
128 else:
129 self.aabb = None
132class CBirth:
133 """Handles the birth model properties.
135 Also handles the generation of the birth location through the use of
136 a distribution object from SERUMS.
138 Attributes
139 ----------
140 loc : numpy array
141 Location parameter for distribution
142 randomize : bool
143 Flag indicating if the state should be randomly sampled.
144 """
146 __slots__ = "_model", "loc", "_rng", "randomize"
148 def __init__(
149 self, b_type=None, loc=None, scale=None, params=None, rng=None, randomize=True
150 ):
151 """Initializes an object.
153 Parameters
154 ----------
155 b_type : string
156 Model type. Options are :code:`'gaussian'`.
157 loc : numpy array
158 location parameter of the model.
159 scale : N x N numpy array
160 scale parameter of the model.
161 params : dict
162 additional parameters for the model.
163 """
164 if b_type.lower() == "gaussian":
165 if len(scale.shape) == 1:
166 std = np.diag(scale)
167 else:
168 std = scale
169 self._model = smodels.Gaussian(mean=loc, covariance=std ** 2)
170 else:
171 self._model = None
172 self.loc = loc
173 self._rng = rng
174 self.randomize = randomize
176 def sample(self):
177 """Draw a sample from the distribution.
179 Will provide the location parameter if not randomizing.
181 Returns
182 -------
183 N x 1 numpy array
184 """
185 if self.randomize:
186 return self._model.sample(rng=self._rng).reshape((-1, 1))
187 else:
188 return self._model.location.reshape((-1, 1)).copy()
191class CEvents:
192 """Holds the events properties.
194 Attributes
195 ----------
196 events : list
197 Each element is a tuple with the first being an event identifier and
198 the second a dict with extra info.
199 """
201 __slots__ = "events"
203 def __init__(self):
204 self.events = []
207class CHazard:
208 """Hold the properties for hazard info.
210 Attributes
211 ----------
212 prob_of_death : float
213 Probability of death at each timestep in the hazard. Must be in the
214 range (0, 1].
215 entrance_times : dict
216 mapping of player id to entrance time.
217 """
219 __slots__ = "prob_of_death", "entrance_times"
221 def __init__(self, prob_of_death=None):
222 """Initialize an object.
224 Parameters
225 ----------
226 prob_of_death : float
227 Probability of death at each timestep.
228 """
229 self.prob_of_death = prob_of_death
230 self.entrance_times = {}
233class CCapabilities:
234 """Hold properties about player/target capabilities.
236 Attributes
237 ----------
238 capabilities : list
239 Each element defines a capability. The :code:`in` keyword must work
240 for chcking if an item is in the list.
241 """
243 __slots__ = "capabilities"
245 def __init__(self, capabilities=None):
246 """Initialize an object.
248 Parameters
249 ----------
250 capabilities : list
251 List of capabilities.
252 """
253 if capabilities is None:
254 capabilities = []
255 self.capabilities = capabilities
258class CPriority:
259 """Hold properties about the priority.
261 Attributes
262 ----------
263 priority : float
264 Priority value.
265 """
267 __slots__ = "priority"
269 def __init__(self, priority=None):
270 """Initialize an object.
272 Parameters
273 ----------
274 priority : float
275 priority of the object.
276 """
277 self.priority = priority
280class CDynamics:
281 """Handles all the properties relaing to the dynamics.
283 Also implements the logic for propagating the state via a dynamics
284 object.
286 Attributes
287 ----------
288 dynObj : :class:`gncpy.dynamics.DynamicsBase`
289 Implements the dynamics equations, control, and state constraints.
290 last_state : numpy array
291 Last state for the dynamics.
292 pos_inds : list
293 Indices of the state vector containing the position info.
294 vel_inds : list
295 Indices of the state vector containing the velocity info.
296 state_args : tuple
297 Additional arguments for propagating the state.
298 ctrl_args : tuple
299 Additional arguments for propagating the state.
300 state_low : numpy array
301 Lower limit of each state.
302 state_high : numpy array
303 Upper limit of each state.
304 """
306 __slots__ = (
307 "dynObj",
308 "last_state",
309 "state",
310 "pos_inds",
311 "vel_inds",
312 "state_args",
313 "ctrl_args",
314 "state_low",
315 "state_high",
316 )
318 def __init__(
319 self,
320 dynObj=None,
321 pos_inds=None,
322 vel_inds=None,
323 state_args=None,
324 ctrl_args=None,
325 state_low=None,
326 state_high=None,
327 ):
328 """Initialize an object.
330 Parameters
331 ----------
332 dynObj : :class:`gncpy.dynamics.DynamicsBase`
333 Implements the dynamics equations, control, and state constraints.
334 pos_inds : list
335 Indices of the state vector containing the position info.
336 vel_inds : list
337 Indices of the state vector containing the velocity info.
338 state_args : tuple
339 Additional arguments for propagating the state.
340 ctrl_args : tuple
341 Additional arguments for propagating the state.
342 state_low : numpy array
343 Lower limit of each state.
344 state_high : numpy array
345 Upper limit of each state.
346 """
347 self.dynObj = dynObj
348 if self.dynObj is not None:
349 n_states = len(self.dynObj.state_names)
350 self.last_state = np.nan * np.ones((n_states, 1))
351 self.state = np.zeros((n_states, 1))
353 self.pos_inds = pos_inds
354 self.vel_inds = vel_inds
355 if state_args is None:
356 state_args = ()
357 self.state_args = state_args
358 if ctrl_args is None:
359 ctrl_args = ()
360 self.ctrl_args = ctrl_args
361 self.state_low = state_low
362 self.state_high = state_high