Building a Village That Thinks: Our Journey Creating an AI-Driven Life Simulation
How a simple apple orchard became a laboratory for emergent behavior
When we set out to build a simulation, we had one question: What if virtual characters could actually learn from their experiences?
Not scripted learning. Not pre-programmed responses. Real, emergent behavior that surprises even us, the developers.
This is the story of how we built that system, what we discovered, and why watching a tiny pixel person learn to avoid rotten apples became genuinely exciting.
The Beginning: An Apple Orchard
Every ambitious project starts somewhere small. Ours started with an orchard.
We imagined a single agent—let's call them the Villager—placed in a procedurally generated world with trees, a home, a pond, and one simple goal: survive.
The catch? We didn't want to tell the Villager how to survive. We wanted them to figure it out.
Our initial commit was humble: basic infrastructure, a character that could move, and trees that had apples. But from that seed, something remarkable grew.
The Core Philosophy: Don't Script It, Let It Emerge
Traditional game AI often works like a flowchart. If hungry, find food. If tired, sleep. Simple, predictable, and—let's be honest—boring.
We took a different approach called Utility AI. Instead of rigid rules, our Villager weighs every possible action against every other action, every single moment.
Here's the magic formula:
Utility = Need × Preference × Situation
When the Villager is moderately hungry, eating an apple might score 0.3. But when they're starving, that same apple shoots up to 0.8. Meanwhile, the urge to sleep might score 0.5 regardless. The highest score wins.
This creates surprisingly human-like behavior. The Villager doesn't just react—they prioritize.
Teaching Without Teaching: The Learning System
Here's where things get interesting.
Some trees in our world are healthy. They grow sweet, nutritious apples. But some trees are diseased—their apples are rotten and provide no benefit.
The Villager doesn't know which is which. They have to learn.
We gave them a simple memory system: a mental scorecard for every tree they've encountered. Each tree starts neutral at 0.5 (on a scale of 0 to 1).
- Eat a sweet apple? The tree's score goes up by 0.15
- Eat a rotten apple? The score drops by 0.30
Notice something important: bad experiences teach faster than good ones. This mirrors how real creatures (and people) learn—we're more cautious about danger than we are eager for rewards.
Over time, the Villager builds a mental map of the world. Good trees become favorites. Bad trees get avoided. All without a single line of code telling them "don't eat from the rotten tree."
What We Saw: Simulation Run Results
Let's look at actual data from our simulation runs.
Run 1: December 23rd - The Store-Then-Eat Pattern
We watched the Villager start with moderate hunger (0.30) and observed something unexpected: they kept walking home instead of eating.
Why? The math told us the story. At low hunger, the desire to store food for later outweighed the desire to eat now. Home was close. The stockpile was empty. Storing felt more urgent than eating.
Only when hunger hit 0.80—when the hunger-squared term in our utility calculation created sudden urgency—did eating finally win.
This wasn't programmed behavior. It emerged from the math.
Run 2: December 28th - Learning in Action
We tracked the Villager's relationship with a single tree:
The Villager learned to love this tree. But notice that tiny drop from 0.65 to 0.64 between bites? That's our memory decay system—the Villager slowly forgets, pulling all memories back toward neutral over time.
This prevents permanent biases. A tree that was once rotten might recover. The Villager will eventually give it another chance.
The Fog of War: You Can't Target What You Can't See
One problem with simple AI is "oracle knowledge"—the character somehow knows where everything is, even things they've never seen.
We solved this with a vision system that varies by terrain:
Mountains became natural scouting positions. Forests became mysterious, requiring exploration. The Villager had to discover the world, not just navigate a map they already knew.
The Food Chain: Fishing for Survival
Apples weren't enough. We added fishing.
But fishing required tools. Tools required materials. Suddenly, a simple "get food" goal became a planning challenge:
- Find sticks (2 seconds each)
- Craft fishing pole (10 seconds, needs 3 sticks)
- Travel to pond
- Fish (15 seconds, 70% success rate)
- Eat fish (way more filling than apples)
We didn't program this sequence. The Villager figures it out because fish are worth it—they restore 0.35 hunger versus 0.10 for an apple. The effort pays off.
But here's the twist: fish spoil in 8 simulated hours. Apples last nearly 5 days. The Villager has to weigh immediate reward against future security.
The World: Procedurally Generated Every Time
Each simulation run creates a unique 100×100 tile world using noise algorithms:
Different worlds create different survival challenges. A run might have abundant meadow trees nearby. Or the Villager might spawn in a food desert, forced to explore.
For the Game Developers: Technical Notes
Built with Rust and Bevy (an Entity-Component-System game engine), our simulation prioritizes:
Determinism: All simulation logic runs in FixedUpdate at exactly 60 ticks per second. Same seed, same world, same behavior. This enables debugging and reproducibility.
Decoupling: Simulation runs independently of rendering. We can fast-forward time without drawing a single frame, enabling "run ahead" features and narrative summaries.
Response Curves: Our utility calculations use exponential and sigmoid functions, not linear interpolation. This creates natural-feeling urgency thresholds.
Asymmetric Learning: Negative experiences (-0.30) outweigh positive ones (+0.15). This creates cautious, risk-averse behavior without explicit programming.
The codebase follows strict ECS patterns: data lives in Components, logic lives in Systems, shared state lives in Resources. No hidden coupling. No spaghetti.
The Development Journey: From Commit to Complexity
Looking at our git history tells the story:
Initial commit ← The dream begins
Implement Apple Orchard MVS ← Core simulation
Refactor to discrete Task System ← Clean architecture
Add fishing with inventory ← Depth through crafting
Add apple freshness mechanics ← Time pressure
Add procedural world generation ← Infinite variety
Add Fog of War and scouting ← Realistic limitations
Add SpriteForge rendering ← Making it visible
Add comprehensive documentation ← Sharing knowledge
Fix Transform corruption ← The unglamorous reality
Add world bounds checking ← Villagers escaping the map
Each commit represents hours of work, debugging, and iteration. The bug fixes at the end? Those taught us as much as the features. Coordinate system conflicts. Agents teleporting. Entities rendering in wrong positions.
That's the real journey of game development: building dreams, then fixing all the ways they break.
What Surprised Us
The "Hungry But Not Desperate" Window: There's a range of hunger (roughly 0.3-0.7) where the Villager makes interesting tradeoffs. Too low, and they ignore food. Too high, and they scramble for anything. That middle zone is where personality emerges.
Mountains as Strategy: We added extended vision from mountains almost as an afterthought. But it transformed gameplay—the Villager now uses high ground to scout for resources. We didn't teach them that.
Memory Decay Creates Forgiveness: Without decay, a single bad experience would permanently blacklist a tree. With decay, the Villager eventually reconsiders. This feels more alive.
Fish vs. Apple Economics: The Villager often ignores nearby apples to make the trek to the pond. The math says fish are worth it. Watching them choose confirms the utility system works.
What's Next
Our single Villager has shown us what's possible. The roadmap includes:
- Multiple agents competing and cooperating
- Social dynamics where Villagers form relationships
- Extended survival runs pushing the limits of sustainability
- Berry bush integration adding another food source
- Rotten tree learning deliberately testing avoidance behavior
We're also exploring narrative summarization—letting the simulation run for simulated weeks, then generating a story of what happened. "The Villager starved on day 14 after exhausting all nearby food sources and failing to discover the fishing pond."
The Real Lesson
What we've built isn't just a simulation. It's a reminder that complexity can emerge from simplicity.
Give a character needs. Give them choices. Give them the ability to remember. Then step back and watch.
They'll surprise you.
Village Sim is built with Rust and Bevy. The full source code and documentation are available on GitHub. If you're interested in agent-based simulation, utility AI, or emergent gameplay, we'd love to hear from you.
Thanks for reading. Now if you'll excuse us, there's a Villager learning to fish who needs watching.