# Path: magical/phys_vars.py
# class PhysicsVariablesBase(metaclass=_PhysicsVariablesMeta, _pbm_skip=True):
#     """Base class for physics variables with convenience methods for sampling
#     random variables, instantiating from defaults, etc."""
#     variables: collections.OrderedDict
# 
#     def __init__(self, *, _var_values):
#         """Constructor for classes holding physics variable values. This should
#         NOT be called directly. Instead, call the sample() or defaults()
#         methods, as appropriate."""
#         if _var_values.keys() != self.variables.keys():
#             raise ValueError("must supply all & only given variable names")
#         for k, v in _var_values.items():
#             assert not isinstance(v, PhysVar), \
#                 f"are you sure '{k}' is meant to be a PhysVar?"
#             setattr(self, k, v)
# 
#     @classmethod
#     def defaults(cls):
#         """Return a new instance of the class with default variable values."""
#         return cls(
#             _var_values={k: v.default
#                          for k, v in cls.variables.items()})
# 
#     @classmethod
#     def sample(cls, rng):
#         """Return a new instance of the class with randomly sampled variable
#         values."""
#         return cls(
#             _var_values={k: v.sample(rng)
#                          for k, v in cls.variables.items()})
# 
#     def __repr__(self):
#         pairs = ', '.join(f'{k}={getattr(self, k)}' for k in self.variables)
#         return f'{self.__class__.__name__}({pairs})'
# 
# class PhysVar:
#     """Container for the default value and randomisation bounds of a physical
#     variable. Randomisation is always uniform."""
#     def __init__(self, default, bounds):
#         assert len(bounds) == 2, bounds
#         lower, upper = bounds
#         assert lower <= default <= upper, (lower, default, upper)
#         self.default = default
#         self.lower = lower
#         self.upper = upper
# 
#     def sample(self, rng):
#         result = rng.uniform(self.lower, self.upper)
#         return result
# 
# Path: magical/style.py
# ARENA_ZOOM_OUT = 1.02
# 
# COLOURS_RGB = {
#     # I'm using Berkeley-branded versions of RGBY from
#     # https://brand.berkeley.edu/colors/ (lightened).
#     'blue': lighten_rgb(rgb(0x3B, 0x7E, 0xA1), 1.7),  # founder's rock
#     'yellow': lighten_rgb(rgb(0xFD, 0xB5, 0x15), 1.7),  # california gold
#     'red': lighten_rgb(rgb(0xEE, 0x1F, 0x60), 1.7),  # rose garden
#     'green': lighten_rgb(rgb(0x85, 0x94, 0x38), 1.7),  # soybean
#     'grey': rgb(162, 163, 175),  # cool grey (not sure which one)
#     'brown': rgb(224, 171, 118),  # buff
# }
# 
# def lighten_rgb(rgb, times=1):
#     """Produce a lighter version of a given base colour."""
#     h, l, s = colorsys.rgb_to_hls(*rgb)
#     mult = 1.4**times
#     hls_new = (h, 1 - (1 - l) / mult, s)
#     return colorsys.hls_to_rgb(*hls_new)


