So, Here's What I'm Thinking...
- Lex C
- Apr 13
- 9 min read
As I've mentioned, I'm a big fan of 4X games. So when I started work on my personal project, it was a 4X.
Introducing Flatspace (Working Title)
Developed in Unity.

The project is in its early stages, still. I have the basic world structure, game traversal mechanism and minimal UI development.
At some point, I'll talk about all of the design decisions and road bumps I encountered. Path finding decisions, my flirtation with addressable assets, using UI Toolkit, the map designer, and the ScriptableObject apocalypse are all worthy topics. Today though, I want to talk about my AI structure.
AI Caveat
First of all, I know that 'AI' is a loaded term with a lot of baggage these days. That's not what I'm talking about here. When I say 'AI' I am referring to game AI, not LLMs or agentic AI.
In other words, the 2020 definition of AI, not the 2022 definition.
Brain First
I decided to develop the AI first before the user control aspects. First, because that's the part of the game that most interests me. Second, because I think that's the best way to figure out what the game is. And third, because I can get pretty far with just text output, punting the player UI work downfield.
So how does the brain work?
Some initial decisions I made:
Action only happens at planets, not open space.
Each game entity should be as self contained as possible
All communication between game entities occurs through a very formalized interface
Localized Action
To a certain extent, this one was driven by laziness. No deep space interceptions. No full open world navigation. Travel can only be interrupted at specific way points.
As a side effect, that decision yields a certain level of commitment to the AI's actions.
It also led me to start lanes. I like star lanes.
Each planet is connected to a limited number of other planets. Ships can travel from one connected planet to any other, but they will pass through some number of intermediate way points on the way there.
It does put a burden on map design, but not an onerous one. Until I try to add an algorithmic map generator, of course.
Encapsulation
AI micro management can quickly become a nightmare to debug and tune. I know, I've been there. I didn't want that.
I don't want my top level AI to control food production on individual planets, for instance. Each planet knows how to best handle their own production. The top level AI can function effectively with only knowledge of end results. Does the planet need food? Did it produce a surplus of consumer goods. Does it have enough population to consider colonizing a neighbor?
Under this stricture, the top level can concentrate on overarching goals, rather than minutia.
Structured Communication
Compliments the strong encapsulation. I've worked on games with wide open game entity interaction. Stuff easily falls through the cracks, get lost, and becomes unmaintainable tribal knowledge. I'm all for emergent behavior, but give me a narrow pipe to limit the systems responsible for tuning.
More singular pipes mean easier debugging, too.
Initial Planet Implementation
Having spitballed a map and space lane structure, I started on planets.

I created about 4 different planet types, primarily differentiated by the amount of resources generated. This planet type is balanced, this type produces research but not a lot of industry, this type produces industry but not a lot of food, etc. I also created a goldilocks planet type (good at everything), called 'Prime' to serve as a rich starting location for each player.
Each planet is driven by a planetary strategy to drive its updates. Initially strategies were 'balanced' (does some of everything) and 'growth' (population growth and expansion).
Later, I added more extreme planet types with appropriate custom strategies.
OMG What Have I Committed Myself To
It was at this point that I realized the exact scale of this undertaking, and started to panic. There was just sooooo much to make.
Once that panic had passed and I could think rationally again, I directed my focus to a limited subset of resources and AI behaviors. Just to get the basic mechanisms solid. Where I could not avoid using other game systems, I'd create bare bones abstractions.
Once the basic structure was solid, I assumed, I could expand it.
I picked 2 resources to start, Food and Grotsits (extra points if you know the source of the word 'grotsit'). I suppose that Grotists will eventually become money, but for now, I'm viewing it as consumer goods, used to maintain planet morale.
I also focused on a single, top level, AI state, 'Growth'. I wanted to make the AI able to expand to cover the entire map if there was no external influence.
<deep breath> Ok, this was a scope I could handle.
Planet Updates
Next was the update step for the more balanced planets (well, just the Prime type, closely followed by 'Normal'). Once I was sure that individual planets could support themselves, I could turn to the larger, driving AI.
It took a while, and a lot of tuning (see ref. ScriptableObject apocalypse), but eventually, Prime and Normal planets wouldn't starve, and produced enough grotsits so keep their morale up. The extreme planet updates followed shortly thereafter.
Planets would produce their main resource, and attempt to keep their population fed and happy.
Time to move on to the higher level AI.
Where Does the Brain Live
Given my driving principles above, I created a Game AI class that would handle the high level thinking, and a map class for it, which contained an abstraction of the game world. Realizing that a single game AI was insufficient, I added a Player AI class to handle individual player decisions.
Keeping with my design philosophy, the Player AI concentrates on driving goals. These goals are very general. 'Expansion', 'Consolidation' and the like. Again limiting my initial scope, I chose 'Expansion' for my proof of concept. It also game me a benchmark for success. If, in the absence of external influence, the Player AI expands to cover the entire map, it is successful.
So what about the Game AI, then? Was it still needed? Yes it was.
Game AI now functions as the communication management interface between the game world (just planets at this point) and the individual Player AIs. It takes output from the game object upddates, passes it to the Player AI, and Processes the player AI output.
Player AI and game objects remain blissfully ignorant of each other. Score points for encapsulation and limited communication.
I now had a Game AI that handles communication between game objects and Player AI. But what would be the actual mechanism for that communication? I got to work on that next.
Events, Actions, and Orders
After a period of obsessive thinking, I made some decisions.
Decision 1. Planets would not expose internal workings, they would only post result Events.
I have a food shortage/surplus, I gained/lost a population, I have reached my maximum population, and so forth. Game AI collects those Events and distributes them to the Player AI(s).
Decision 2. Player AI would only process the events directly related to it's owner. No crosstalk. Game AI filters the events by player and provides that filtered list to the Player AI.
Decision 3. Player AI would return a list of atomic Orders for the Game AI to execute. These Orders could either execute immediately, or be scheduled to execute on a future turn. Order management is handled by the Game AI. That means Player AI can 'fire and forget' the Orders. Game AI handles the actual scheduling and processing of those orders.
I was feeling pretty good about the distribution of labor at this point..
No Strategy Survives First Contact With the Enemy
The basic structure was in place, now I could put it to the test. That would show where my system needed improvement.
As the growth strategy progressed, I realized 2 things.
First, my initial planet updates were insufficient. Determining that a planet had a surplus/shortfall for the current turn not enough. Planets would ping pong between food surplus and shortfall and growth was curtailed. Or they would to give up too many grotists this turn to keep their people happy next turn.
So I added predictive code into the planet update to create a desired 'reserve' of resource.
Problem solved.
Second, the atomic Orders were too granular for the Player AI to make coherent decisions. Update and maintenance of the process became unworkable very quickly.
For example, the decision to colonize a planet initially required:
A 'reduce population' order on the origin planet, execution immediate, to represent the colonist leaving
A 'transfer population' order on the target planet, execution delayed by travel time, which would add a population to the target planet when executed
Because of the delay in the transfer order, the target planet would be considered a valid colonization target for the during the intervening turns. Meaning that multiple colonization sequences would be triggered for it.
So I added:
A 'colonization in progress' order on the target, execution immediate, that would remove the target planet from future colonization decisions.
A 'clear colonization in progress' order on the target, execution delayed, that would clear the colonization flag
Retrofitting player AI colonization pattern decision to handle the new orders, I saw there was a clear difference between the colonization decision and the colonizing execution. Those 2 aspects were too tightly coupled in my existing implementation.
So I split Player AI updates into 2 separate phases, 'Decision' and 'Execution'.
I added the concept of an 'Action' to be the output of the Decision phase and input to the Execution phase. Actions represent high level choices that require a series of orders to fully execute. The Decision phase of player AI only output actions such as 'colonize planet' or 'ship food here'
The Execution phase took those actions and converted them into the orders, passing them back to Game AI for scheduling and implementation.
This separation worked. Decoupling the decision and execution phases allowed me to finish the POC work very quickly.
To recap graphically, the final AI flow is:

