internal static bool BuildRail(TileIndex previous, TileIndex from, TileIndex to, TileIndex next, HashSet <TileIndex> forbidden, int railCount = 1, Action <TileIndex, string> sign = null) { var path = RailBuilder.FindPath(from, to, previous, forbidden, sign); if (path == null) { return(false); } for (var i = 0; i < path.Count - 1; i++) { var p = i == 0 ? next : path[i - 1].Tile; var c = path[i].Tile; var n = i == path.Count - 1 ? previous : path[i + 1].Tile; var nt = i == 0 ? BuildType.Basic : path[i - 1].Type; //sign(c, "[" + AIMap.GetTileX(c) + ", " + AIMap.GetTileY(c) + "] " + path[i].type); bool good = false; switch (path[i].Type) { case BuildType.Basic: if (path[i].Length > 1) { throw new Exception("Should not be rail"); } if (nt != BuildType.Basic) { continue; } //AILog.Info("Build a rail from [" + AIMap.GetTileX(p) + ", " + AIMap.GetTileY(p) + "] via [" + AIMap.GetTileX(c) + ", " + AIMap.GetTileY(c) + "] to [" + AIMap.GetTileX(n) + ", " + AIMap.GetTileY(n) + "]."); good = AIRail.BuildRail(p, c, n); break; case BuildType.Bridge: var bridgeTypes = new AIBridgeList_Length(path[i].Length); //AILog.Info("Build a bridge " + bridgeTypes.Begin() + " from [" + AIMap.GetTileX(c) + ", " + AIMap.GetTileY(c) + "] to [" + AIMap.GetTileX(n) + ", " + AIMap.GetTileY(n) + "]."); good = AIBridge.BuildBridge(AIVehicle.VT_RAIL, bridgeTypes.Begin(), c, n); if (!good) { sign(p, "s"); sign(c, "e"); } break; case BuildType.Tunnel: throw new Exception("Tunnels not supported"); } if (!good) { AILog.Error("Failed to build on [" + AIMap.GetTileX(c) + ", " + AIMap.GetTileY(c) + "]. Reason: " + AIError.GetLastErrorString()); } } return(true); }
/// <summary> /// Uses A*-algorithm to find the best route between two tiles. /// </summary> /// <param name="start">First tile to search from.</param> /// <param name="goal">Goal to find.</param> /// <param name="previous">Tile just before the first tile.</param> /// <param name="forbidden">List of tiles that should be never used on route.</param> /// <param name="sign">Optional function for building signs.</param> /// <returns></returns> internal static List <PathInfo> FindPath(TileIndex start, TileIndex goal, TileIndex previous, HashSet <TileIndex> forbidden = null, Action <TileIndex, string> sign = null) { // Nodes to evaluate var tilesToProcess = new FibonacciHeap <TileIndex>(); tilesToProcess.Insert(start, 0); // Nodes evaluated var cameFrom = new Dictionary <TileIndex, PathInfo>(); cameFrom[start] = new PathInfo(start, 1, 0, BuildType.Basic, new PathInfo(previous, 1, 0, BuildType.Basic, null)); while (tilesToProcess.Count() > 0) { var current = tilesToProcess.Pop(); if (current == goal) { // We found the target. return(RailBuilder.BuildFinalPath(cameFrom[current])); } var neighbors = RailBuilder.GetNeighbors(current, cameFrom[current]); foreach (var neighborItem in neighbors) { var neighbor = neighborItem.Tile; if ((neighbor == previous) || ((forbidden != null) && forbidden.Contains(neighbor))) { // We can't go here. continue; } var neighborDist = neighborItem.Length; if (!cameFrom.ContainsKey(neighbor)) { tilesToProcess.Insert(neighbor, neighborItem.Cost); } else { if (neighborItem.Cost >= cameFrom[neighbor].Cost) { continue; } } sign?.Invoke(neighbor, neighborItem.Cost.ToString()); cameFrom[neighbor] = neighborItem; } } return(null); }
protected /*override*/ void Start2() { SignManager signManager = new SignManager(); this.signs = new AISignList(); var list = new AIRailTypeList(); AIRail.SetCurrentRailType(list.Begin()); AICompany.SetLoanAmount(AICompany.GetMaxLoanAmount()); ////* //RailBuilder.BuildRail( // AIMap.GetTileIndex(42, 121), // AIMap.GetTileIndex(41, 121), // AIMap.GetTileIndex(31, 121), // AIMap.GetTileIndex(30, 121), // new HashSet<TileIndex>(), // 1, // this.BuildSign); ///*/ //var i = 39; //var ns = RailBuilder.GetNeighbors( // AIMap.GetTileIndex(i, 121), // new RailBuilder.PathInfo( // AIMap.GetTileIndex(i + 1, 121), // 1, // 1, // RailBuilder.BuildType.Rail, // new RailBuilder.PathInfo( // AIMap.GetTileIndex(i + 2, 121), // 1, // 1, // RailBuilder.BuildType.Rail, // null)), // this.BuildSign); //foreach (var n in ns) //{ // this.BuildSign(n.Tile, n.Cost.ToString()); //} ///* */ ///* try { if (this.workStatus == 0) { var towns = new AITownList(); towns.Valuate(AITown.GetPopulation); RailStationBuildResult s1 = null; foreach (var(town, _) in towns) { if (s1 == null) { s1 = RailStationBuilder.BuildStationNear(AITown.GetLocation(town), this.platformSize, 2); } else { RailStationBuildResult s2 = RailStationBuilder.BuildStationNear(AITown.GetLocation(town), this.platformSize, 2); HashSet <TileIndex> forbidden = new HashSet <TileIndex>(); forbidden.Add(s1.ExitFarther); forbidden.Add(s2.EntryFarther); var good = RailBuilder.BuildRail(s1.EntryCloser, s1.EntryFarther, s2.ExitFarther, s2.ExitCloser, forbidden); signManager.ClearSigns(); if (good) { forbidden = new HashSet <TileIndex>(); good = RailBuilder.BuildRail(s1.ExitCloser, s1.ExitFarther, s2.EntryFarther, s2.EntryCloser, forbidden, 1); } if (good) { if (s1.DepotTile != AIMap.TILE_INVALID) { TrainManager.BuildTrain(s1.DepotTile, new StationID[] { s1.StationID, s2.StationID }, this.BuildSign); } else { TrainManager.BuildTrain(s2.DepotTile, new StationID[] { s2.StationID, s1.StationID }, this.BuildSign); } } else { AILog.Info("No route found."); } s1 = null; //if (AICompany.GetBankBalance(AICompany.COMPANY_SELF) < 50000) { // Better to stop before bankrupcy. AILog.Info("Better to stop before bankrupcy."); break; } } } } this.workStatus = 1; } catch { AILog.Error("An error happened"); } /* */ signManager.ClearSigns(); AILog.Info("Sign count: " + this.signs.Count()); AIController.Sleep(150); AILog.Info("Remove all signs"); foreach (var(signId, _) in this.signs) { AISign.RemoveSign(signId); } while (true) { // Main loop //this.setMinLoan(); CsTestAi.SetMinimumLoanAmount(); Sleep(100); } }
internal static List <PathInfo> GetNeighbors(TileIndex tile, PathInfo cameFrom, Action <TileIndex, string> sign = null) { var oldcost = cameFrom.Cost; var result = new List <PathInfo>(); var isFlat = AITile.GetSlope(tile) == AITile.SLOPE_FLAT; var dirs = new int[][] { new int[] { 0, 1 }, new int[] { 0, -1 }, new int[] { 1, 0 }, new int[] { -1, 0 } }; var oldDir = Helper.GetDirection(tile, cameFrom.Previous.Tile); foreach (var dir in dirs) { var x = dir[0]; var y = dir[1]; if (cameFrom.Tile == tile + AIMap.GetTileIndex(x, y)) { continue; } var newDir = Helper.GetDirection(tile + AIMap.GetTileIndex(x, y), tile); var isCoast = AITile.IsCoastTile(tile); var straight = newDir == oldDir; var maxLength = isCoast ? 20 : (straight ? 5 : 1); TileIndex neighbor; for (var length = 1; AIMap.IsValidTile(neighbor = tile + AIMap.GetTileIndex(x * length, y * length)) && length <= maxLength; length++) { if (isCoast) { if (!straight) { break; } if (AITile.IsWaterTile(neighbor)) { continue; } } double multiplier = 1; if (AITile.IsFarmTile(neighbor) || AITile.IsRockTile(neighbor) || AITile.IsRoughTile(tile)) { // Make farms, rocks, etc more expensive. multiplier *= 1.1; } if (AITile.IsBuildable(neighbor)) { double angleFactor = RailBuilder.CalculateAngle(neighbor, tile, cameFrom.Previous); if (isCoast) { result.Add(new PathInfo( neighbor, length, oldcost + ((length * multiplier * 2) + angleFactor), BuildType.Bridge, cameFrom )); break; } else if ((isFlat && cameFrom.Length == 1) || ((length == 1) && straight)) { result.Add(new PathInfo( neighbor, length, oldcost + ((length * multiplier) + angleFactor), length == 1 ? BuildType.Basic : BuildType.Bridge, cameFrom )); break; } } else if (!( (AIRail.IsRailTile(neighbor) || AIRoad.IsRoadTile(neighbor) || AITile.IsWaterTile(neighbor)) && (AITile.GetSlope(neighbor) == AITile.SLOPE_FLAT) && !AIStation.IsValidStation(AIStation.GetStationID(neighbor)))) { // Can't built over break; } } } return(result); }