OpenTTD AI which builds a road network between all towns it can reach.

line.nut 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /*--------------------------------------------------------------------
  2. | |
  3. | Line |
  4. | |
  5. --------------------------------------------------------------------*/
  6. class Line
  7. {
  8. towns = null;
  9. stations = null;
  10. depot = null;
  11. vehicles = null;
  12. date_last_vehicle = 0;
  13. ready = false;
  14. passenger_cargo_id = 0;
  15. pending_vehicles = 0;
  16. new_location = null;
  17. try_rebuild = false;
  18. town_exit = null;
  19. n_buses = 0;
  20. constructor(cargo_id) {
  21. towns = [];
  22. stations = [[], []];
  23. depot = [];
  24. vehicles = [];
  25. ready = false;
  26. date_last_vehicle = 0;
  27. passenger_cargo_id = cargo_id;
  28. new_location = [0,0];
  29. pending_vehicles = 0;
  30. n_buses = 0;
  31. }
  32. function AddVehicles();
  33. function EstimateBusesNeeded(station0, station1);
  34. function AddDepot();
  35. function CreateNewLine(towns);
  36. }
  37. function Line::CreateNewLine(town_pair)
  38. {
  39. local connected = false;
  40. local success = [false, false];
  41. if (town_pair) {
  42. local town_idx = 0;
  43. towns = town_pair;
  44. AILog.Info(CountryRoads.TimeStamp() + "New bus line from: " + AITown.GetName(town_pair[0]) + " to " + AITown.GetName(town_pair[1]));
  45. local town_idx = 0;
  46. foreach (town in town_pair){
  47. local built = TownManager.FindLineBusStopLocation(town, passenger_cargo_id, false);
  48. if (built) {
  49. stations[town_idx].append( built );
  50. //AISign.BuildSign(built,"st");
  51. success[town_idx] = true;
  52. //town_exit[town_idx] = TownManager.CreateExitRoute(AIRoad.GetRoadStationFrontTile(built), built);
  53. //AILog.Info(CountryRoads.TimeStamp() + "busstop " + town_idx + " built");
  54. town_idx++;
  55. }
  56. }
  57. // Build depot
  58. if((success[0] == false) || (success[1] == false)) {
  59. AILog.Info(CountryRoads.TimeStamp() + "Failed to build bus stations");
  60. }
  61. else {
  62. depot.append(AddDepot(AITown.GetLocation(towns[0])));
  63. depot.append(AddDepot(AITown.GetLocation(towns[1])));
  64. if (depot[0]) {
  65. //AILog.Info("Depot built " + depot);
  66. connected = true;
  67. //local handle = AIStation.GetStationID(depot[0]);
  68. //AILog.Info("id "+depot[0]+" station "+handle);
  69. }
  70. else if (depot[1]) {
  71. depot[0] = depot[1];
  72. connected = true;
  73. }
  74. else {
  75. AILog.Info(CountryRoads.TimeStamp() + "Failed to build depot");
  76. }
  77. }
  78. }
  79. else {
  80. AILog.Info(CountryRoads.TimeStamp() + "no 2 towns to connect");
  81. }
  82. return connected;
  83. }
  84. /*------------------------------------------------------------------*/
  85. function Line::IsVehicleOnLine(vehicle)
  86. {
  87. return vehicles.find(vehicle);
  88. }
  89. /*------------------------------------------------------------------*/
  90. function Line::GetBestEngine(zerorccars)
  91. {
  92. local min_reliability = 60;
  93. local engine_list = AIEngineList(AIVehicle.VT_ROAD);
  94. engine_list.Valuate(AIEngine.GetRoadType);
  95. engine_list.KeepValue(AIRoad.ROADTYPE_ROAD);
  96. /*local balance = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
  97. engine_list.Valuate(AIEngine.GetPrice);
  98. engine_list.KeepBelowValue(balance);*/
  99. //engine_list.Valuate(AIEngine.GetCargoType);
  100. //engine_list.KeepValue(passenger_cargo_id);
  101. engine_list.Valuate(function(engine_id, cargo_id) {
  102. if (AIEngine.GetCargoType(engine_id)==cargo_id)
  103. return 1;
  104. else if (AIEngine.CanRefitCargo(engine_id, cargo_id))
  105. return 1;
  106. return 0;
  107. }, passenger_cargo_id);
  108. engine_list.KeepAboveValue(0);
  109. engine_list.Valuate(AIEngine.GetReliability);
  110. engine_list.KeepAboveValue(min_reliability);
  111. /*if (current_engine) {
  112. //AILog.Info("current max speed "+ AIEngine.GetMaxSpeed(current_engine));
  113. engine_list.Valuate(AIEngine.GetMaxSpeed);
  114. engine_list.RemoveBelowValue(AIEngine.GetMaxSpeed(current_engine));
  115. //AILog.Info("highest speed "+ AIEngine.GetMaxSpeed(engine_list.Begin()));
  116. engine_list.Sort(AIList.SORT_BY_VALUE, true);
  117. //AILog.Info("lowest speed "+ AIEngine.GetMaxSpeed(engine_list.Begin()));
  118. }*/
  119. if (zerorccars!=2) {
  120. engine_list.Valuate(AIEngine.GetRunningCost);
  121. if (zerorccars==1) {
  122. engine_list.KeepAboveValue(0);
  123. } else if (zerorccars==3) {
  124. engine_list.KeepValue(0);
  125. }
  126. }
  127. engine_list.Valuate(AIEngine.GetMaxSpeed);
  128. local maxSpeed = engine_list.Begin();
  129. engine_list.Valuate(function(engine_id, passenger_cargo_id, maxSpeed) {
  130. local speed = AIEngine.GetMaxSpeed(engine_id);
  131. local cap = AIEngine.GetCapacity(engine_id);
  132. local rel = AIEngine.GetReliability(engine_id);
  133. local rcost = AIEngine.GetRunningCost(engine_id);
  134. local cost = AIEngine.GetPrice(engine_id);
  135. local life = AIEngine.GetMaxAge(engine_id)/365;
  136. local ret = 1000* (speed * cap).tofloat() / (cost.tofloat() / life + rcost + 1) * ( rel.tofloat()/100 ) /* (speed.tofloat()/maxSpeed)*/;
  137. //AILog.Info("val (" + speed + " * "+cap+")="+(speed * cap)+" / ("+cost+" / "+life+" + "+rcost+" + 1)="+(cost.tofloat() / life + rcost + 1)+" * ( "+rel+"/100 )="+( rel.tofloat()/100 )+" * ("+speed+"/"+maxSpeed+")="+(speed.tofloat()/maxSpeed)+" = "+ ret);
  138. //local ret = ((AIEngine.GetCapacity(engine_id) * AIEngine.GetMaxSpeed(engine_id) * AIEngine.GetReliability(engine_id)) /* (AIEngine.GetRunningCost(engine_id)+1)*/);
  139. /*if (AIEngine.CanRefitCargo(engine_id, passenger_cargo_id)) {
  140. ret = ret / 4;
  141. }*/
  142. if (AIEngine.GetName(engine_id).find("Fire") != null
  143. || AIEngine.GetName(engine_id).find("Ambulance") != null
  144. || AIEngine.GetName(engine_id).find("Dustbin") != null
  145. || AIEngine.GetName(engine_id).find("Police") != null)
  146. ret = 0;
  147. //AILog.Info("Vehicle " + AIEngine.GetName(engine_id) + " " + ret);
  148. return ret.tointeger();
  149. }, passenger_cargo_id, maxSpeed);
  150. engine_list.Sort(AIList.SORT_BY_VALUE, AIList.SORT_DESCENDING);
  151. if (engine_list.Count() == 0)
  152. return;
  153. /*local engine_id;// = engine_list.Begin();
  154. for (engine_id = engine_list.Begin(); !engine_list.IsEnd(); engine_id = engine_list.Next()) {
  155. AILog.Info("Vehicle " + AIEngine.GetName(engine_id) +
  156. " " + engine_list.GetValue(engine_id) +
  157. " " + AIEngine.GetPrice(engine_id) +
  158. " " + AIEngine.GetMaxSpeed(engine_id) +
  159. " " + AIEngine.GetCapacity(engine_id) +
  160. " " + AIEngine.GetRunningCost(engine_id) +
  161. " " + (AIEngine.GetCapacity(engine_id) * AIEngine.GetMaxSpeed(engine_id)) +
  162. " " + (((AIEngine.GetCapacity(engine_id) * AIEngine.GetMaxSpeed(engine_id) * AIEngine.GetReliability(engine_id)) / (AIEngine.GetRunningCost(engine_id)+1))) +
  163. " " + AIEngine.GetDesignDate(engine_id) +
  164. " " + AIEngine.GetMaxAge(engine_id)/365 +
  165. " " + AIEngine.GetReliability(engine_id)
  166. );
  167. }*/
  168. //} while (!engine_list.IsEnd());
  169. //engine_list.Valuate(AIEngine.GetCapacity)
  170. engine_list.KeepTop(1);
  171. return engine_list.Begin();
  172. }
  173. /*------------------------------------------------------------------*/
  174. function Line::AddVehicles(bus_model, zerorccars)
  175. {
  176. if(this.stations[0].len() != this.stations[1].len()) {
  177. AILog.Info("AddVehicles nof stations incorrect");
  178. return null;
  179. }
  180. //local bus_model = GetBestEngine(zerorccars);
  181. local new_bus;
  182. local i;
  183. if ( n_buses == 0 ) {
  184. n_buses = 1;//EstimateBusesNeeded(this.stations[0][this.stations[0].len()-1], this.stations[1][this.stations[1].len()-1]);
  185. }
  186. else {
  187. AILog.Info("Add vehicles: retry to add buses to the line" + n_buses);
  188. }
  189. if (!bus_model) {
  190. AILog.Info("BuyBuses: failed to find a bus to build");
  191. return false;
  192. }
  193. // Buy buses
  194. local buses_build = 0;
  195. for(i = 0; i < n_buses; ++i)
  196. {
  197. new_bus = AIVehicle.BuildVehicle(this.depot[0], bus_model);
  198. local er = AIError.GetLastError();
  199. if (AIVehicle.IsValidVehicle(new_bus)) {
  200. AIVehicle.RefitVehicle(new_bus, passenger_cargo_id);
  201. this.vehicles.append(new_bus);
  202. buses_build = buses_build + 1;
  203. if (buses_build > 1)
  204. AIOrder.ShareOrders(new_bus, this.vehicles[0]);
  205. }
  206. else {
  207. if (er != AIError.ERR_NOT_ENOUGH_CASH) {
  208. n_buses = n_buses - 1; // prevent endless retries
  209. }
  210. AILog.Info("Buy vehicles failed "+ er+" "+ AIError.GetLastErrorString() + " " + this.depot);
  211. }
  212. }
  213. //AISign.BuildSign(this.depot, "D");
  214. n_buses = n_buses - buses_build;
  215. pending_vehicles = pending_vehicles + buses_build;
  216. AILog.Info(CountryRoads.TimeStamp() + "Busses pending: " + pending_vehicles);
  217. if (this.vehicles.len() > 0)
  218. {
  219. local bus = this.vehicles[0];
  220. if(bus == null || !AIVehicle.IsValidVehicle(bus)) {
  221. AILog.Info("Vehicle[ " + bus + "] is not valid!");
  222. }
  223. else {
  224. AIOrder.AppendOrder(bus, this.stations[0][0], AIOrder.OF_NONE/*_STOP_INTERMEDIATE*/);
  225. AIOrder.AppendOrder(bus, this.depot[0], AIOrder.OF_NONE/*_STOP_INTERMEDIATE*/);
  226. AIOrder.AppendOrder(bus, this.depot[0], AIOrder.OF_SERVICE_IF_NEEDED);
  227. AIOrder.AppendOrder(bus, this.stations[1][0], AIOrder.OF_NONE/*_STOP_INTERMEDIATE*/);
  228. if(this.depot[1]) {
  229. AIOrder.AppendOrder(bus, this.depot[1], AIOrder.OF_NONE/*_STOP_INTERMEDIATE*/);
  230. AIOrder.AppendOrder(bus, this.depot[1], AIOrder.OF_SERVICE_IF_NEEDED);
  231. }
  232. }
  233. /*if (this.vehicles[1] != null)
  234. {
  235. AIOrder.SkipToOrder(this.vehicles[1], 1);
  236. }*/
  237. }
  238. }
  239. /*------------------------------------------------------------------*/
  240. function Line::EstimateBusesNeeded(station0, station1)
  241. {
  242. if(!station0 || !station1)
  243. return 0;
  244. local acceptance = AITile.GetCargoAcceptance(station0,
  245. passenger_cargo_id, 1, 1,
  246. AIStation.GetCoverageRadius (AIStation.STATION_BUS_STOP)) +
  247. AITile.GetCargoAcceptance(station1,
  248. passenger_cargo_id, 1, 1,
  249. AIStation.GetCoverageRadius (AIStation.STATION_BUS_STOP)) ;
  250. local distance = AIMap.DistanceManhattan( station0, station1 );
  251. AILog.Info("EstimateBusesNeeded: distance: " + distance +
  252. " acceptance: " + acceptance);
  253. local num_bus = 2 + (acceptance / 35) * (distance / 35);
  254. //if (num_bus > 25) num_bus = 25;
  255. local max_busses = AIGameSettings.GetValue("vehicle.max_roadveh");
  256. local num_towns = AITown.GetTownCount();
  257. local busses_per_town = (max_busses/num_towns);
  258. if ( num_bus > busses_per_town )
  259. num_bus = busses_per_town;
  260. AILog.Info("Buy " + num_bus + " buses");
  261. return num_bus;
  262. }
  263. /*------------------------------------------------------------------*/
  264. function Line::AddDepot(tile)
  265. {
  266. local tl = AITileList();
  267. local tl2 = AITileList();
  268. tl.AddRectangle(tile + AIMap.GetTileIndex(-10, -10), tile + AIMap.GetTileIndex(10, 10));
  269. tl2.AddList(tl);
  270. tl2.Valuate(AIRoad.IsRoadDepotTile);
  271. tl2.KeepValue(1);
  272. for (local rstl = tl2.Begin(); !tl2.IsEnd() ; rstl = tl2.Next()){
  273. /* reuse stations */
  274. if (AITile.GetOwner(rstl) == AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)) {
  275. return rstl;
  276. }
  277. }
  278. local tl = AITileList();
  279. local x = AIMap.GetTileX(tile);
  280. local y = AIMap.GetTileY(tile);
  281. local aitile = 0;
  282. local found = false;
  283. tl.AddRectangle(AIMap.GetTileIndex(x-10,y-10), AIMap.GetTileIndex(x+10,y+10));
  284. if (tl.Count())
  285. {
  286. if (tl.Count()) {
  287. /* find all tiles that are next to a road tile */
  288. tl.Valuate(AIRoad.GetNeighbourRoadCount);
  289. tl.KeepAboveValue(0);
  290. }
  291. if (tl.Count()) {
  292. /* find all tiles that are not road */
  293. tl.Valuate(AIRoad.IsRoadTile);
  294. tl.KeepValue(0);
  295. }
  296. if (tl.Count()) {
  297. /* find all tiles that are not sloped */
  298. tl.Valuate(AITile.GetSlope);
  299. tl.KeepValue(0);
  300. }
  301. if (tl.Count()) {
  302. tl.Valuate(AITile.GetDistanceManhattanToTile, tile/*AITown.GetLocation(towns[1])*/);
  303. tl.Sort(AITileList.SORT_BY_VALUE, true);
  304. local dpf = PathFinder();
  305. for (aitile = tl.Begin();!tl.IsEnd() && !found; aitile = tl.Next()) {
  306. local adjacentTiles = Tile.GetAdjacentTiles(aitile);
  307. /*Loop through all adjacent tiles*/
  308. for(local tile2 = adjacentTiles.Begin(); !adjacentTiles.IsEnd() && !found; tile2 = adjacentTiles.Next()) {
  309. if(AIRoad.IsRoadTile(tile2) && !AITile.GetSlope(tile2) ) {
  310. //if (!AIRoad.IsRoadTile(tile2+tile2-aitile)) {
  311. if(!AIRoad.IsRoadStationTile(aitile)) {
  312. found = AITile.IsBuildable(aitile);
  313. if (found) {
  314. local success = false;
  315. local count = 0;
  316. while(!success && (count < 100)){
  317. local pathlen = dpf.BuildRoad(tile2, aitile);
  318. if (pathlen == 2) {
  319. success = true;
  320. } else {
  321. count = count + 1;
  322. }
  323. }
  324. if (success) {
  325. found = AIRoad.BuildRoadDepot(aitile, tile2)
  326. }
  327. }
  328. }
  329. //}
  330. }
  331. }
  332. if (found) {
  333. break;
  334. }
  335. }
  336. if (!found) {
  337. for (aitile = tl.Begin();!tl.IsEnd() && !found; aitile = tl.Next()) {
  338. local adjacentTiles = Tile.GetAdjacentTiles(aitile);
  339. /*Loop through all adjacent tiles*/
  340. for(local tile2 = adjacentTiles.Begin(); !adjacentTiles.IsEnd() && !found; tile2 = adjacentTiles.Next()) {
  341. if(AIRoad.IsRoadTile(tile2) && !AITile.GetSlope(tile2) ) {
  342. //if (!AIRoad.IsRoadTile(tile2+tile2-aitile)) {
  343. if(!AIRoad.IsRoadStationTile(aitile)) {
  344. found = AITile.IsBuildable(aitile);
  345. if (!found) {
  346. found = AITile.DemolishTile(aitile);
  347. }
  348. if (found) {
  349. local success = false;
  350. local count = 0;
  351. while(!success && (count < 100)){
  352. local pathlen = dpf.BuildRoad(tile2, aitile);
  353. if (pathlen == 2) {
  354. success = true;
  355. } else {
  356. count = count + 1;
  357. }
  358. }
  359. if (success) {
  360. found = AIRoad.BuildRoadDepot(aitile, tile2)
  361. }
  362. }
  363. }
  364. //}
  365. }
  366. }
  367. if (found) {
  368. break;
  369. }
  370. }
  371. }
  372. }
  373. }
  374. if (found) {
  375. return aitile;
  376. }
  377. else {
  378. return null;
  379. }
  380. }