”Brencil” is a game my roomate and I are making, in which the player fights enemies that adapt to the player. The overall goal of our game is to make the experience of the game different for each player, and each playthrough a player has. It also has a secondary goal of reducing a player’s ability to cheese (Abuse the game mechanics to drastically decrease difficulty) the enemy AI by playing an unusual playstyle.
Since the making of this project, the focus of the game Brencil has changed significantly, and the code in this project will not resemble in the slightest the gameplay in the final version of Brencil. This project has a large focus on the genetic algorithm the AI uses to react to the player.
Genetic Algorithms are one of the most powerful tools in programming to date, as they give us the ability to replicate the actions of nature in a program. The way they operate is very similar in nature to that of natural selection. An initial population is instantiated, and a fitness function is used to determine the strongest individuals. They produce children, and those children may be used in future populations.
The game begins with an initial population of 100 enemies. The enemy’s stats are turned into a chromosome that can be passed down to future generations. I decided to implement the chromosome as an array of 5 floats, with each float holding a different stat: Attack, Defense, Speed, Attack Threshold, and Distance Threshold.[0]: attack: chromosome[1]: defense chromosome[2]: speed chromosome[3]: Attack Threshold chromosome[4]: Distance Threshold
This is a Debug Display, which shows the chromosomes above each enemy.
The fitness function is based mostly on the amount of damage the enemy deals to the player, and somewhat based on how much of the enemy’s lifetime was spent within 1 screens length of the player. This function will encourage enemies to spawn that do the best at getting close to the player, dealing damage.
Three separate player strategies were used to test the Genetic Algorithm.
An "Aggressive" strategy, where I seek to kill every enemy I see, a "Run Away" Strategy where I avoid each enemy I see, and a "Run away and Shoot" strategy, where I avoid enemies while also attacking them. These are the results after 1, 10, 20, and 30 Generations of the Algorithm.
Aggressive Strategy
Run away Strategy
Run away and Shoot Strategy
All three AI’s favor Speed, as they can’t deal damage if they can’t reach the player. The Run away strategy AI cares little for view distance, since I was moving so fast that I would engage enemies regardless of how far away they could see. I could go on for many more paragraphs about the reasons why these AI have the stats that they do. It can be assumed, however, that after 100’s of generations, even these different AI’s will converge to the same strategy, since many of the variables are strictly better to have higher than lower. For example, it is always better to have a higher attack, defense, speed, distThreshold, and always better to have a lower attack threshold.
As mentioned before, for this genetic algorithm, there is likely an objectively best strategy that the AI will converge to. This could be fixed by making these variables dependent upon each other. For example, the higher attack an enemy has, the lower the defense. Also, more complex chromosomes will produce more complex children. It’s completely possible that two completely different AI strategy come about within the same generation, each with a similar fitness value. My roommate and I aren’t entirely sure whether we will continue to work with genetic algorithms and ”Brencil”, but this project taught me a lot about the uses, and power of genetic algorithms, especially with respect to AI in video games.