Comment by ogig
11 days ago
I've spent the last months building a videogame: Cardume [1], its a pvp game where 2 players battle using a swarm of thousands of cells. It uses Reynold's boids, Couzin flocking behaviors and diffuse fields to generate a mini ecological simulation each game. The sim is pretty well optimized, handling 12k agents at 60fps in a medium hardware machine. It also has some pretty cool visuals. Any feedback appreciated, the Store went page online this last week and I'm still working on the presentation.
Awesome game! Small suggestion, for each paragraph that is in your description, you can add a small gif-style webm video. It's kind of expected now for Steam games, and specially because it's such a unique game it would make an easier introduction to the concept. Looking forward to play it :D
Thank you very much. Will definitely add those gif-style sections!
Hi ogig, funilly enough I was also running this boids experiment a few months ago. Managed to get around 8k at 60 fps on 1 thread on my cpu, using golang.
My goal for this experiment was to encode the optimal cache data structures into meta programming generators such that claude can write high level DSL and generate down to this level of simulations. I am curious if you had such an approach also.
That's very interesting, thanks for commenting. Not my approach, I have a multithreaded Rust kernel (SoA, spatial hash, bounded-kNN, rayon on some passes). The agents sit on top of several diffuse fields, an econ layer and combat logic, all of it needing bit-determinism for the netcode.
I do use some metaprogramming, but as safety rather than generation: a declarative macro derives the modifier struct, defaults, parsing and wire order from a single list, and feeds hash/save/resync so I can add mechanics at a high level without being able to desync the sim. Also a set of probes help me test for correctness and speed after a change. So seems like I'm taking an iteration speed approach with safeguards, checking after the fact, while yours is optimizing beforehand by trying to encode optimal structs. That's fascinating, it will probably occupy my head for a good portion of the day, thanks again.
I went full meme, and stacked like 3 level of generators on top of eachother.
Level 1: A basic generator does the same stuff: Raw Structs to SoA + Wire Codecs + Bitfields to not waste space.
Level 2: Then on top of this one, I got a generator for relation between entities.
Level 3: And on top of this one one for "game" design and schema behavior.
And then all this runs on the simluator engine that handles Collision, Flow Fields, Events, Genetic Algorithms, Networking, etc.
The flow is something like this:
You ask claude to read a story, novel, w/e and extract relations, entities, etc.
Then it edits a Schema.go with the High Level DSL adnotations, like 10 lines per entity.
Then I run the Level 2 Generator: That creates the primitives and a high level API.
Then it combines the high level API rules in main.go and just compiles it to the final gamestate.
And runs benchmarks to see how many allocs/ops per entity are.
And one more fun thing, it encodes the gamestate on 1400 bytes, in order to fit on a UDP datagram such that it can support networking, quite fun. Once you get the primitives correct.
For all this + networking + disk i/o; works on 4 cores (threds) only. But the simulation in itself is on 1 core 1 thread. The networking offloaded to 1 core (1 thread), and disk i/o to a different core (thread), and the painting on screen (to a different core (thread))