Hi all,
I’m happy to present Arceus.jl, a fork of a high-performance AliceRoselia’s original package Arceus, which introduces a novel way to resolve behaviors for entities based on their component sets—using magic bitboards, an optimization technique originally used in chess engines for O(1) move lookups.
Arceus.jl provides constant-time behavior resolution for ECS-style setups where entities are encoded as UInt64
bitfields. This is especially useful in games, simulations, or other systems where thousands of entities need to be matched to behaviors in real-time.
Explanations
You define a @traitpool
, which is a set of traits (characteristics influencing the entity’s behavior) and subpool (subset of traits and other subpool).
Once done, you define a new instance of a traitpool with @make_traitpool
and add the relevant traits, only those who describe your object, you can add and remove traits with @addtrait
and @removetrait
.
Once done, you create a new lookup function with @lookup
, you specify in the body how each traits affects the result when present. This may take a while the first time since it compute the magic number which is slow. But for any future run, you won’t have problems (unless you add or remove some test in the lookup function)
Now you are all set to create your lookup table with @make_lookup
.
Now for any instance of that trait pool, the resulting behavior for it’s set of traits will be retrieved in less than 10ns, even on a potato hardware.
Features
-
O(1) Behavior Lookup
Behaviors are precomputed for all possible component combinations, enabling instant retrieval. -
Magic Bitboard Caching
Magic numbers are expensive to compute, so they’re deterministically cached to disk (CSV), enabling fast startup on subsequent runs. -
ECS Compatibility
Works with any archetype-based ECS that represents components using bitmasks or similar binary encodings. -
Fine-Grained Bit Control
You can allocate custom bit ranges for component pools, set precise trait positions, and manage trait dependencies.
Example
Here’s a simplified usage example:
using Arceus
@traitpool "EndingTrait" begin
@trait start
@trait meetX
@trait gotSaber
@trait lostAlly at 2
@subpool side begin
@trait good
@trait evil
end
end
@make_traitpool GameEnding from "EndingTrait" begin
@trait start
end
@addtrait GameEnding begin
@trait meetX
@trait lostAlly
@trait side.good
end
f1 = @lookup END "EndingTrait" begin
val = UInt(0)
if @hastrait END.start
val |= 1
end
if @hastrait END.meetX
val |= 2
end
if @hastrait END.gotSaber
val |= 4
end
if @hastrait END.lostAlly
val |= 8
end
if @hastrait END.side.good
val |= 64
end
if @hastrait END.side.evil
val |= 128
end
return val
end
@register_variable f1
ending = @make_lookup f1
println(ending[GameEnding])
This allows sub-10ns behavior lookup on low hardware.
Known Limitations
- Heavy Initial Computation: Magic numbers are expensive to generate. It’s best to compute them once during initialization—after that, they are cached to a CSV file.
- 64-bit Limit: The system operates on
UInt64
, meaning you’re limited to 64 component flags. Beyond that, the magic bitboard trick doesn’t apply.
Links
- GitHub: https://github.com/Gesee-y/Arceus.jl
- Original author: AliceRoselia (@Tarny_GG_Channie)
Contribute
Contributions are very welcome—whether it’s suggestions, PRs, or feedback. And please consider supporting the original author of the system!
Summary
If you’re working with ECS architectures and need lightning-fast behavior resolution for complex trait combinations, Arceus.jl might be worth a look. Let me know what you think, and feel free to ask questions or open issues.