from enum import Enum
class Orientation(Enum): # 16-wind compass directions
N = 0
NNE = 1
NE = 2
ENE = 3
E = 4
ESE = 5
SE = 6
SSE = 7
S = 8
SSW = 9
SW = 10
WSW = 11
W = 12
WNW = 13
NW = 14
NNW = 15
[docs]
class GameObject:
"""
The Parent Class of every detected object in the Atari games (RAM Extraction mode)
:ivar category: The Category of class name of the game object (e.g. Player, Ball).
:vartype category: str
:ivar x: The x positional coordinate on the image (on the horizontal axis).
:vartype x: int
:ivar y: The y positional coordinate on the image (on the vertical axis).
:vartype y: int
:ivar w: The width/horizontal size of the object (in pixels).
:vartype w: int
:ivar h: The height/vertical size of the object (in pixels).
:vartype h: int
:ivar prev_xy: The positional coordinates x and y of the previous time step in a tuple.
:vartype prev_xy: (int, int)
:ivar xy: Both positional coordinates x and y in a tuple.
:vartype: (int, int)
:ivar h_coords: History of coordinates, i.e. current (x, y) and previous (x, y) position.
:vartype h_coords: [(int, int), (int, int)]
:ivar dx: The pixel movement corresponding to: current_x - previous_x.
:vartype dx: int
:ivar dy: The pixel movement corresponding to: current_y - previous_y.
:vartype dy: int
:ivar xywh: The positional and width/height coordinates in a single tuple (x, y, w, h).
:vartype xywh: (int, int, int, int)
:ivar orientation: The orientation of the object (if available); game specific.
:vartype orientation: int
:ivar center: The center of the bounding box of the object.
:vartype center: (int, int)
:ivar hud: True, if part of the Heads Up Display, and thus not interactable.
:vartype hud: bool
"""
GET_COLOR = False
GET_WH = False
def __init__(self):
self.rgb = (0, 0, 0)
self._xy = (0, 0)
self.wh = (0, 0)
self._prev_xy = None
self._orientation = None
self.hud = False
def __repr__(self):
return f"{self.__class__.__name__} at ({self._xy[0]}, {self._xy[1]}), {self.wh}"
@property
def category(self):
return self.__class__.__name__
@property
def x(self):
return self._xy[0]
@property
def y(self):
return self._xy[1]
@property
def w(self):
return self.wh[0]
@w.setter
def w(self, w):
self.wh = int(w), self.h
@property
def h(self):
return self.wh[1]
@h.setter
def h(self, h):
self.wh = self.w, int(h)
@property
def prev_xy(self):
if self._prev_xy is not None:
return self._prev_xy
else:
return self._xy
@prev_xy.setter
def prev_xy(self, newval):
self._prev_xy = newval
@property
def prev_x(self):
return self.prev_xy[0]
@property
def prev_y(self):
return self.prev_xy[1]
@property
def xy(self):
return self._xy
@xy.setter
def xy(self, xy):
self._xy = (int(xy[0]), int(xy[1]))
@x.setter
def x(self, x):
self._xy = int(x), self.y
@y.setter
def y(self, y):
self._xy = self.x, int(y)
# returns 2 lists with current and past coords
@property
def h_coords(self):
return [self._xy, self.prev_xy]
@property
def dx(self):
return self._xy[0] - self.prev_xy[0]
@property
def dy(self):
return self._xy[1] - self.prev_xy[1]
@property
def xywh(self):
return self._xy[0], self._xy[1], self.wh[0], self.wh[1]
def _save_prev(self):
self._prev_xy = self._xy
# @x.setter
# def x(self, x):
# self._xy = x, self.xy[1]
# @y.setter
# def y(self, y):
# self._xy = self.xy[0], y
@property
def orientation(self):
return self._orientation
@orientation.setter
def orientation(self, o):
self._orientation = o
@property
def center(self):
return self._xy[0] + self.wh[0]/2, self._xy[1] + self.wh[1]/2
[docs]
def is_on_top(self, other):
"""
Returns ``True`` if this and another gameobject overlap.
:return: True if objects overlap
:rtype: bool
"""
return (other.x <= self.x <= other.x + other.w) and \
(other.y <= self.y <= other.y + other.h)
[docs]
def manathan_distance(self, other):
"""
Returns the manathan distance between the center of both objects.
:return: True if objects overlap
:rtype: bool
"""
c0, c1 = self.center, other.center
return abs(c0[0] - c1[0]) + abs(c0[1]- c1[1])
[docs]
def closest_object(self, others):
"""
Returns the closest object from others, based on manathan distance between the center of both objects.
:return: (Index, Object) from others
:rtype: int
"""
if len(others) == 0:
return None
return min(enumerate(others), key=lambda item: self.manathan_distance(item[1]))
@property
def properties(self):
"""
All the properties of the object in a list.
:return: The properties of the object.
:rtype: list
"""
ignore = ["properties", "GET_COLOR", "GET_WH", "xy", "wh", "prev_xy", "h_coords", "xywh"]
properties = [prop for prop in self.__dir__()]
[properties.remove(p) for p in ignore if p in properties]
return [prop for prop in properties
if not prop.startswith("_") and
not callable(self.__getattribute__(prop))]
[docs]
class ValueObject(GameObject):
"""
This class represents a game object that incorporates any notion of a value.
For example:
* the score of the player (or sometimes Enemy).
* the level of useable/deployable resources (oxygen bars, ammunition bars, power gauges, etc.)
* the clock/timer
:ivar value: The value of the score.
:vartype value: int
"""
def __init__(self):
super().__init__()
self._value = 0
self._prev_value = None
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = None if value is None else int(value)
@property
def prev_value(self):
if self._prev_value is not None:
return self._prev_value
else:
return self._value
def _save_prev(self):
super()._save_prev()
self._prev_value = self._value
@property
def value_diff(self):
return self.value - self.prev_value