Transport Tycoon Forums

The place to talk about Transport Tycoon
It is currently Tue Nov 21, 2017 3:25 pm

All times are UTC




Post new topic  Reply to topic  [ 7 posts ] 
Author Message
PostPosted: Thu Aug 24, 2017 1:52 am 
Offline
Tycoon
Tycoon
User avatar

Joined: Sat Jan 22, 2005 7:31 pm
Posts: 6082
Location: Wakefield, West Yorkshire
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.

_________________
Image
Worst Behaved IRC Member of 2008, 2009 & 2010 - Go Me!


Top
   
PostPosted: Sat Aug 26, 2017 12:56 am 
Offline
President
President
User avatar

Joined: Tue Nov 23, 2010 9:25 pm
Posts: 941
Location: ::1
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.


Attachments:
File comment: This is the scenario I'll be using while writing this test AI.
Scenario.scn [109.52 KiB]
Downloaded 14 times


Last edited by Sylf on Sat Aug 26, 2017 3:35 am, edited 1 time in total.
Top
   
PostPosted: Sat Aug 26, 2017 3:34 am 
Offline
President
President
User avatar

Joined: Tue Nov 23, 2010 9:25 pm
Posts: 941
Location: ::1
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
Code:
  local cargoes = AICargoList();
  local oil;
  foreach (cargo, value in cargoes) {
    if( AICargo.GetCargoLabel(cargo) == "OIL_") {
      oil = cargo;
      break;
    }
  }

Next, I find all industries that produce oil
Code:
  local oilrigs = AIIndustryList_CargoProducing(oil);

I take that list, and find all industries in that list that has a dock
Code:
  oilrigs.Valuate(AIIndustry.HasDock);
  oilrigs.KeepValue(1);

Next, I find the last month production, and select the rig that produced the most.
Code:
  oilrigs.Valuate(AIIndustry.GetLastMonthProduction, oil);
  local myRig = oilrigs.Begin();

Let's mark this industry, so we get some visuals.
Code:
  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:
  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:
  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);

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


Top
   
PostPosted: Sat Aug 26, 2017 2:20 pm 
Offline
OpenTTD Developer
OpenTTD Developer
User avatar

Joined: Mon Jun 09, 2003 6:21 pm
Posts: 4532
Location: /home/sweden
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.


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.

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.

Redirect 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://noai.openttd.org/docs/ This one is for AI API
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)


Top
   
PostPosted: Sun Aug 27, 2017 5:15 am 
Offline
President
President
User avatar

Joined: Tue Nov 23, 2010 9:25 pm
Posts: 941
Location: ::1
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.
Code:
  local accepting = AITileList_IndustryAccepting(industry, 4);

Of course, I can only build docks on coastal tiles, so let's see if there are any within this set of tiles.
Code:
  accepting.Valuate(AITile.IsCoastTile);
  accepting.KeepValue(1);

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.
Code:
  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;
    }
  }

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.
Code:
  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);

I want to build a buoy about every 20 tiles, as directed on this wiki page.
Code:
  local numSegments = distance / 20;
  local xSegSize = xDistance / numSegments;
  local ySegSize = yDistance / numSegments;
  AILog.Info("X segment size: " + xSegSize);
  AILog.Info("Y segment size: " + ySegSize);

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!
Code:
  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.


Top
   
PostPosted: Mon Aug 28, 2017 12:15 am 
Offline
President
President
User avatar

Joined: Tue Nov 23, 2010 9:25 pm
Posts: 941
Location: ::1
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.
Code:
  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:
  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:
  local rigStation = AIIndustry.GetDockLocation(rig);
  AIOrder.InsertOrder(ship, 0, rigStation, AIOrder.OF_FULL_LOAD_ANY);

Next, we iterate through the list of buoys, and add them to the orders.
Code:
  local counter = 1;
  foreach (buoy, value in buoys) {
    local location = AIWaypoint.GetLocation(buoy);
    AIOrder.InsertOrder(ship, counter, location, AIOrder.OF_NONE);
    counter++;
  }

And unload, and leave empty...
Code:
  AIOrder.InsertOrder(ship, counter, dock, (AIOrder.OF_NO_LOAD + AIOrder.OF_UNLOAD));
  counter++;

And buoy orders for the return trip...
Code:
  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++;
  }

Finish this section by starting the ship!
Code:
  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:
  while (true) {
    this.ManageFleet();
    Sleep(72);
  }

And to implement the MagageFleet() function...
Code:
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.


Top
   
PostPosted: Mon Nov 06, 2017 2:36 pm 
Offline
Engineer
Engineer

Joined: Sun May 12, 2013 10:28 pm
Posts: 89
Location: Argentina
Thank you very much Sylf :)


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 7 posts ] 

All times are UTC


Who is online

Users browsing this forum: Bing [Bot] and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000-2017 phpBB Limited

Copyright © Owen Rudge/The Transport Tycoon Forums 2001-2017.
Hosted by Zernebok Hosting.