AI:Need To Know

From OpenTTD
(Difference between revisions)
Jump to: navigation, search
(Cargos)
Line 106: Line 106:
  
  
Of course there is always the situation where you want to make a passengers only AI. Although it is advisable to avoid such limitations on your AI, this of course is possible. NewGRF specification specifies that Passangers cargo should always be marked with a CargoLabel PASS. This doesn't include tourist, and who knows what more in the future. You can of course scan the array for this label, and use that value. Again, to be very explicit: don't believe that the value you get on your computer, works on all other computers. It might differ over time, mostly because of NewGRFs. The code to find the label:
+
Of course it is also possible to find a cargo based on the class of the cargo. Don't just use value '0' for passengers, it might not hold for all NewGRFs. What is safe to do, is find the cargo that belongs to AICargo.CC_PASSENGERS. To give a simple code snippet which illustrates this:
  
 
  local list = AICargoList();
 
  local list = AICargoList();
 
  local pass_cargo = -1;
 
  local pass_cargo = -1;
 
  for (local cargo = list.Begin(); list.HasNext(); cargo = list.Next()) {
 
  for (local cargo = list.Begin(); list.HasNext(); cargo = list.Next()) {
   if (AICargo.GetCargoLabel(cargo) == "PASS") {
+
   if (AICargo.HasCargoClass(cargo, AICargo.CC_PASSENGERS)) {
 
     pass_cargo = cargo;
 
     pass_cargo = cargo;
 +
    break;
 
   }
 
   }
 
  }
 
  }
 
  if (pass_cargo == -1) AILog.Error("Your game doesn't have any passengers cargo, and as we are a passenger only AI, we can't do anything");
 
  if (pass_cargo == -1) AILog.Error("Your game doesn't have any passengers cargo, and as we are a passenger only AI, we can't do anything");
  
And that is all there is to.
+
At the end, either it gave an error, or pass_cargo is the value of the first cargo belonging to the CC_PASSENGERS class.
 +
 
 +
 
 +
===Bus station or Truck station===
 +
 
 +
There is one simple way to find out if the cargo you want to transport should go to a bus station or a truck station. And it is the most obvious: if the cargo belongs to the CC_PASSENGERS class, it needs a bus station. All other cases needs a truck station. So:
 +
 
 +
local truck_station = !AICargo.HasCargoClass(cargo, AICargo.CC_PASSENGERS);
 +
 
 +
It could not be more simple!
  
 
[[Category:Development]]
 
[[Category:Development]]
 
[[Category:NoAI]]
 
[[Category:NoAI]]

Revision as of 09:21, 23 May 2008

Contents

GetTick()

When your Start() is called for the first time, GetTick() always returns 0. You can not do any action in that tick. Tick 0 is meant to initialize things, not to build things. After that, GetTick() isn't really important for you, or rather: shouldn't be important. It is just meant to give an indication of time, as we always find it useful to know when an AI spit out a message (was it ages ago, or was it recent). This said, you of course want to know when it's value changes.

Of course it changes when you do Sleep(), and with the exact value you specified in the Sleep(). But it also changes when you execute a command. This has to do that in networking it takes time to do something, and as you want to know if something really build, we have to wait till we get a signal back from the server. Now to make your life easier, we made singleplayer and multiplayer simular: they both delay. In singleplayer this is always 1 tick, unless you configured it with SetDelay(). In multiplayer it is at least 1 tick, but depends on the server-configuration. If you set SetDelay() lower than the server-configuration, the server-configuration wins, and else SetDelay() does.

So what does this mean:

GetTick() -> 3
SetCompanyName()
GetTick() -> 4 (or higher)

As you might notice, you can't use GetTick() for anything else but debugging. It isn't a good indicator of time, it isn't usable to do something every 10 ticks or something, nothing of that. You will need to make such a system yourself.

How to start only my AI?

The easiest way is to set the difficulty setting to No AIs, and in a new game type in the console:

start_ai <the name of your AI>

The AI of your choice will start immediatly.

To list all the AIs in-game, type in the console:

list_ai

The list of AIs detected by OpenTTD is also available via:

./openttd --help

You can force the use of your AI when letting the game start an AI like it always does (so without you triggering the 'start_ai'). This you can do by running OpenTTD like:

./openttd -a <the name of your AI>

Every AI started after that, will be the AI you specified, unless you used 'start_ai' (after which a Random next-one is picked).

Map coordinates

Map (X, Y) coordinates are done from the top to the bottom - the top most tile is (0,0), the bottom most tile is (Max_X, Max_Y). X is increased by going SW, and Y increased by going SE. Any time a more than one tile dimension object is returned, it'll be the most top coordinate, tending towards the top right:

