names = ["Pathways", "Pather", "Cheap Ways"]; suffixes = ["Inc.", "Co.", "S.A.", "Corps"] cityCargoClasses = [ AICargo.CargoClass.CC_PASSENGERS, AICargo.CargoClass.CC_MAIL ]; function pick(arr) { return arr[1.0 * arr.len() * rand() / RAND_MAX]; } function checkMoney(money, func) { if ( AICompany.GetBankBalance(AICompany.CompanyID.COMPANY_SELF) > money ) func(AICompany.GetBankBalance(AICompany.CompanyID.COMPANY_SELF)); } function ClosestDepotToRoad(begin) { neighbors = AITileList(); neighbors.Add(begin, 0); costs = { [begin] = 0 }; cameFrom = { [begin] = null; }; function CostFor(current, tile) { return GetSlope(tile) != AITile.Slope.SLOPE_FLAT ? 2 : 1; } function CostForBridge(tile) { return AIBridge.GetMinLength(AIBridge.GetBridgeID(tile)) / AIBridge.GetMaxSpeed(AIBridge.GetBridgeID(tile)); } function CostForTunnel(tile) { return AITile.GetManhattanDistance(tile, AITunnel.GetOtherTunneEnd(tile)) / AIEngine.GetMaxSpeed( AIEngineList(AIVehicle.VehicleType.VT_RAIL) .Valuate(AIEngine.GetPrice) .End() ); } function CalcCameFrom(tile, current) { if ( !cameFrom.HasItem(tile) || neighbors.GetValue(tile) < CostFor(current, tile) ) cameFrom[tile] <- current; } function AddNeighbor(tile, current) { if ( AIRail.IsRailTile(tile) && AIRail.AreTilesConnected(tile, current) ) { CalcCameFrom(tile, current); neighbors.SetValue(tile, CostFor(current, tile)); } } function AddBridge(tile) { if ( !AIRail.IsRailTile(tile) ) return; if ( AIBridge.IsBridgeTile(tile) ) { CalcCameFrom(AIBridge.GetOtherBridgeEnd(tile), tile); neighbors.SetValue(tile, CostForBridge(tile)); } else if ( AITunnel.IsTunnelTile(tile) ) { CalcCameFrom(AITunnel.GetOtherTunneEnd(tile), tile); neighbors.SetValue(tile, CostForTunnel(tile)); } } while ( neighbors.Count() > 0 ) { t = neighbors.Begin(); neighbors.RemoveTop(1); // orthogonals AddNeighbor(tile - AIMap.GetTileIndex(1,0)); AddNeighbor(tile - AIMap.GetTileIndex(0,1)); AddNeighbor(tile - AIMap.GetTileIndex(-1,0)); AddNeighbor(tile - AIMap.GetTileIndex(0,-1)); // diagonals AddNeighbor(tile - AIMap.GetTileIndex(-1,1)); AddNeighbor(tile - AIMap.GetTileIndex(1,1)); AddNeighbor(tile - AIMap.GetTileIndex(1,-1)); AddNeighbor(tile - AIMap.GetTileIndex(-1,-1)); AddBridge(t); } return function(depot) { if ( cameFrom.HasItem(depot) ) return costs[depot]; return 0x7FFF; }; } function TilesInCity(city) { tiles = AITileList(); neighbors = AITileList(); function AddNeighbor(tile) { if ( AITown.IsWithinTownInfuence(city, tile) ) { neighbors.AddTile(tile); tiles.AddTile(tile); } } while ( neighbors.Count() > 0 ) { t = neighbors.Begin(); neighbors.RemoveTop(1); // orthogonals AddNeighbor(tile - AIMap.GetTileIndex(1,0)); AddNeighbor(tile - AIMap.GetTileIndex(0,1)); AddNeighbor(tile - AIMap.GetTileIndex(-1,0)); AddNeighbor(tile - AIMap.GetTileIndex(0,-1)); } return tiles; } function CityOutletRoads(city) { outlets = AITileList(); function IsOutlet(tile) { if ( !AIRoad.IsRoadTile(tile) ) return false; neighbors = OrthoNeighbors(tile); neighboringRoads = 0; foreach ( tile in neighbors ) { if ( !AITile.IsBuildable(tile) ) return false; if ( AIRoad.IsRoadTile(tile) ) neighboringRoads++; } return neighboringRoads == 1; } foreach ( tile, val in TilesInCity(city) ) { if ( IsOutlet(tile) ) outlets.AddTile(tile); } return outlets; } function OrthoNeighbors(point) { return [ AddNeighbor(tile - AIMap.GetTileIndex(1,0)), AddNeighbor(tile - AIMap.GetTileIndex(0,1)), AddNeighbor(tile - AIMap.GetTileIndex(-1,0)), AddNeighbor(tile - AIMap.GetTileIndex(0,-1)) ]; } class RoadPath { path = null; constructor(p) { path = p; } function BuildPath() { for ( local i = 0; i < path.len() - 1; i++ ) if ( !AIRoad.BuildRoad(path[i], path[i] + 1) ) return false; return true; } function BuildStations() { local done = false; local pos = 0; while ( AIRoad.BuildRoadStation( path[pos], path[pos] + 1, AIRoad.RoadVehicleType.ROADVEHTYPE_BUS, AIStation.STATION_JOIN_ADJACENT ) ) { pos++; done = true; } if ( !done ) return false; done = false; pos = path.len() - 1; while ( AIRoad.BuildRoadStation( path[pos], path[pos] - 1, AIRoad.RoadVehicleType.ROADVEHTYPE_BUS, AIStation.STATION_JOIN_ADJACENT ) ) { pos--; done = true; } return done; } function RandomPoint() { return pick(path); } function RandomNeighboringPoint() { candidates = []; while ( candidates.len() < path.len() ) { p = RandomPoint(); if ( !(p in candidates) ) candidates.push(p); } foreach ( p in candidates ) foreach ( neighbor in OrthoNeighbors(p) ) if ( !AIRoad.IsRoadTile(neighbor) && AITile.IsBuildable(neighbor) ) return { pos = neighbor, from = p }; return null; } function Begin() { return path[0]; } function End() { return path[path.len() - 1]; } } function CityPathes(city1, city2) { paths = [] pathCosts = AIList(); destinations = CityOutletRoads(city2); function findOutletPath(outlet) { neighbors = AITileList(); neighbors.Add(outlet, 0); costs = { [outlet] = 0 }; cameFrom = { [outlet] = null; }; function CostFor(current, tile) { return GetSlope(tile) != AITile.Slope.SLOPE_FLAT ? 2 : 1; } function CostForBridge(tile) { return AIBridge.GetMinLength(AIBridge.GetBridgeID(tile)) / AIBridge.GetMaxSpeed(AIBridge.GetBridgeID(tile)); } function CostForTunnel(tile) { return AITile.GetManhattanDistance(tile, AITunnel.GetOtherTunneEnd(tile)) / AIEngine.GetMaxSpeed( AIEngineList(AIVehicle.VehicleType.VT_ROAD) .Valuate(AIEngine.GetPrice) .End() ); } function CalcCameFrom(tile, current) { if ( !cameFrom.HasItem(tile) || neighbors.GetValue(tile) < CostFor(current, tile) ) cameFrom[tile] <- current; } function AddNeighbor(tile, current) { if ( AIRoad.IsRoadTile(tile) && AIRoad.AreRoadTilesConnected(tile, current) ) { CalcCameFrom(tile, current); neighbors.SetValue(tile, CostFor(current, tile)); } } function AddBridge(tile) { if ( !AIRoad.IsRoadTile(tile) ) return; if ( AIBridge.IsBridgeTile(tile) ) { CalcCameFrom(AIBridge.GetOtherBridgeEnd(tile), tile); neighbors.SetValue(tile, CostForBridge(tile)); } else if ( AITunnel.IsTunnelTile(tile) ) { CalcCameFrom(AITunnel.GetOtherTunneEnd(tile), tile); neighbors.SetValue(tile, CostForTunnel(tile)); } } while ( neighbors.Count() > 0 ) { t = neighbors.Begin(); neighbors.RemoveTop(1); // orthogonals AddNeighbor(tile - AIMap.GetTileIndex(1,0)); AddNeighbor(tile - AIMap.GetTileIndex(0,1)); AddNeighbor(tile - AIMap.GetTileIndex(-1,0)); AddNeighbor(tile - AIMap.GetTileIndex(0,-1)); AddBridge(tile); } pathes = []; pcosts = AIList(); foreach ( dest in destinations ) { path = []; c = dest; while ( c != null && c in cameFrom ) { path.push(c); c = cameFrom[c]; if ( c == outlet ) break; } pcosts.SetValue(pathes.len(), costs[dest]); path.reverse(); pathes.push(path); } return { "pathes" = pathes, "costs" = pcosts }; } foreach ( outlet, _ in CityOutletRoads(city1) ) { res = findOutletPath(outlet); paths.extend(RoadPath(res.pathes)); pathCosts.AddList(res.costs); } return { "pathes": paths, "costs": pathCosts }; } function ClosestCityPath(city1, city2) { res = CityPathes(city1, city2); if ( res.costs.Count() == 0 ) return null; return res.pathes[res.costs.Begin()]; } function pollEvents() { while (AIEventController.IsEventWaiting()) { local e = AIEventController.GetNextEvent(); switch (e.GetEventType()) { case AIEvent.AI_ET_VEHICLE_CRASHED: local ec = AIEventVehicleCrashed.Convert(e); local v = ec.GetVehicleID(); AILog.Info("We have a crashed vehicle (" + v + ")"); /* Handle the crashed vehicle */ AIVehicle.StartStopVehicle( wAIVehicle.CloneVehicle( AIDepotList() .Valuate(ClosestDepotToRoad( AIEventVehicleCrashed.Convert(e).GetCrashSite()) ) .End(), AIEventVehicleCrashed.Convert(e).GetVehicleID(); true ) ); break; } } } function max(a, b) { return a > b ? a : b; } function EstimateNumVehicles(path, engine) { return max( AITile.GetManhattanDistance(path.Begin(), path.End()) + 3) / AIEngine.GetMaxSpeed(engine) - AIEngine.GetCapacity(engine) / 8), 1 ); } function linkGivenCities(engine, start, end) { path = ClosestCityPath(start, end); if ( path == null ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + ": No path found!"); return false; } if ( !path.BuildPath() ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + ": Road building failed!"); return false; } else { depot = path.RandomNeighboringPoint(); if ( depot == null ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + ": No depot location found in pathway built!"); return false; } if ( !AIRoad.BuildRoadDepot(depot.pos, depot.from) ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + ": Depot building failed!"); return false; } veh = BuildVehicle(depot.pos, engine); for ( i = 1; i <= EstimateNumVehicles(path, engine); i++ ) { if ( veh == null ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + " with vehicle #" + i + ": Vehicle buying failed!"); return false; } if ( !path.BuildStations() ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + " with vehicle #" + i + ": Could not build stations!"); return false; } if ( AITile.GetManhattanDistance(depot, path.Begin()) < AITile.GetManhattanDistance(depot, path.End()) ) locs = [path.Begin(), depot.pos, path.End()]; else locs = [path.Begin(), path.End(), depot.pos]; foreach ( l in locs ) { if ( !AIOrder.AppendOrder( veh, l, l == depot.pos ? AIOrder.AIOrderFlags.OF_SERVICE_IF_NEEDED : AIOrder.AIOrderFlags.OF_NONE ) ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + " with vehicle #" + i + ": Could not add vehicle orders!"); return false; } } if ( !AIVehicle.StartStopVehicle(veh) ) { AILog.Warning("Could not link " + AITown.GetName(start) + " to " + AITown.GetName(end) + " with vehicle #" + i + ": Could not start vehicle!"); return false; } } } return true; } function linkCities(engine) { } class EdsgerMain extends AIController { function Start(); } function NameCompany() { if ( AICompany.IsMine(AICompany.CompanyID.COMPANY_SELF) ) { retries = 0; while ( !AICompany.SetName(pick(names) + " " + pick(suffixes)) && retries < 25 ) retries++; if ( retries == 25 ) { i = 0; while ( true ) { retries = 0; while ( !AICompany.SetName(pick(names) + " " + pick(suffixes)) + " #" + i && retries < 25 ) retries++; if ( retries == 25 ) i++; else break; } } AICompany.SetPresidentName("Edsger W. Dijkstra") } } function EdsgerMain::Start() { NameCompany(); while (true) { pollEvents(); cheapVeh = AIEngineList(AIVehicle.VehicleType.VT_ROAD) .Valuate(function(engine) { AICargo.HasCargoClass(AIEngine.GetCargoType(engine), pick(cityCargoClasses)) }) .KeepValue(1) .Valuate(AIEngine.GetPrice) .End(); checkMoney(12000 + AIEngine.GetPrice(cheapVeh), function(cost) { linkCities(cheapVeh); }) this.Sleep(150); } }