RailPathfinder
NoAI Framework

NoAI Main Page

Development

Development milestones
Suggested API changes

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
Home page
Common mistakes
Lists
Coping with OTTD errors
Trams
AI Libraries
Forum
Forum FAQ
AI Behavior
ShortNames in use

AIs

TestAI
WrightAI
Convoy
SimpleAI
Comparison of AIs
See forum
See BaNaNaS
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.