The AI placed this airport, using the top right most coordinate, which is also it's returned location. The airport size is X=4, Y=3

When for example using AITile.IsBuildableRectangle with Lists, the list will contain those tiles where you can build an object of the size specified, where the returned tile is the one with the lowest X and Y value of the rectangle (or rather: the tile most North).

Loading and restarting AI's

All AIs are reloaded when starting a new game. There is no need to restart the game; abandoning the current level and starting again is usually the easiest method to restart the AI from a scratch map (try 'newgame' from console), or loading a scenario setup to be the same each time. The AI Debug Window also allows restarting individual AIs.

To load a specific scenario immediately, use:

./openttd -g relative/path/to/scenario.scn

Logging and Debug Panel

AI Debug panel is accessible from the Land Area Information (red question mark) labelled "AI Debug Console". All outputs of AILog.XXX() go to here, and can be selected per AI. The AI can also be restarted here. When restarting, the company of that AI is removed (with all his property), and a new AI controlled by the same script (which is reloaded from disk) is started. So when changing the AI, it is enough to just hit the Reload button to get your new AI to work.

AI Debug Panel

Developer Console Usage

The console is accessed using the ` key (next to "1") on the keyboard, and appears in the top part of the screen. This can be scrolled by using the Shift + Page Up/Down.

To get a separate window (Windows Only) use the -d parameter by itself (use -d ai=5 to get only AI information) on a shortcut:

./openttd -d

This provides this output (Windows Only):

Zuu's example of the developer console -d version outputting AI errors.

For linux, using -d ai=5 will send all information to the stdout.

Logging

AILog.XXX() commands can be output to the developer console directly by:

./openttd -d ai=5
openttd.exe -d ai=5

Or by inputting debug_level ai=5 in the console.

Cargos

CargoID, like any ID, is an index (for people who didn't figure that out yet). An index on its own means nothing, it is just a pointer to a value in an array, and in this case, a cargo-array. The main problem with Cargo, is that you can't trust a value to mean something in all cases. NewGRFs are very powerful, and allows changing of cargos in all kinds of way. Normally, CargoID 0 represents Passengers, but with a single NewGRF, this can be changed to, say, Coal. Now here is the tricky part of it all: no longer you can transport this cargo from and to towns. So, how to make sure your AI is compatible enough that it works in all cases?

Well, say you want to transport stuff from one town to the other town. Than you most likely don't care if it is passengers or mail, just the one which is giving you the most profit, right? Well, that is mail in this case, but how to find out?

First, make a cargo list:

local list = AICargoList();

Now filter what we want.. we want non-freight (short: mail, passengers, ... everything that doesn't go or goes to industry (find a dictionary to lookup the meaning in your language!)).

list.Valuate(AICargo.IsFreight);
list.KeepValue(0);

Next, we want the one that is most profitable for a given length (10 tiles in 2 days is what we use here, but use what ever value you want):

list.Valuate(AICargo.GetCargoIncome, 10, 2);

Now the best cargo to transport is the first entry of the list:

local cargo = list.Begin();

And it is that simple! And you will see, when using this method, you can get other types of cargo when loading certain NewGRFs. Nevertheless, it is always a cargo you can transport from and to towns, and it is always the one that is most profitable. So you shouldn't care if it is passengers or not, although some people claim you should. Your AI shouldn't matter what it transports, just as long as it makes profit!


Of course it is also possible to find a cargo based on the class of the cargo. Don't just use value '0' for passengers, it might not hold for all NewGRFs. What is safe to do, is find the cargo that belongs to AICargo.CC_PASSENGERS. To give a simple code snippet which illustrates this:

local list = AICargoList();
local pass_cargo = -1;
for (local cargo = list.Begin(); list.HasNext(); cargo = list.Next()) {
  if (AICargo.HasCargoClass(cargo, AICargo.CC_PASSENGERS)) {
    pass_cargo = cargo;
    break;
  }
}
if (pass_cargo == -1) AILog.Error("Your game doesn't have any passengers cargo, and as we are a passenger only AI, we can't do anything");

At the end, either it gave an error, or pass_cargo is the value of the first cargo belonging to the CC_PASSENGERS class.


Bus station or Truck station

There is one simple way to find out if the cargo you want to transport should go to a bus station or a truck station. And it is the most obvious: if the cargo belongs to the CC_PASSENGERS class, it needs a bus station. All other cases needs a truck station. So:

local truck_station = !AICargo.HasCargoClass(cargo, AICargo.CC_PASSENGERS);

It could not be more simple!

Personal tools