import abc
import collections
import functools
import inspect
import gym
import numpy as np
import pymunk as pm
import magical.entities as en
import magical.gym_render as r
from gym import spaces
from gym.utils import EzPickle
from magical.phys_vars import PhysicsVariablesBase, PhysVar
from magical.style import ARENA_ZOOM_OUT, COLOURS_RGB, lighten_rgb
        class C(…, EzPickle):
            @ez_init()
            def __init__(self, spam, ham, …):
                …"""
    assert len(args) == 0 and len(kwargs) == 0, \
        "ez_init takes no args at the moment (use `@ez_init()` only)"

    def inner_decorator(method):
        # sanity checks
        msg = f"Got function {method}; should only be used to decorate " \
              f"__init__() methods of classes"
        assert inspect.isfunction(method), msg
        assert method.__name__ == '__init__', msg

        @functools.wraps(method)
        def wrapper(*args, **kwargs):
            self_var = args[0]
            EzPickle.__init__(self_var, *args[1:], **kwargs)
            return method(*args, **kwargs)

        return wrapper

    return inner_decorator


class PhysicsVariables(PhysicsVariablesBase):
    """Default values & randomisation ranges for key physical parameters of the
    environment."""
    # robot_pos_joint_max_force = PhysVar(3, (2.5, 4))
    robot_pos_joint_max_force = PhysVar(3, (2.2, 3.5))

# Path: magical/phys_vars.py
# class PhysicsVariablesBase(metaclass=_PhysicsVariablesMeta, _pbm_skip=True):
#     """Base class for physics variables with convenience methods for sampling
#     random variables, instantiating from defaults, etc."""
#     variables: collections.OrderedDict
# 
#     def __init__(self, *, _var_values):
#         """Constructor for classes holding physics variable values. This should
#         NOT be called directly. Instead, call the sample() or defaults()
#         methods, as appropriate."""
#         if _var_values.keys() != self.variables.keys():
#             raise ValueError("must supply all & only given variable names")
#         for k, v in _var_values.items():
#             assert not isinstance(v, PhysVar), \
#                 f"are you sure '{k}' is meant to be a PhysVar?"
#             setattr(self, k, v)
# 
#     @classmethod
#     def defaults(cls):
#         """Return a new instance of the class with default variable values."""
#         return cls(
#             _var_values={k: v.default
#                          for k, v in cls.variables.items()})
# 
#     @classmethod
#     def sample(cls, rng):
#         """Return a new instance of the class with randomly sampled variable
#         values."""
#         return cls(
#             _var_values={k: v.sample(rng)
#                          for k, v in cls.variables.items()})
# 
#     def __repr__(self):
#         pairs = ', '.join(f'{k}={getattr(self, k)}' for k in self.variables)
#         return f'{self.__class__.__name__}({pairs})'
# 
# class PhysVar:
#     """Container for the default value and randomisation bounds of a physical
#     variable. Randomisation is always uniform."""
#     def __init__(self, default, bounds):
#         assert len(bounds) == 2, bounds
#         lower, upper = bounds
#         assert lower <= default <= upper, (lower, default, upper)
#         self.default = default
#         self.lower = lower
#         self.upper = upper
# 
#     def sample(self, rng):
#         result = rng.uniform(self.lower, self.upper)
#         return result
# 
# Path: magical/style.py
# ARENA_ZOOM_OUT = 1.02
# 
# COLOURS_RGB = {
#     # I'm using Berkeley-branded versions of RGBY from
#     # https://brand.berkeley.edu/colors/ (lightened).
#     'blue': lighten_rgb(rgb(0x3B, 0x7E, 0xA1), 1.7),  # founder's rock
#     'yellow': lighten_rgb(rgb(0xFD, 0xB5, 0x15), 1.7),  # california gold
#     'red': lighten_rgb(rgb(0xEE, 0x1F, 0x60), 1.7),  # rose garden
#     'green': lighten_rgb(rgb(0x85, 0x94, 0x38), 1.7),  # soybean
#     'grey': rgb(162, 163, 175),  # cool grey (not sure which one)
#     'brown': rgb(224, 171, 118),  # buff
# }
# 
# def lighten_rgb(rgb, times=1):
#     """Produce a lighter version of a given base colour."""
#     h, l, s = colorsys.rgb_to_hls(*rgb)
#     mult = 1.4**times
#     hls_new = (h, 1 - (1 - l) / mult, s)
#     return colorsys.hls_to_rgb(*hls_new)


# Path: magical/base_env.py
import abc
import collections
import functools
import inspect
import gym
import numpy as np
import pymunk as pm
import magical.entities as en
import magical.gym_render as r
from gym import spaces
from gym.utils import EzPickle
from magical.phys_vars import PhysicsVariablesBase, PhysVar
from magical.style import ARENA_ZOOM_OUT, COLOURS_RGB, lighten_rgb

        class C(…, EzPickle):
            @ez_init()
            def __init__(self, spam, ham, …):
                …"""
    assert len(args) == 0 and len(kwargs) == 0, \
        "ez_init takes no args at the moment (use `@ez_init()` only)"

    def inner_decorator(method):
        # sanity checks
        msg = f"Got function {method}; should only be used to decorate " \
              f"__init__() methods of classes"
        assert inspect.isfunction(method), msg
        assert method.__name__ == '__init__', msg

        @functools.wraps(method)
        def wrapper(*args, **kwargs):
            self_var = args[0]
            EzPickle.__init__(self_var, *args[1:], **kwargs)
            return method(*args, **kwargs)

        return wrapper

    return inner_decorator


class PhysicsVariables(PhysicsVariablesBase):
    """Default values & randomisation ranges for key physical parameters of the
    environment."""
    # robot_pos_joint_max_force = PhysVar(3, (2.5, 4))
    robot_pos_joint_max_force = PhysVar(3, (2.2, 3.5))

    robot_rot_joint_max_force = PhysVar(1, (0.7, 1.5))