I want to introduce my strategy game, "People's Conquest" and how I do AI and game balance

There's only need for one forum for now. Discuss development. No advertising or thinly veiled attempts at advertising. Create a single thread for your project and be prepared to show and tell.
Post Reply
EighthDayAdvent
Posts: 40
Joined: Fri May 08, 2020 2:33 pm

I want to introduce my strategy game, "People's Conquest" and how I do AI and game balance

Post by EighthDayAdvent »

I don't know where to begin. Asking me about this project is like asking someone "tell me all the words in the dictionary you know of, off the top of your head."

I started out wanting to make a casual strategy game for iOS about 5 years ago. About 18 months ago I saw that the project scope creep was getting enormous and that there was no way I would get something playable by my internal deadline. So, I set about making the codebase more moddable, and over time took more and more out of the code and put it into text files organized by scenario.

On startup when you go New, it gives you a choice of scenarios to load. They are so different that I would call them mods, but over time the changes are becoming large enough that I could call each one a separate game. The only commonality is the map and some map movement rules. They share an economic engine, but the way the economy engine is used is different for each scenario.

The project is called "Eighth Day Advent," and my first playable game I made with it is "Franks and Goths," a troll game that I wanted people to think was about Roman legions (you lay as a dude named Frank fighting Goth music fans who want to destroy Canadian cities). This first effort was very important as I got better at modding, and I also was able to essentially mod out all the unfinished features of the base game, in addition to the UI that was not needed.

This really helped because I stopped having a UI artist to go to about 18 months ago, so I cut out the unfinished parts and it really helped. The best part about the modding approach is that it is easier to cut things when you know they aren't really gone - just modded out. I can go back to them so its easier to part with.

The first game from it that I really like a lot is "People's Conquest." It is a take on "Strategic Conquest," a game from the 1980s. In Strategic Conquest, you play as Black, AI is Red, and there are Green neutral cities. In People's Conquest, you are Red, the AI is Black.

Here is a video of me taking a neutral city by first bombing it and then sending in infantry:

The AI is still the same as in the first game - I kept the same AI and soon I will work to building a better AI specific to this game. All the AI knows how to do is conquer cities, produce infantry, and attack the human player if spotted. It doesn't know how to use artillery or planes.

Here is a picture of the AI conquering neutral cities:

But, more work needs to be done on the AI, so this is going to be about how I implement AI in my game. I really believe if you need a strategy game AI, you need to implement it in a similar way. After talking about how I did AI, I will talk about how I go about game balance and how to best adjust things to get the outcomes you want.

On startup, the game reads in mod folders, and they can be added after a compile. It reads in unit graphics, sounds, and many other text files that tell it how to construct the game. The screenshot below shows some of it.
Screen Shot 2022-07-07 at 2.23.15 PM.png
Screen Shot 2022-07-07 at 2.23.15 PM.png (2.38 MiB) Viewed 830 times
So in the "sacred.text" file, that is the main build file. But other files can be called from that file as well. Societies are loaded in with LOAD_SOCIETY(name) and the name is the folder with that society. The folder contains a text file that tells the game how to make it, and there are graphics on what the units are. They can be animated and are read in with a image_1 image_2... format (image can be anything, as long as it ends in _ and the frame number).

Music is also loaded in as well in the sounds.

Actions folder is the AI folder, and I opened up an example of a normal_ai.text that contains the AI for People's Conquest.

However, AI is linked to units so an airplane and a tank can have separate AIs. This normal_ai.text file is the AI for the leader unit, which is a hidden unit that you never see. That is the unit that makes the production decisions for the cities.

The middle text file in the image shows how units are created in the files. The unit info contains what sound to use when it moves, how it moves, what the unit can do, and what image folder location to use (it can be animated).

The way I do the AI is by behavior trees. Each BEGIN_MOVEMENT - END_MOVEMENT block contains a priority for the node, the condition on which to execute it, and what it does. The nodes are read in and the game code retains the highest priority node with a true condition and executes it. It continues repeating until the movement points are run our or the time is up (1 second, its important to have a timeout when reading in text files as its easy to make it loop forever accidentally).

