Engine refactor

Refactored the core engine datastructures to be a bit less scary. There's no reason to use arrays over properly constructed objects -- JITs actually do better with objects with consistent properties.

This was only possible due to the test suite, thanks for that!

Need to do the standalone deserialization bits, but the inliner scripts associated with that aren't in the repo.

Goal: better optimizations, easier understanding of core algorithms/implementations separate interpreters.

Rules like `[ Objects ] -> []` could be optimized into and-ing a mask with level.dat. Rules with one group don't need to generate the cross-product tuples, and could directly apply matches instead.