Simulations are fun. Programmers love designing complex systems, with lots of exciting moving parts and little wiggly bits that go sproing when they should (and often when they should not). A lot of people have built The Game of Life at some point in their programming career, and for good reason. It’s impossibly cool seeing complex systems form from simple rules.
The trap, however, is believing that complex systems are necessary. Developers in general are very good at building things they won’t need, which is why there are a number of chanted mantras and silly little acronyms. They try to protect us from ourselves. Hah. Good luck with that.
A trick (there’s no trick to it! It’s just a simple trick!) that I’ve tried to use when I feel myself slipping down the rabbit hole of overdevelopment is, simply, to fake it. Not just for those with bad lovers anymore, faking it allows the crafty developer to pull the wool over the eyes of users, in a way. Rather than determine a complex series of interactions, why not just leave it up to a single rand() call? You can even still have all of the false complexity there, exposed to the user in some way, but simply ignore it when it comes to getting work done.
The benefits to this are astounding. It separates concerns in an almost-too-obvious way, breaking apart what could end up ugly and complicated if done ‘correctly’. Refactoring and reworking a more complex system later is much simpler when all of the endpoints of a system are already in place.