For our first chapter, let's start off with a fun example to show how you can use DUC to send knights to raid enemy lumber camps.
DUC works similar to how as a human player plays the game. To do anything in the game, you first select some objects. Then, you give those objects an action or check information about them. This two-step process is analogous to how you use DUC. To tell the AI to send its knights to raid a lumber camp, first we need to make some code to pick the knights we want to attack with. Then, we need to find an enemy lumber camp to target and tell the AI's knights to attack.
Of course, the code in these tutorials could be improved or expanded upon, but it will give you a solid foundation to make your own DUC army control.
First, let's create some defconsts to name and initialize the goals and timer we will use in our code. Also, let's store the location of our TC and determine which enemy is the closest to us. That will be the player we attack. Lastly, if we have at least 6 knights, set g-knight-raiding to 1 to show that we are ready to start raiding.
Line Code Comment 1 (defconst g-knight-raiding 101) Track the status of our knight raid 2 (defconst g-position-self-x 102) x-coordinate of our starting TC 3 (defconst g-position-self-y 103) y-coordinate of our starting TC 4 (defconst g-closest-enemy 104) Store the player number of our closest enemy 5 (defconst g-local-total 105) Stores the number of units in the local search list (explained below) 6 (defconst g-local-last 106) Stores the number of units from the last local DUC search 7 (defconst g-remote-total 107) Stores the number of units in the remote search list 8 (defconst g-remote-last 108) Stores the number of units from the last remote DUC search 9 (defconst g-building-target-id 109) Stores the map ID of the closest enemy building 10 (defconst t-attack-timer 1) A timer to separate attacks 11 12 (defrule 13 (true) 14 => 15 (set-goal g-knight-raiding 0) 16 (up-get-point position-self g-position-self-x) Store the location of our TC in g-position-self-x and g-position-self-y 17 (disable-self) 18 ) 19 20 (defrule 21 (goal g-knight-raiding 0) 22 (not (town-under-attack)) 23 (players-building-count any-enemy > 0) 24 => 25 (up-find-player enemy find-closest g-closest-enemy) Find the closest enemy and store the player ID 26 (up-modify-sn sn-focus-player-number g:= g-closest-enemy) Set the focus player to be the closest enemy 27 ) 28 29 (defrule 30 (goal g-knight-raiding 0) 31 (unit-type-count knight-line >= 6) 32 (not (town-under-attack)) 33 (players-building-count any-enemy > 0) 34 (players-building-type-count focus-player lumber-camp > 0) Our closest enemy has a lumber camp 35 => 36 (set-goal g-knight-raiding 1) 37 )
Now let's get to the DUC stuff. First, we need to pick the knights that should attack. Second, we need to pick a lumber camp to target. To do this in an AI script, we need to perform two different DUC searches, one for the knights that we own, and one for the lumber camp owned by our enemy. To do anything with the objects found in these searches, we will need to store the results of our search, which brings us to the most fundamental concept in DUC: search lists.
Results from DUC searches are stored in one of two lists: search-local and search-remote.
search-local is used to store the objects that we want to command. As you can guess, search-local can only store the AI's own objects, not the objects of other players. The size of search-local is limited to 240, which is often more than you need. In our code, we will want to search for the knights we want to raid with and store them in search-local.
search-remote is used to store the objects that we want to target. Unlike search-local, search-remote can store objects from any player, even Gaia or the AI itself. For example, if we wanted to make villagers force drop resources at the town center, the town center would be the target of the action, so the town center would be stored in search-remote. Unfortunately, search-remote can only store 40 objects. In our code, we will want to search for the enemy lumber camp for the knights to target in their raid and store it in search-remote.
Line Code Comment 1 (defrule (goal g-knight-raiding 1) (unit-type-count knight-line >= 6) (not (town-under-attack)) (players-building-count any-enemy > 0) (players-building-count focus-player > 0) => (up-full-reset-search) (up-find-local c: knight-line c: 240) Code to search for a maximum of 240 knights )
Here you can see the use of two DUC commands: up-full-reset-search and up-find-local.
This command clears the results of all previous DUC searches and filters. When you're starting a new section of DUC code in your AI it's important to start with an empty search-local and search-remote. Otherwise, if you have leftover units from a previous DUC search still in the search lists, then you could accidentally command or target the wrong objects, resulting in what's called a "DUC leak". up-full-reset-search is not the only way to reset the searches and filters (we'll go through the other options later), but it is the most efficient option in many cases.
up-find-local searches for objects owned by the AI player and adds the found objects (if any) to the search-local list. You have two pieces of information you need provide for up-find-local: the type of objects to search for and the number of objects to search for.
Our code above used the line (up-find-local c: knight-line c: 240). Here, we chose knight-line as the type of object to search for, and we told the AI to try to search for 240 knights. Since we chose to search for knight-line, this code will also search for cavaliers or paladins. If the AI cannot find 240 knights (pretty likely), then it will store as many knights as it can find in search-local. In other words, this code will try to add all of our knights to search-local. If you only wanted to raid with a smaller number of knights, say 6 knights, then you'd change 240 to 6.
The article is under construction past this point. Check it out again later for more knight power. ;)
Line Code Comment 1 (defrule (goal g-knight-raiding 1) (unit-type-count knight-line >= 6) (not (town-under-attack)) (players-building-count any-enemy > 0) (players-building-count focus-player > 0) => (up-full-reset-search) (up-filter-include cmdid-military -1 -1 -1) (up-filter-exclude -1 actionid-explore orderid-relic warship-class) (up-find-local c: all-units-class c: 240) (up-remove-objects search-local object-data-hitpoints < 50) (up-get-search-state g-local-total) )