cxr

Math, code, people

Sen pro Kyne. Emotionally overavailable.

Yeah I mean https://cohost.org/sen-guide but I'm going through some things right now

posts from @cxr tagged #code

also:

You can get the full details in the readme for cxr.state.state.

In summary, I realized while working with pygame that I needed a consistent and repeatable way to give stateful behavior to game objects. So I figured out a way to utilize a doubly-nested function as a decorator. So I can do something like this...

@player.add_state("falling")
def player_falling(event):
    self.gravity += 0.1
    # etc, behavior goes here

And that's the usual boilerplate pretty much entirely eliminated.

The decorator is a bit hard to grok, but take a look and then I'll explain it:

def add_state(self, key):

    def outer_wrapper(f):

        def inner_wrapper(event):
            f(event)

        if key in self._states:
            raise StateError(f"State add failed: {self.name}({self.key}) StateManager already has a state with key {key}")
        else:
            self._states[key] = inner_wrapper
            if not self._current:
                self._current = self._states[key]
                self._current_key = key

    return outer_wrapper

The important behavior is that if your decorator is a function that a) takes arguments and 1) returns a function, then that returned function is what is applied to the decorated function. So @player.add_state("falling") is calling the add_state function, which returns outer_wrapper, and that function is applied as the decorator which catalogues the state accordingly. This pattern utilizes something called a closure.

The decorator for the controller (the core function for default behavior and whatnot) is much simpler:

def controller(self, f):
    self._controller = f

And decorators really CAN be that simple. Its applied behaviors can be counterintuitive, but if you're finding yourself wrapping a lot of unrelated stuff in very similar-looking stuff, a decorator is probably what you need.