Most aggressive AoE II AI
Age of Empires is a challenging game with a steep learning curve and a community of experienced players. I was never able to climb back to the 1000 ELO I was given when I first started playing multiplayer. Naturally, this makes me lean towards custom lobby games with friends or single-player scenarios where I can dominate the AI—no matter how “expert” it claims to be.
One of my favorite scenarios is starting a game in the Post-Imperial Age with infinite resources against a single expert AI, outproducing its army until it submits—almost like a deathmatch but more open-ended. The setup works well, but on some maps, the AI doesn’t behave the way I want it to, likely because it was designed for standard game settings. That makes it less challenging and a lot less fun, but I would still want to keep playing this scenario, because how fast paced and satisfying it is.
The only logical conclusion? I need to write my own expert AI system for AoE—one that plays exactly the way I want it to.
Challenge
I am building an AI to play a custom scenario game that I myself play a lot just for fun. I call it Army Rush: 500 pop, post-Imperial, all-visible game with infinite resources played in Amazon Tunnel against five players. I have won with selected civs against Moderate AI. The goal is to see if I can create an AI which can handle Hard or Extreme AIs.
AI intro
This is an introduction to the AoE II DE AI Expert System. Most of the information is based on documentation that comes with the game located here:
<Install Location>\AoE2DE\Docs\All
AI Expert System
A rule based system that uses a LISP like script to define a series of rules which are tested to perform various actions by the AI player.
Rules
Rules are if-then-do statements. The predicate can be compounded and the action can be any number of things including a way to disable the rule itself from further processing.
(defrule
(predicate)
=>
(action)
)
- Game parses all rules and does a rule pass, several times a second.
- Rule pass evaluates and executes actions where rule was true
- All rules remain active unless disabled by self.
(defrule
(true)
=>
(disable-self)
)
Facts
The AI Expert systems call these predicates, Facts. There are a lot of in-built facts that can be used to make scripting easier.
Actions
Actions are limited in a sense what a player can do in the game. Most of the actions are predefined as well.
- build a building
- train a unit
- send a chat message
- command a unit
- game command
Defconst
This lets you define constants so the code is more readable instead of having random numbers and strings everywhere.
Load
Helps you modularize the code and include files in the main AI script file. It also lets you load files randomly to build a more versatile AI player.
Preprocessors
C/C++ like preprocessors to conditionally load rules and files.
Conditional loading recognizes four directives:
- #load-if-defined
<system-defined-symbol>
- #load-if-not-defined
<system-defined-symbol>
- #else,
- #end-if
system defined symbol list is present in the documentation
Predefined Stuff
- System Defined Constant: Player specific variables populated by the game based on player selection and game settings
- Fact List: predicates to check various aspects of the game
- Action List: actions to be performed by the AI player based on the rules.
- Parameter List: Lists of parameters used in facts and actions.
AI Location
Location where the AI files are kept
<Installed Location>\AoE2DE\resources\_common\ai
File Structure
- One ainame.ai file to make it show up in the game drop down
- One main ainame.per file that contains the main logic
- Optional multiple *.per file to be included in ainame.per for modularity
Continuous Development
- Open the game and select you ai from the drop down as one of the player
- Add more player and set game properties as per the AI development goal
- Start the game and monitor
- Tab out of the game. Do not stop the game!
- Use a text editor to edit script
- Tab back into the game.
- Use the game menu to restart the game.
Challenge
I am building an AI to play a custom scenario game that I myself play a lot just for fun. I call it Army Rush, 500 Pop, post-Imperial, all visible game with infinite resources played in Amazon Tunnel against five players. I have won with selected civs against Moderate AI. The goal is to see if I can create an AI which can handle Hard or Extreme AIs.
Resources
- https://forums.aiscripters.com/viewforum.php?f=8&sid=6ee7f3108e4b3144e78b53c48fa33bb3
- https://userpatch.aiscripters.net/reference.html
- CPSB documentation
- https://aok.heavengames.com/cgi-bin/forums/display.cgi?action=t&fn=28
A blank AI
Created ranuzz.ai
and ranuzz.per
in the ai
directory. Just a check for civilization and a chat message to self to make sure that AI was loaded.
; Resign if selected civilisation is not franks
(defrule
(not (civ-selected frankish))
=>
(chat-local-to-self "I am not playing")
(resign)
(disable-self))
; Announce the ai to self
(defrule
(true)
=>
(chat-local-to-self "ranuzz ai. plays army rush only with franks v2")
(disable-self))
- Scout doesn’t move but later started following enemy scout once it came in its line of sight
- All three villager started moving
- one became lumberjack immediately
- rest of them just wandering aimlessly, most likely as civilian explorer
Enabling Scout
After exploring the documentation a bit, I found the very basic way to enable scouting.
(defrule
(true)
=>
(up-send-scout group-type-land-explore scout-opposite)
(disable-self))
Everyone’s a Builder
Since this is an infinite resource game there is no need for economy management and civilian gatherer. All I need is a builder. The inbuild strategy numbers and goals compel the AI to do something towards maintaining the economy and attacking the enemy. For my game I need civilians to be builders. Based on what I have found I have updated the following strategy numbers to make sure no villager is worrying about the economy.
(defrule
(true)
=>
(set-strategic-number sn-percent-civilian-builders 100)
(set-strategic-number sn-cap-civilian-explorers 0)
(set-strategic-number sn-cap-civilian-gatherers 0)
(set-strategic-number sn-maximum-town-size 500)
(set-strategic-number sn-food-gatherer-percentage 0)
(set-strategic-number sn-wood-gatherer-percentage 0)
(set-strategic-number sn-gold-gatherer-percentage 0)
(set-strategic-number sn-stone-gatherer-percentage 0)
(set-strategic-number sn-percent-civilian-explorers 0)
(set-strategic-number sn-percent-civilian-gatherers 0)
(set-strategic-number sn-cap-civilian-builders 50)
(set-strategic-number sn-minimum-town-size 500)
(set-strategic-number sn-total-number-explorers 10)
(disable-self))
Train Train Train
Enabled training with a fixed true
predicate so it doesn’t stop as long as I have buildings available. For villagers the population is capped at 30, but no limit on paladins.
(defrule
(civilian-population < 30)
(can-train villager)
=> (train villager))
(defconst gl-escrow-state 101)
(defrule
(up-can-train gl-escrow-state c: knight-line)
=>
(up-train gl-escrow-state c: knight-line)
)
Building Houses and Stables
With the initial three villagers I immediately assigned two on house building duty and one on stable duty. These are one shot rules and execute only once at the start of the game. As a result, as soon as the game starts two villagers start making houses and one goes out to build a stable.
(defrule
(true)
=>
(up-assign-builders c: house c: 2)
(disable-self))
(defrule
(true)
=>
(up-assign-builders c: stable c: 1)
(disable-self))
Later in the game as I get more villagers I increase the builders for stable. I don’t know how to specify a percentage yet so I manually added rules for all possible population numbers. Ther twenty plus rules where N ranges from 4 to 30.
(defrule
(civilian-population == N)
=>
(up-assign-builders c: stable c: N-2)
)
Buildings are continuously getting built as one finishes. I don’t know yet how to preplan building placement.
(defrule
(up-can-build 0 c: stable)
=>
(up-build place-normal 0 c: stable))
(defrule
(up-can-build 0 c: house)
=>
(up-build place-normal 0 c: house))
The Match
TBD