Creating AI documentation + help
Moderator: OpenTTD Developers
- Redirect Left
- Tycoon
- Posts: 7249
- Joined: 22 Jan 2005 19:31
- Location: Wakefield, West Yorkshire
Creating AI documentation + help
I'm trying to build a simple AI. However i'm struggling, as the wiki page basically leaves you with a non functioning (loads but does nothing) info.nut and a main.nut. You are given the code for that, then told "here is the API, get reading".
It never tries to slowly build you up to an AI from scratch, suggesting to copy others (with sutiable licences) - even though that'll be totally overwhelming for the majority of people looking at .nut code for the first time in such a huge amount.
the API page (this) and the page linking to it on the wiki make no note of what these versions correspond to, whether one is better for gamescripts or not, whether one is more geared towards AI, or whether I should simply use the highest number and thus assumably most recent.
So i'm basically clueless, as this seems very different to the only other languages i use frequently, PHP & Pawn.
I've tried looking at some other AIs, but they're way too complex to really start with and hard to learn from, too much in one go.
I was thinking of starting with a simple oil service, boat -> refinery, then trying to do train services with the goods produced to towns, but so far I can't even get a basic bit of code together, as it seems when the AI pages were wrote, they gave up halfway and left you with the API that isn't very layman friendly instead of taking you through it, I'm trying to add to the seemingly lack of AI developers, but the tools to do so are not noob friendly.
I currently just have the basic info/main.nut files the tutorial does, and totally lost really already.
It never tries to slowly build you up to an AI from scratch, suggesting to copy others (with sutiable licences) - even though that'll be totally overwhelming for the majority of people looking at .nut code for the first time in such a huge amount.
the API page (this) and the page linking to it on the wiki make no note of what these versions correspond to, whether one is better for gamescripts or not, whether one is more geared towards AI, or whether I should simply use the highest number and thus assumably most recent.
So i'm basically clueless, as this seems very different to the only other languages i use frequently, PHP & Pawn.
I've tried looking at some other AIs, but they're way too complex to really start with and hard to learn from, too much in one go.
I was thinking of starting with a simple oil service, boat -> refinery, then trying to do train services with the goods produced to towns, but so far I can't even get a basic bit of code together, as it seems when the AI pages were wrote, they gave up halfway and left you with the API that isn't very layman friendly instead of taking you through it, I'm trying to add to the seemingly lack of AI developers, but the tools to do so are not noob friendly.
I currently just have the basic info/main.nut files the tutorial does, and totally lost really already.
Re: Creating AI documentation + help
I will take a crack at this. I make no guarantee at this point that I will produce a working AI at this point - I've written a very simple game script (so I've touched a bit of squirrel script), but never written an AI before.
I'm going to see if I can connect at least 1 oil rig to a refinery using ships.
Here is my assumption.
* The map is in temperate climate
* The map already has oil rigs (fresh temperate map has no oil rigs, so I've prepared a random temperate scenario for testing purpose)
* All refineries on the maps are close enough to the water, so they can be serviced by ships.
Here is my plan.
1. Find all oil rigs on the map
2. Find the oil rig that is producing most oil
3. Find the oil refinery that is closest to the oil rig from step 2
4. If the distance between the rig and refinery is less than 30, that's too close. Find the next closest refinery
5. Place a port next to the refinery
6. Place buoys between the refinery and the rig
7. Place a ship yard close to the route
8. Purchase ships, and make them work
9. If I still have enough motivation left to keep going, then work on connecting the rest of oil rigs to the refinery from the steps above.
I don't have any code written at this point.
I will make posts little by little, as I make some progress, over next several days.
If all goes well, someone can summarize my adventure, and submit the tutorial to the wiki.
I'm going to see if I can connect at least 1 oil rig to a refinery using ships.
Here is my assumption.
* The map is in temperate climate
* The map already has oil rigs (fresh temperate map has no oil rigs, so I've prepared a random temperate scenario for testing purpose)
* All refineries on the maps are close enough to the water, so they can be serviced by ships.
Here is my plan.
1. Find all oil rigs on the map
2. Find the oil rig that is producing most oil
3. Find the oil refinery that is closest to the oil rig from step 2
4. If the distance between the rig and refinery is less than 30, that's too close. Find the next closest refinery
5. Place a port next to the refinery
6. Place buoys between the refinery and the rig
7. Place a ship yard close to the route
8. Purchase ships, and make them work
9. If I still have enough motivation left to keep going, then work on connecting the rest of oil rigs to the refinery from the steps above.
I don't have any code written at this point.
I will make posts little by little, as I make some progress, over next several days.
If all goes well, someone can summarize my adventure, and submit the tutorial to the wiki.
- Attachments
-
- Scenario.scn
- This is the scenario I'll be using while writing this test AI.
- (109.52 KiB) Downloaded 106 times
Last edited by Sylf on 26 Aug 2017 03:35, edited 1 time in total.
Re: Creating AI documentation + help
First thing first. Since my initial goal is to work with oil rigs, I want to get the list of all oil rigs on the map.
Oil rigs has 2 characteristics I can use within my script:
1. They produce oil
2. They have docks
So, I start out by finding the oil cargo within the game
Next, I find all industries that produce oil
I take that list, and find all industries in that list that has a dock
Next, I find the last month production, and select the rig that produced the most.
Let's mark this industry, so we get some visuals.
==============================================================
Now we move on to finding which refinery we'll want to use. The step-by-step concept is very similar to above.
==============================================================
Now, before I wrap up for the day, I want to see if I can build anything. For now, I'm going to hard code a tile location, and build a dock there.
There. We've identified a producing industry we want to use, and a processing industry we want to use. And we built a dock.
Tomorrow, I'll find a way to let the script determine the nearby coast tile where we can build a dock.
Right now, the main.nut looks like this: https://paste.openttdcoop.org/pfif4lzeo
Oil rigs has 2 characteristics I can use within my script:
1. They produce oil
2. They have docks
So, I start out by finding the oil cargo within the game
Code: Select all
local cargoes = AICargoList();
local oil;
foreach (cargo, value in cargoes) {
if( AICargo.GetCargoLabel(cargo) == "OIL_") {
oil = cargo;
break;
}
}
Code: Select all
local oilrigs = AIIndustryList_CargoProducing(oil);
Code: Select all
oilrigs.Valuate(AIIndustry.HasDock);
oilrigs.KeepValue(1);
Code: Select all
oilrigs.Valuate(AIIndustry.GetLastMonthProduction, oil);
local myRig = oilrigs.Begin();
Code: Select all
AISign.BuildSign(AIIndustry.GetLocation(myRig), "origin");
Now we move on to finding which refinery we'll want to use. The step-by-step concept is very similar to above.
Code: Select all
local refineries = AIIndustryList_CargoAccepting(oil);
refineries.Valuate(AIIndustry.GetDistanceManhattanToTile, AIIndustry.GetLocation(myRig));
refineries.Sort(AIList.SORT_BY_VALUE, true);
local myRef = refineries.Begin();
AISign.BuildSign(AIIndustry.GetLocation(myRef), "destination");
Now, before I wrap up for the day, I want to see if I can build anything. For now, I'm going to hard code a tile location, and build a dock there.
Code: Select all
AILog.Info(AITile.GetSlope(0x418E)); // 0x418E comes from Land Area Information tool within the game
AILog.Info(AITile.IsCoastTile(0x418E));
AIMarine.BuildDock(0x418E, AIStation.STATION_NEW);
Tomorrow, I'll find a way to let the script determine the nearby coast tile where we can build a dock.
Right now, the main.nut looks like this: https://paste.openttdcoop.org/pfif4lzeo
Re: Creating AI documentation + help
You are right that the docs is mainly written on a level that they make sense to people who have learned to program already using some other language/platform, and then come to OpenTTD AI/GS and Squirrel 2.0. It doesn't mean it is impossible to learn to program while also writing an AI, but the debugging facilities is not on the same level as more mainstream languages. Because it takes time and resources to provide that and our community is much smaller than PHP for example. What we do have to offer however is the possibility to write code that control AI/GS in OpenTTD that may keep you motivated. And motivation is very important in order to succeed in programming where problems come in all different shapes and there is not always a guide that fit your case.Redirect Left wrote:I'm trying to build a simple AI. However i'm struggling, as the wiki page basically leaves you with a non functioning (loads but does nothing) info.nut and a main.nut. You are given the code for that, then told "here is the API, get reading".
It never tries to slowly build you up to an AI from scratch, suggesting to copy others (with sutiable licences) - even though that'll be totally overwhelming for the majority of people looking at .nut code for the first time in such a huge amount.
I hope I don't sound to negative, but my point is that you are right that we don't have a full guide on the level you may want, but either you have to write it yourself (its a wiki) or live with that there is no such guide and use the forum and IRC to get help.
I do recommend though to activate ai_developer mode as it will provide you with some extra debugging facilities that I and others have added to the game for some interactive debugging. Also, there are some useful wiki articles with pitfalls and things we've learned over the years of using squirrel.
https://noai.openttd.org/docs/ This one is for AI APIRedirect Left wrote:the API page (this) and the page linking to it on the wiki make no note of what these versions correspond to, whether one is better for gamescripts or not, whether one is more geared towards AI, or whether I should simply use the highest number and thus assumably most recent.
https://nogo.openttd.org/docs/ This one is for GS API
The version numbers is the corresponding OpenTTD Version that the API docs correspond to. In general you can assume that people have the last stable and code for that. Or if you want the latest features, use trunk. This will eventually become the next stable. What you pick depends of course what trunk has to offer for new things but also if you write it only for yourself or if you think it is important that you can share it with a large player base.
My OpenTTD contributions (AIs, Game Scripts, patches, OpenTTD Auto Updater, and some sprites)
Junctioneer (a traffic intersection simulator)
Junctioneer (a traffic intersection simulator)
Re: Creating AI documentation + help
OK. Today, I figured out how to build a station the proper way.
Also, I built buoys between the oil rig and the refinery.
Yesterday, I built a dock using a hard-coded tile location.
After reading through some of the API pages, I found this nifty class.
Of course, I can only build docks on coastal tiles, so let's see if there are any within this set of tiles.
Now the "accepting" is a list of coastal tiles that accept oil. I'm going to now iterate through these tiles, attempting to build a dock on that tile. I keep repeating until I succeed.
Save, and run the AI. It will build the dock right next to the refinery! Hooray!
==============================================================
Now that we have a real dock, it's time to start building buoys.
First, I'm going to find the distance in terms of X-Y coordinates.
I want to build a buoy about every 20 tiles, as directed on this wiki page.
Now that we know how many buoys we need to build, and how far apart they should be in X and Y directions, we build the buoys!
==============================================================
This code is now getting somewhat lengthy. I'm going to break some of the code to separate functions.
After the refactoring, the code now looks like this.
It works great with the given scenario I'm using! If there are other industries or land on the tile the script wanted to build a buoy, it would fail, and just move on. In a real AI, you would want to expand BuildBuoy() function, so it will keep attempting to build a buoy until it succeeds. And that also means that you will not want to assume that numSegments = distance / 20; logic isn't the best one to use, if you need to go around a massive peninsula to reach the destination. But I won't address that issue in this tutorial to keep the code absolutely simple.
Tomorrow, I'm going to work on buying some ships, and give them orders.
Also, I built buoys between the oil rig and the refinery.
Yesterday, I built a dock using a hard-coded tile location.
After reading through some of the API pages, I found this nifty class.
Code: Select all
local accepting = AITileList_IndustryAccepting(industry, 4);
Code: Select all
accepting.Valuate(AITile.IsCoastTile);
accepting.KeepValue(1);
Code: Select all
foreach(myTile, value in accepting) {
local slope = AITile.GetSlope(myTile);
// Keep trying to build a dock until it succeeds
if (AIMarine.BuildDock(myTile, AIStation.STATION_NEW)) {
dockLocation = myTile;
break;
}
}
==============================================================
Now that we have a real dock, it's time to start building buoys.
First, I'm going to find the distance in terms of X-Y coordinates.
Code: Select all
local rigStation = AIIndustry.GetDockLocation(myRig);
local distance = AIMap.DistanceMax(dockLocation, rigStation);
AILog.Info("Two stations are " + distance + " tiles apart.");
local xDistance = AIMap.GetTileX(rigStation) - AIMap.GetTileX(dockLocation);
local yDistance = AIMap.GetTileY(rigStation) - AIMap.GetTileY(dockLocation);
AILog.Info("X distance: " + xDistance);
AILog.Info("Y distance: " + yDistance);
Code: Select all
local numSegments = distance / 20;
local xSegSize = xDistance / numSegments;
local ySegSize = yDistance / numSegments;
AILog.Info("X segment size: " + xSegSize);
AILog.Info("Y segment size: " + ySegSize);
Code: Select all
local buoys = AIList();
local xLoc = AIMap.GetTileX(dockLocation);
local yLoc = AIMap.GetTileY(dockLocation);
for (local i = 0; i < numSegments; i++) {
xLoc = xLoc + xSegSize;
yLoc = yLoc + ySegSize;
local tile = AIMap.GetTileIndex(xLoc, yLoc);
if (AIMarine.BuildBuoy(tile)) {
buoys.AddItem(AIWaypoint.GetWaypointID(tile);, 0);
}
}
This code is now getting somewhat lengthy. I'm going to break some of the code to separate functions.
After the refactoring, the code now looks like this.
It works great with the given scenario I'm using! If there are other industries or land on the tile the script wanted to build a buoy, it would fail, and just move on. In a real AI, you would want to expand BuildBuoy() function, so it will keep attempting to build a buoy until it succeeds. And that also means that you will not want to assume that numSegments = distance / 20; logic isn't the best one to use, if you need to go around a massive peninsula to reach the destination. But I won't address that issue in this tutorial to keep the code absolutely simple.
Tomorrow, I'm going to work on buying some ships, and give them orders.
Re: Creating AI documentation + help
Now that we have the docks and buoys built, it's time to build ships.
Of course, we must build a ship yard (depot) first.
We will build one close to the target oil rig. Here, we're going to use a similar logic we used for building the dock.
==============================================================
Now we have a dock. We will now build our first ship.
==============================================================
We now need to give it orders. I'm going to add some more logic within the BuildShip() function to make this happen - this is why I passed in the dock (next to refinery) location, and list of all buoys to this function.
First, we will add an order to go to the rig, and full load any cargo.
Next, we iterate through the list of buoys, and add them to the orders.
And unload, and leave empty...
And buoy orders for the return trip...
Finish this section by starting the ship!
==============================================================
Now, let's write a logic that will manage my fleet. I want to monitor the target oil rig, and make sure that it has enough ships to service it well. This is the while(true){} loop finally comes into play.
And to implement the MagageFleet() function...
==============================================================
At this point, my whole code looks like this.
You can run this AI, and watch the fleet grow over time.
Of course, this AI will not expand its network of transport after the start - this is what you can implement within the main while(true) loop as well.
Nor is the ManageFleet() function robust enough to deal with excess ships.
Nor is the script smart enough to deal with ships that cannoct reach the destination.
But there should be enough in this simple tutorial that gives you some ideas on how to get started with your own AI.
Of course, we must build a ship yard (depot) first.
We will build one close to the target oil rig. Here, we're going to use a similar logic we used for building the dock.
Code: Select all
local depot = this.BuildDepot(AIIndustry.GetLocation(myRig));
.
.
function MyTest::BuildDepot(tile) {
local xLoc = AIMap.GetTileX(tile);
local yLoc = AIMap.GetTileY(tile);
local myDock = false;
xLoc = xLoc - 5;
yLoc = yLoc - 5;
if (xLoc < 0) xLoc = 0;
if (yLoc < 0) yLoc = 0;
for (local x = 0; x < 12; x++) {
for (local y = 0; y < 12; y++) {
local tile1 = AIMap.GetTileIndex(xLoc+x, yLoc+y);
local tile2 = AIMap.GetTileIndex(xLoc+x+1, yLoc+y);
if (AIMarine.BuildWaterDepot(tile1, tile2)) {
myDock = tile1;
break;
}
}
if (myDock) {
break;
}
}
return myDock;
}
Now we have a dock. We will now build our first ship.
Code: Select all
local ship = this.BuildShip(depot, myDock, myRig, buoys);
.
.
function MyTest::BuildShip(depot, dock, rig, buoys) {
local engines = AIEngineList(AIVehicle.VT_WATER);
engines.Valuate(AIEngine.CanRefitCargo, oil);
engines.KeepValue(1);
engines.Valuate(AIEngine.GetCapacity);
local engine = engines.Begin();
local ship = AIVehicle.BuildVehicle(depot, engine);
AIVehicle.RefitVehicle(ship, oil);
return ship;
}
We now need to give it orders. I'm going to add some more logic within the BuildShip() function to make this happen - this is why I passed in the dock (next to refinery) location, and list of all buoys to this function.
First, we will add an order to go to the rig, and full load any cargo.
Code: Select all
local rigStation = AIIndustry.GetDockLocation(rig);
AIOrder.InsertOrder(ship, 0, rigStation, AIOrder.OF_FULL_LOAD_ANY);
Code: Select all
local counter = 1;
foreach (buoy, value in buoys) {
local location = AIWaypoint.GetLocation(buoy);
AIOrder.InsertOrder(ship, counter, location, AIOrder.OF_NONE);
counter++;
}
Code: Select all
AIOrder.InsertOrder(ship, counter, dock, (AIOrder.OF_NO_LOAD + AIOrder.OF_UNLOAD));
counter++;
Code: Select all
buoys.Sort(AIList.SORT_BY_ITEM, true);
foreach (buoy, value in buoys) {
local location = AIWaypoint.GetLocation(buoy);
AIOrder.InsertOrder(ship, counter, location, AIOrder.OF_NONE);
counter++;
}
Code: Select all
AIVehicle.StartStopVehicle(ship);
Now, let's write a logic that will manage my fleet. I want to monitor the target oil rig, and make sure that it has enough ships to service it well. This is the while(true){} loop finally comes into play.
Code: Select all
while (true) {
this.ManageFleet();
Sleep(72);
}
Code: Select all
function MyTest::ManageFleet() {
foreach (rig, value in myRigs) {
local dockLocation = AIIndustry.GetDockLocation(rig);
local dock = AIStation.GetStationID(dockLocation);
if (AIStation.HasCargoRating(dock, oil)) {
// If a station rating falls below 57%, clone a ship
if (AIStation.GetCargoRating(dock, oil) < 57) {
this.CloneFleet(dock);
}
}
}
}
function MyTest::CloneFleet(station) {
local vehicles = AIVehicleList_Station(station);
local vehicle = vehicles.Begin();
local stationLocation = AIStation.GetLocation(station);
local depots = AIDepotList(AITile.TRANSPORT_WATER);
depots.Valuate(AITile.GetDistanceSquareToTile, stationLocation);
depots.Sort(AIList.SORT_BY_VALUE, true);
local depot = depots.Begin();
local newShip = AIVehicle.CloneVehicle(depot, vehicle, true);
AIVehicle.StartStopVehicle(newShip);
Sleep(500);
}
At this point, my whole code looks like this.
You can run this AI, and watch the fleet grow over time.
Of course, this AI will not expand its network of transport after the start - this is what you can implement within the main while(true) loop as well.
Nor is the ManageFleet() function robust enough to deal with excess ships.
Nor is the script smart enough to deal with ships that cannoct reach the destination.
But there should be enough in this simple tutorial that gives you some ideas on how to get started with your own AI.
Re: Creating AI documentation + help
Thank you very much Sylf
Who is online
Users browsing this forum: No registered users and 16 guests