The priority level can be set to any number so to add a node between 1 and 2, I just do 1.5. Technically, you are limited to 5 decimal places I think but I figure if anyone gets to the point where they are typing 1.512345234 I'll just go back and add more precision to the node process.

As far as balance in things like combat:

The trick is to use a logistic function. Here is my implementation and an example usage showing how you use the probability output.
Screen Shot 2022-07-07 at 2.37.22 PM.png
Screen Shot 2022-07-07 at 2.37.22 PM.png (813.97 KiB) Viewed 830 times
I use a variable length function as its important to get the core math correct so you minimize mistakes. I use this for a lot of things. Combat odds and outcomes are multiple models. I maintain the models in MS Excel and validate output that way to ensure I'm getting what I intend. It is easy to make a mistake - for instance the input variables all need to be floats. If you send it an int it will mess up.

The nice thing about these functions is you can manage the output even if the core function is junk. One thing I do to adjust balance is I take one logistic function output and use it in ANOTHER logistic function as the only variable and play with the values in MS Excel to get the output to be more reasonable.

The nice thing about logistic function is you can get balance by design.

Lets say your combat is based on Unit Tech and Unit Size.

Lets call Unit Size - S and Unit Tech - T.

Lets say two units have Size 10 and Tech 2, and are in combat.

You can get balance, 50% odds, simply by building a model with no intercept, and use the difference of the S and T as inputs. If the difference is 0 for both, the model outputs 50% odds by default. Play with it in excel and try to get the range of outputs you want. You can transform variables too - maybe you want Size to be the square root of size. This would make larger sizes beneficial at a slower rate. The choice is up to you.

In combat, you often want to link models outcome.

I use an expected loss format and a Monte Carlo for combat.

When two units attack, or a unit attacks a village, here are the steps:

1) Obtain base probability odds of a kill, and you might also want odds of "draw," i.e neither side kills, and odds of being killed.
2) loop through and apply odds in a trial, record results. You might need to update some variables that change over the course of combat (lets say a 2 to 1 ratio becomes a 1:1 halfway through).

I typically loop through and base it on ratio of one side to another. If if there are more defenders, its the attacker that has the 1 to many ratio. Kills then are the whole ratio. But the models are also build on the ratios - that is important. If there are more attackers then the odds are flipped (1-whatever).

Its important to remember that models are creative, and its important to be able to build many, ensure they are behaving like you want, and to also be able to adjust them as needed. For example, if the economy develops too fast - I just add in the ability to mod the economic model outputs down (or up). The actual model itself can be pretty imperfect but as long as you can adjust the outputs to get the result you want, its good enough.

Generally, you want your models to have the first and second derivatives you want. What that means in practical terms is you want more of something to be better, but not at such a rate that the people with less are so hopelessly behind. But, you may want it to be only first derivative positive. If you make the second derivative positive though, what that means is more of something tends to REALLY leave the people with less behind.

So, as an example lets say you have unit with Size 100 fighting a unit of size 50. An example of if the second derivative were 0 and first derivative positive, then 100 could be 2x as strong. But if the second derivative is positive, then 100 could be 2.5x or 3x stronger. Maybe you want that - I don't know. But I prefer to make the second derivative negative, which means 100 is maybe only 1.5x stronger.

There are ways to accomplish this in logistic modeling in probabilities, but also beware that logistic models tend to blow up and get towards 1 or 0 really fast at their extremes. It is important to tailor your model to the range of values you expect to use.

For example, if your max unit size was 1000, it doesn't really matter if the model blows up at 2500. But if it blows up at 200, what that may mean is that there is little practical benefit to having more than 200. Same way with the other size - going to zero. If you need to have 20% of the size of your opponent to have any impact, there is little difference between 0-20%.

Anyways, I do data analytics and modeling as a full time job so if you have any questions, let me know.
Post Reply