Did It Work
Well, yes, it did. Thank you for asking.
I met my goals of a growth POC state successfully. For example, I added a colony ship requirement to the colonization process without touching the Decision phase at all. Research and production mechanisms were also added with no modification to the Game AI/Player AI structure. New decision types were needed, of course.
Here you can take a look at the growth POC in action.
Early game:
The first colonization choice is planet Farm 0. It's immediately short of grotsits, so Prime 0 sends a shipment to Farm 0, at the same time it's colonizing Normal 1.
By the end of the video, I have occupied 5 planets, and regular resource shipments are occurring.
Here's an example from later in. Watch the red wave engulf the galaxy.
Success! Player AI will expand to cover the entire map if there is no external influence.
Next step: the Consolidation strategy. I'll let you know how that goes.
Last Notes
Just some final thoughts.
A Glaring Omission
You may have noticed that I didn't talk about how Player AI makes it's decisions. I felt it deserved its own post, because I think it's that cool. Watch for that post follow shortly.
I Broke My Own Rule
Currently, Player AI does break my encapsulation requirement by querying planet status directly.
Well, directly through the AI map. It checks flags that are set on planets (like the 'colonization in progress' flag).
It's a little disappointing (but only a little). After all, It's just flag checking, nothing deeper.
I'm debating whether or not to move the flag data into AI map, instead of keeping it on the actual planets. That would keep the encapsulation where I want it, at the cost of duplicate data and potential out of sync problems.
Ah, the eternal conflict between functionality and elegance rears its ugly head.
Emergent Behavior
Lastly, I saw some intriguing emergent behavior in the system.
When a planet ships its resource surplus to a shortage planet, it ships its entire surplus. Perhaps a naïve choice, but one with an interesting consequence.
Planet A has a large food surplus. Planet B has a minor shortage. Planet A ships its entire surplus to Planet B. When it arrives, Planet B has a significant stockpile of food. It's got enough food for a while.
That's good as far as it goes.
But, think on this:
Surplus calculations are made using produced food and stored food, not just produced food
If a planet had enough food to cover current and projected needs, it has a surplus
Shortage fulfillment is based on distance
So let's add a Planet C to the mix. Planet C is closer to Planet B than it is to Planet A. Planet C also has a food shortage. After current and projected consumption, Planet B has a huge stockpile of food left over from the shipment it received from Planet A.
Given the rules, the shortage on Planet C is fulfilled by the food on Planet B, not Planet A. Planet B functions here as a 'food warehouse' for close planets.
Look, Ma! I've created supply lines!
I'm thrilled about this because this gives me a 'handle'. Something I can use to differentiate Flatspace from the metric ton of other 4X games out there, if I lean into the 'supply lines' concept hard enough.
But that's a discussion for another day.
Peace out,
-- Lex

Comments