Development
AI Programming
- API Documentation
- Introduction
- info.nut file
- main.nut file
- Basics
- Using the road pathfinder
- Using the rail pathfinder
- Saving / Loading data
- Things you need to know
- Squirrel
- Lists
- Coping with OTTD errors
- Trams
- AI Libraries
- Forum
- Forum FAQ
- AI Behavior
- ShortNames in use
AIs
All articles in NoAI category
Contents |
Prerequisite conditions
Before you call the pathfinder, you must choose which rail type to use. This is a safe way Zutty suggests to pick the first rail type on the list.
local types = AIRailTypeList(); AIRail.SetCurrentRailType(types.Begin());
Initialization
As with any library, before you can use it, you need to import it. You can do this by placing the following line at the top of your code.
import("pathfinder.rail", "RailPathFinder", 1);
This imports the rail pathfinder version 1 and names it RailPathFinder
. You can now instance it like any other class:
local pathfinder = RailPathFinder();
Before you start finding a route, you may want to set some parameters first. The available parameters are:
Name | Default value | Meaning |
---|---|---|
cost.max_cost | 2000000000 | The maximum cost for a route. |
cost.tile | 100 | The cost for a non-diagonal track. |
cost.diagonal_tile | 70 | The cost for a diagonal track. |
cost.turn | 50 | The cost that is added to _cost_tile / _cost_diagonal_tile if the direction changes. |
cost.slope | 100 | The extra cost if a rail tile is sloped. |
cost.bridge_per_tile | 150 | The cost per tile of a new bridge, this is added to _cost_tile. |
cost.tunnel_per_tile | 120 | The cost per tile of a new tunnel, this is added to _cost_tile. |
cost.coast | 20 | The extra cost for a coast tile. |
cost.max_bridge_length | 6 | The maximum length of a bridge that will be build. |
cost.max_tunnel_length | 6 | The maximum length of a tunnel that will be build. |
To set a parameter, you just use:
pathfinder.cost.tile = 100;
Planning a route
SO, now we know how to create an instance of the pathfinder and to set the cost functions, it's time to put that knowledge to some use. Let's plan our first rail track. First we need one (or more) source tile(s) and one (or more) goal tile(s). For now let's assume we already have those, as it's outside the scope of this page to explain how to get those. So say for example we want to build a rail between two stations st_a and st_b. For this example we will assume that the direction of the stations is NE_SW and we want to connect the NE side of the station. First we call InitializePath
on our pathfinder object:
local tile_a = AIStation.GetLocation(st_a); local tile_b = AIStation.GetLocation(st_b); pathfinder.InitializePath([[tile_a, tile_a + AIMap.GetTileIndex(-1, 0)]], [[tile_b + AIMap.GetTileIndex(-1, 0), tile_b]]);
The pathfinder expects an array of [start_tile, tile_before_start]-arrays and an array of [last_tile, tile_after_end]-arrays. since we only have one source and one goal tile, we'll just create two arrays with one element each. Now the pathfinder is ready to find a path.
local path = pathfinder.FindPath(-1);
The parameter FindPath
accepts is the number of iterations it should do before returning. -1 means looping until a path is found. When it's done, it returns either the path it found or null to indicate no route exists.
Building the planned route
Suppose the pathfinder returned a path, how do start building it? For a start, it's nice to know that FindPath
returns an instance of the AyStar.Path
class. To view the documentation, open bin/ai/library/graph/aystar/main.nut
. Basically, it has three functions: GetParent()
, GetCost()
and GetTile()
. GetCost()
isn't useful for now, so we'll just use GetParent()
and GetTile()
. To build the route found by the pathfinder, we call path.GetParent()
until it returns null. As the pathfinder can also return bridges / tunnels, we need to check the distance between the current node and the previous node. If that distance is more than 1, we should build a bridge / tunnel.
local prev = null; local prevprev = null; while (path != null) { if (prevprev != null) { if (AIMap.DistanceManhattan(prev, path.GetTile()) > 1) { if (AITunnel.GetOtherTunnelEnd(prev) == path.GetTile()) { AITunnel.BuildTunnel(AIVehicle.VT_RAIL, prev); } else { local bridge_list = AIBridgeList_Length(AIMap.DistanceManhattan(path.GetTile(), prev) + 1); bridge_list.Valuate(AIBridge.GetMaxSpeed); bridge_list.Sort(AIList.SORT_BY_VALUE, false); AIBridge.BuildBridge(AIVehicle.VT_RAIL, bridge_list.Begin(), prev, path.GetTile()); } prevprev = prev; prev = path.GetTile(); path = path.GetParent(); } else { AIRail.BuildRail(prevprev, prev, path.GetTile()); } } if (path != null) { prevprev = prev; prev = path.GetTile(); path = path.GetParent(); } }
This code loops over all the whole route. For every element it tries to build either a bridge / tunnel or a piece of rail. Note that in a real AI you'll probably want to check if AIRail.BuildRail
returns true, and if not restart pathfinding.