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

main.nut 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. /*
  2. * This file is part of CountryRoads.
  3. *
  4. * CountryRoads is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * CountryRoads is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with CountryRoads. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * Copyright 2015 Sebastian Strzelec
  18. */
  19. //import("util.MinchinWeb", "MetaLib", 9);
  20. import("util.MinchinWeb", "MetaLib", 9);
  21. require("pathfinder.nut");
  22. require("line.nut");
  23. require("town_manager.nut");
  24. require("delaunay.nut");
  25. require("namelist.nut");
  26. require("Pathfinder.Road.nut");
  27. RoadPathFinder <- _RoadPathfinder_;
  28. class CountryRoads extends AIController
  29. {
  30. constructor()
  31. {
  32. ::main_instance <- this;
  33. this.settings.pathfinderIterations <- CountryRoads.GetSetting("pathfinderIterations");
  34. this.settings.currencyMultiplier <- CountryRoads.GetSetting("currencyMultiplier");
  35. this.settings.buildTwisty <- CountryRoads.GetSetting("buildTwisty");
  36. this.settings.maxBridgeLength <- CountryRoads.GetSetting("maxBridgeLength");
  37. this.settings.maxTunnelLength <- CountryRoads.GetSetting("maxTunnelLength");
  38. this.settings.avoidSlopes <- CountryRoads.GetSetting("avoidSlopes");
  39. this.settings.avoidBridges <- CountryRoads.GetSetting("avoidBridges");
  40. this.settings.avoidTunnels <- CountryRoads.GetSetting("avoidTunnels");
  41. this.settings.avoidCoast <- CountryRoads.GetSetting("avoidCoast");
  42. this.settings.buildcars <- CountryRoads.GetSetting("buildcars");
  43. this.settings.buildatall <- CountryRoads.GetSetting("buildatall");
  44. this.settings.zerorccars <- CountryRoads.GetSetting("zerorccars");
  45. this.connectionfinder = RoadPathFinder();
  46. route_planner = null;
  47. company = AICompany();
  48. }
  49. function Init()
  50. {
  51. }
  52. function Start();
  53. builtTowns = null;
  54. settings = {};
  55. optimizing = false;
  56. statusSign = null;
  57. connectionfinder = null;
  58. company = null;
  59. vehicle_to_depot = {};
  60. route_planner = null;
  61. lines = [];
  62. passenger_cargo_id = AICargo.CC_MAIL;
  63. current_engine = null;
  64. delaunay_network = [];
  65. last_balance_string = "";
  66. }
  67. function CountryRoads::TimeStamp()
  68. {
  69. local date = AIDate.GetCurrentDate();
  70. local month = AIDate.GetMonth(date);
  71. local day = AIDate.GetDayOfMonth(date);
  72. local aMonth = "";
  73. local aDay = "";
  74. if (month < 10) aMonth = "0";
  75. if (day < 10) aDay = "0";
  76. local ret = "[" + AIDate.GetYear(date) + "-" + aMonth + AIDate.GetMonth(date) + "-" +
  77. aDay + AIDate.GetDayOfMonth(date) + "] ";
  78. return ret;
  79. }
  80. function CountryRoads::Save()
  81. {
  82. local table = {};
  83. return table;
  84. }
  85. function CountryRoads::Load(version, data)
  86. {
  87. }
  88. function CountryRoads::SetCompanyName(prefix)
  89. {
  90. AILog.Info(TimeStamp() + "Setting Company Name");
  91. local suffixes = ["Council", "Foundation", "Association", "Initiative"];
  92. AICompany.SetName(prefix + " " + suffixes[ AIBase.RandRange(suffixes.len()) ]);
  93. }
  94. function CountryRoads::Start()
  95. {
  96. AILog.Info(TimeStamp() + "Starting Script");
  97. AICompany.SetPresidentName( NameList.GetRandomName( AICompany.GetPresidentGender(AICompany.COMPANY_SELF) ) );
  98. SetCompanyName("Country Road");
  99. AICompany.SetAutoRenewStatus(true);
  100. AICompany.SetAutoRenewMonths(0);
  101. AICompany.SetAutoRenewMoney(0);
  102. //if (!AIMap.IsValidTile(AICompany.GetCompanyHQ(AICompany.COMPANY_SELF))) BuildHQ(0);
  103. //statusSign = AISign.BuildSign(AICompany.GetCompanyHQ(AICompany.COMPANY_SELF), "Road Council Initializing...");
  104. RepayLoan();
  105. local list= AICargoList();
  106. list.Valuate(function(cargoId) {
  107. AILog.Info(AICargo.GetCargoLabel(cargoId));
  108. return 1;
  109. });
  110. AIRoad.SetCurrentRoadType(AIRoad.ROADTYPE_ROAD);
  111. current_engine = Line.GetBestEngine(this.settings.zerorccars);
  112. AILog.Info(TimeStamp() + "Best engine is " + AIEngine.GetName(current_engine));
  113. AILog.Info(TimeStamp() + "We have " + AITown.GetTownCount() + " Towns, calculating network.");
  114. local locations = [];
  115. local townlist = AITownList();
  116. local townid = townlist.Begin();
  117. local tl = AITileList();
  118. local tl2 = AITileList();
  119. local townTile = null;
  120. local townInUse = false;
  121. do {
  122. townTile = AITown.GetLocation(townid);
  123. /*
  124. tl.Clear();
  125. tl2.Clear();
  126. tl.AddRectangle(townTile + AIMap.GetTileIndex(-8, -8), townTile + AIMap.GetTileIndex(8, 8));
  127. tl2.AddList(tl);
  128. tl2.Valuate(AIRoad.IsRoadStationTile);
  129. tl2.KeepValue(1);
  130. townInUse = false;
  131. for (local rstl = tl2.Begin(); !tl2.IsEnd() ; rstl = tl2.Next()){
  132. if (AITile.GetOwner(rstl) != AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)) {
  133. townInUse = true;
  134. break;
  135. }
  136. }
  137. if (!townInUse)
  138. {*/
  139. locations.append({
  140. x=AIMap.GetTileX(townTile),
  141. y=AIMap.GetTileY(townTile),
  142. sentinel=false,
  143. townid=townid
  144. });
  145. //AILog.Info("Appending x:" + AIMap.GetTileX(townTile) + " y:"+AIMap.GetTileY(townTile));
  146. //}
  147. townid = townlist.Next();
  148. } while (!townlist.IsEnd());
  149. if (locations.len()>0){
  150. local delaunayCalculator = Delaunay();
  151. delaunay_network = delaunayCalculator.triangulate(locations);
  152. }
  153. if (delaunay_network.len() < 1)
  154. {
  155. AILog.Warning(TimeStamp() + "Town network build error");
  156. return;
  157. }
  158. AILog.Info(TimeStamp() + "List sorted");
  159. if (settings.buildatall) {
  160. local triangles = delaunay_network.len();
  161. while (triangles--) {
  162. local triangle = delaunay_network[triangles];
  163. local town1 = triangle.a.townid;
  164. local town2 = triangle.b.townid;
  165. local town3 = triangle.c.townid;
  166. AILog.Warning(TimeStamp() + "Building Triangle "+triangles+": "+AITown.GetName(town1)+" - "+AITown.GetName(town2)+" - "+AITown.GetName(town3));
  167. local pairs = [[town1,town2],[town2,town3],[town3,town1]];
  168. pairs.sort(byDist);
  169. local i=0;
  170. while (i < pairs.len()){
  171. BuildRoadTowns(pairs[i][0], pairs[i][1]);
  172. if (this.settings.buildcars==2) {
  173. BusLine(pairs[i]);
  174. }
  175. i++;
  176. }
  177. ManageVehicles();
  178. }
  179. }
  180. if (this.settings.buildcars==3) {
  181. SetCompanyName("Federal Post");
  182. AILog.Warning(TimeStamp() + "Starting Building busses");
  183. /* Start Building busses */
  184. local triangles = delaunay_network.len();
  185. while (triangles--) {
  186. local triangle = delaunay_network[triangles];
  187. local town1 = triangle.a.townid;
  188. local town2 = triangle.b.townid;
  189. local town3 = triangle.c.townid;
  190. AILog.Warning(TimeStamp() + "Building Bus Triangle "+triangles+": "+AITown.GetName(town1)+" - "+AITown.GetName(town2)+" - "+AITown.GetName(town3));
  191. BusLine([town1, town2]);
  192. BusLine([town2, town3]);
  193. BusLine([town3, town1]);
  194. if (!this.HasMoney(AICompany.GetMaxLoanAmount()*0.2)) {
  195. AILog.Error(TimeStamp() + "No money left, waiting for income or reset");
  196. while (!this.HasMoney(AICompany.GetMaxLoanAmount()*0.2)) {
  197. ManageVehicles();
  198. RepayLoan();
  199. Sleep(100);
  200. }
  201. }
  202. /*if (this.company.GetLoanAmount() > 0) {
  203. if (this.GetIncome() > 0)
  204. AILog.Warning(TimeStamp() + "Has income, repaying loan");
  205. while (this.company.GetLoanAmount() > 0 && this.GetIncome() > 0) {
  206. ManageVehicles();
  207. RepayLoan();
  208. Sleep(100);
  209. }
  210. }*/
  211. }
  212. local stationList = AIStationList(AIStation.STATION_TRUCK_STOP);
  213. stationList.Valuate(function(stationId)
  214. {
  215. local acceptedList = AICargoList_StationAccepting(stationId);
  216. local dist = 0;
  217. for (local acceptedId = acceptedList.Begin(); !acceptedList.IsEnd(); acceptedId = acceptedList.Next()) {
  218. if (AICargo.HasCargoClass(acceptedId, AICargo.CC_MAIL))
  219. {
  220. acceptedId = acceptedList.Next();
  221. continue;
  222. }
  223. local industryId = CountryRoads.GetNearestIndustry(AIStation.GetLocation(stationId), acceptedId);
  224. dist = dist + AIIndustry.GetDistanceManhattanToTile(industryId, AIStation.GetLocation(stationId));
  225. //AILog.Info(CountryRoads.TimeStamp() + "Dist "+dist+" from " + AIStation.GetName(stationId) + " to " + AIIndustry.GetName(industryId));
  226. local industryNeedsList = AIList();
  227. industryNeedsList.AddList(AICargoList_IndustryAccepting(industryId));
  228. for (local industryNeedsCargoId = industryNeedsList.Begin(); !industryNeedsList.IsEnd(); industryNeedsCargoId = industryNeedsList.Next()) {
  229. if (AICargo.HasCargoClass(industryNeedsCargoId, AICargo.CC_PASSENGERS))
  230. continue;
  231. local industry2Id = CountryRoads.GetNearestIndustry(AIIndustry.GetLocation(industryId), industryNeedsCargoId);
  232. dist = dist + AIIndustry.GetDistanceManhattanToTile( industryId, AIIndustry.GetLocation(industry2Id));
  233. //AILog.Info(CountryRoads.TimeStamp() + "+Dist "+dist+" from " + AIIndustry.GetName(industryId) + " to " + AIIndustry.GetName(industry2Id));
  234. }
  235. }
  236. return dist;
  237. });
  238. stationList.KeepAboveValue(0);
  239. stationList.RemoveAboveValue(1000);
  240. stationList.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
  241. for (local stationId = stationList.Begin(); !stationList.IsEnd(); stationId = stationList.Next()) {
  242. local dist = stationList.GetValue(stationId);
  243. AILog.Warning(TimeStamp() + "Would build from " +
  244. AIStation.GetName(stationId) + " dist " + dist
  245. );
  246. /*local acceptedList = AICargoList_StationAccepting(stationId);
  247. local dist = 0;
  248. for (local acceptedId = acceptedList.Begin(); !acceptedList.IsEnd(); acceptedId = acceptedList.Next())
  249. {
  250. if (AICargo.HasCargoClass(acceptedId, AICargo.CC_MAIL))
  251. {
  252. acceptedId = acceptedList.Next();
  253. continue;
  254. }
  255. local industryId = CountryRoads.GetNearestIndustry(AIStation.GetLocation(stationId), acceptedId);
  256. //AILog.Info(CountryRoads.TimeStamp() + "Dist "+dist+" from " + AIStation.GetName(stationId) + " to " + AIIndustry.GetName(industryId));
  257. local industryNeedsList = AIList();
  258. industryNeedsList.AddList(AICargoList_IndustryAccepting(industryId));
  259. for (local industryNeedsCargoId = industryNeedsList.Begin(); !industryNeedsList.IsEnd(); industryNeedsCargoId = industryNeedsList.Next()) {
  260. if (AICargo.HasCargoClass(industryNeedsCargoId, AICargo.CC_PASSENGERS))
  261. continue;
  262. local industry2Id = CountryRoads.GetNearestIndustry(AIIndustry.GetLocation(industryId), industryNeedsCargoId);
  263. //AILog.Info(CountryRoads.TimeStamp() + "+Dist "+dist+" from " + AIIndustry.GetName(industryId) + " to " + AIIndustry.GetName(industry2Id));
  264. industryNeedsCargoId = industryNeedsList.Next();
  265. }
  266. acceptedId = acceptedList.Next();
  267. }*/
  268. }
  269. /*local Walker = MetaLib.SpiralWalker();
  270. Walker.Start(AIIndustry.GetLocation(industryId));
  271. local tile = Walker.Walk();
  272. local last_tile = tile;
  273. local built = AIRoad.BuildRoad(tile, last_tile);
  274. while (built == false && Walker.GetStage()/4<=4) {
  275. last_tile = tile;
  276. tile = Walker.Walk();
  277. if (!(built = AIRoad.BuildRoad(tile, last_tile)))
  278. AISign.BuildSign(last_tile,"no");
  279. }*/
  280. //BuildRoad( AIStation.GetLocation(stationId), tile, AIStation.GetName(stationId), AIIndustry.GetName(industryId) );
  281. }
  282. AILog.Warning(TimeStamp() + "Finished work. Maintaining Network");
  283. local triangles = delaunay_network.len();
  284. local triangle = 0, counter = 0;
  285. while (1) {
  286. counter++;
  287. if (counter >= 30)
  288. {
  289. RebuildRoad(triangle++);
  290. if (triangle >= triangles)
  291. triangle = 0;
  292. counter = 0;
  293. }
  294. ManageVehicles();
  295. RepayLoan();
  296. Sleep(100);
  297. }
  298. }
  299. function CountryRoads::GetNearestIndustry( tile, cargoType )
  300. {
  301. local industryList = AIList();
  302. industryList.AddList(AIIndustryList_CargoProducing(cargoType));
  303. industryList.Valuate(AIIndustry.GetDistanceManhattanToTile, tile);
  304. return industryList.Begin();
  305. }
  306. function CountryRoads::GetIncome()
  307. {
  308. local incomeLastYear =
  309. AICompany.GetQuarterlyIncome(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+1) +
  310. AICompany.GetQuarterlyIncome(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+2) +
  311. AICompany.GetQuarterlyIncome(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+3) +
  312. AICompany.GetQuarterlyIncome(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+4);
  313. local expensesLastYear =
  314. AICompany.GetQuarterlyExpenses(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+1) +
  315. AICompany.GetQuarterlyExpenses(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+2) +
  316. AICompany.GetQuarterlyExpenses(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+3) +
  317. AICompany.GetQuarterlyExpenses(AICompany.COMPANY_SELF, AICompany.CURRENT_QUARTER+4);
  318. return incomeLastYear + expensesLastYear;
  319. }
  320. function CountryRoads::RebuildRoad(index)
  321. {
  322. if (!(index > -1 && index < delaunay_network.len()))
  323. return;
  324. local triangle = delaunay_network[index];
  325. local town1 = triangle.a.townid;
  326. local town2 = triangle.b.townid;
  327. local town3 = triangle.c.townid;
  328. local pairs = [[town1,town2],[town2,town3],[town3,town1]];
  329. pairs.sort(byDist);
  330. local i=0;
  331. while (i < pairs.len()) {
  332. BuildRoadTowns(pairs[i][0], pairs[i][1]);
  333. i++;
  334. }
  335. }
  336. function CountryRoads::HandleEvents()
  337. {
  338. while (AIEventController.IsEventWaiting()) {
  339. local e = AIEventController.GetNextEvent();
  340. AILog.Info("Handling Event: "+e.GetEventType());
  341. switch (e.GetEventType()) {
  342. case AIEvent.AI_ET_VEHICLE_CRASHED: {
  343. local crash = AIEventVehicleCrashed.Convert(e);
  344. local v = crash.GetVehicleID();
  345. AILog.Info(TimeStamp() + "Crashed vehicle (" + v + "), buying replacement");
  346. if ( lines.len() > 0 )
  347. {
  348. foreach (line_idx, line_it in lines) {
  349. if (line_it.IsVehicleOnLine(v)) {
  350. line_it.AddVehicles(current_engine, this.settings.zerorccars);
  351. line_it.vehicles.remove(line_it.vehicles.find(v));
  352. break;
  353. }
  354. }
  355. }
  356. } break;
  357. default:
  358. break;
  359. }
  360. }
  361. }
  362. function CountryRoads::BuildHQ(town)
  363. {
  364. // AICompany.BuildCompanyHQ(TileIndex tile)
  365. AILog.Info(TimeStamp() + "Building Company HQ in "+ AITown.GetName(town));
  366. local Walker = MetaLib.SpiralWalker();
  367. Walker.Start(AITown.GetLocation(town));
  368. local HQBuilt = false;
  369. while (HQBuilt == false) {
  370. HQBuilt = AICompany.BuildCompanyHQ(Walker.Walk());
  371. }
  372. this.RepayLoan();
  373. }
  374. /*------------------------------------------------------------------*/
  375. function CountryRoads::HasMoney(money)
  376. {
  377. // AILog.Info("Has money: balance " + AICompany.GetBankBalance(AICompany.COMPANY_SELF)) ;
  378. if (this.GetIncome() > 0)
  379. return AICompany.GetBankBalance(AICompany.COMPANY_SELF) - this.company.GetLoanAmount() > money;
  380. else
  381. return AICompany.GetBankBalance(AICompany.COMPANY_SELF) + (AICompany.GetMaxLoanAmount() - this.company.GetLoanAmount()) > money;
  382. }
  383. function CountryRoads::FormatMoney(money) {
  384. money = money * this.settings.currencyMultiplier;
  385. if (money < -1000000 || money > 1000000)
  386. return (money / 1000000) + "M";
  387. if (money < -1000 || money > 1000)
  388. return (money / 1000) + "k";
  389. return money;
  390. }
  391. function CountryRoads::RepayLoan() {
  392. local bankBalance = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
  393. local return_string = "";
  394. if (AICompany.GetLoanAmount() > 0 ) {
  395. local loan = 0
  396. local interval = AICompany.GetLoanInterval();
  397. if (AICompany.GetBankBalance(AICompany.COMPANY_SELF) > interval*2) {
  398. local worked = false;
  399. local actualLoan = AICompany.GetLoanAmount();
  400. do {
  401. worked = AICompany.SetLoanAmount(loan);
  402. loan += interval;
  403. } while (!worked && loan<actualLoan);
  404. }
  405. return_string = "Balance: " + FormatMoney(AICompany.GetBankBalance(AICompany.COMPANY_SELF)) +
  406. " Loan: " + FormatMoney(AICompany.GetLoanAmount()) +
  407. " Real Balance: " + FormatMoney(AICompany.GetBankBalance(AICompany.COMPANY_SELF)-AICompany.GetLoanAmount());
  408. }
  409. else
  410. return_string = "Balance: " + FormatMoney(AICompany.GetBankBalance(AICompany.COMPANY_SELF));
  411. if (return_string != last_balance_string && return_string != "") {
  412. AILog.Info(TimeStamp() + return_string);
  413. last_balance_string = return_string;
  414. }
  415. }
  416. /*
  417. * Pathfinds and builds road between two defined points
  418. */
  419. function CountryRoads::BuildRoad(start, end, startName, endName)
  420. {
  421. if ( start > end )
  422. {
  423. local tmp = start;
  424. start = end;
  425. end = tmp;
  426. }
  427. this.settings.pathfinderIterations = CountryRoads.GetSetting("pathfinderIterations");
  428. this.settings.currencyMultiplier = CountryRoads.GetSetting("currencyMultiplier");
  429. this.settings.buildTwisty = CountryRoads.GetSetting("buildTwisty");
  430. this.settings.maxBridgeLength = CountryRoads.GetSetting("maxBridgeLength");
  431. this.settings.maxTunnelLength = CountryRoads.GetSetting("maxTunnelLength");
  432. this.settings.avoidSlopes = CountryRoads.GetSetting("avoidSlopes");
  433. this.settings.avoidBridges = CountryRoads.GetSetting("avoidBridges");
  434. this.settings.avoidTunnels = CountryRoads.GetSetting("avoidTunnels");
  435. this.settings.avoidCoast = CountryRoads.GetSetting("avoidCoast");
  436. if (start == end) {
  437. AILog.Warning(TimeStamp() + "Trying to connect to self, distance is 0");
  438. return;
  439. }
  440. local dist = AITile.GetDistanceManhattanToTile(start, end);
  441. this.connectionfinder.PresetCheckExisting();
  442. this.connectionfinder.InitializePath([start], [end]);
  443. local path = this.connectionfinder.FindPath(this.settings.pathfinderIterations);
  444. while (path == false) {
  445. path = this.connectionfinder.FindPath(this.settings.pathfinderIterations);
  446. }
  447. local builtLength = 999999999999;
  448. if (path != null)
  449. {
  450. builtLength = path.GetLength();
  451. if (builtLength < 2*dist) {
  452. //AILog.Info(TimeStamp() + "- Already connected, path length: "+ path.GetLength());
  453. return;
  454. }
  455. }
  456. /* Tell OpenTTD we want to build normal road (no tram tracks) */
  457. /* Create an instance of the pathfinder */
  458. //local pathfinder = RoadPathFinder();
  459. local pathfinder = this.connectionfinder;
  460. /* Setting variables within the pathfinder */
  461. pathfinder.PresetPerfectPath();
  462. pathfinder._max_cost = 1000000; ///< The maximum cost for a route.
  463. pathfinder._cost_tile = 50; ///< The cost for a single tile.
  464. pathfinder._cost_no_existing_road = 50; ///< The cost that is added to `_cost_tile` if no road exists yet.
  465. pathfinder._cost_turn = this.settings.buildTwisty ? 0 : 50; ///< The cost that is added to `_cost_tile` if the direction changes.
  466. pathfinder._cost_slope = this.settings.avoidSlopes ? 200 : 100; ///< The extra cost if a road tile is sloped.
  467. pathfinder._cost_bridge_per_tile = this.settings.avoidBridges ? 150 : 100; ///< The cost per tile of a new bridge, this is added to `_cost_tile`.
  468. pathfinder._cost_tunnel_per_tile = this.settings.avoidTunnels ? 150 : 100; ///< The cost per tile of a new tunnel, this is added to `_cost_tile`.
  469. pathfinder._cost_coast = this.settings.avoidCoast ? 200 : 100; ///< The extra cost for a coast tile.
  470. pathfinder._cost_level_crossing = 100; ///< the extra cost for rail/road level crossings.
  471. pathfinder._cost_drivethru_station = 500; ///< The extra cost for drive-thru road stations.
  472. pathfinder._max_bridge_length = this.settings.maxBridgeLength; ///< The maximum length of a bridge that will be build.
  473. pathfinder._max_tunnel_length = this.settings.maxTunnelLength; ///< The maximum length of a tunnel that will be build.
  474. pathfinder._cost_only_existing_roads = false; ///< Choose whether to only search through existing connected roads
  475. pathfinder._distance_penalty = 1; ///< Penalty to use to speed up pathfinder, 1 is no penalty
  476. pathfinder.InitializePath([end], [start]);
  477. /* Try to find a path */
  478. local path = false;
  479. local startDate = AIDate.GetCurrentDate();
  480. AILog.Info(TimeStamp() + "Surveying route between " + startName + " and " + endName);
  481. while (path == false) {
  482. ManageVehicles();
  483. path = pathfinder.FindPath(this.settings.pathfinderIterations); /* @param iterations, how many tries before giving up,
  484. * -1 = infinity || > 0 */
  485. }
  486. local endDate = AIDate.GetCurrentDate();
  487. local dateDiff = endDate - startDate;
  488. if (path == null) {
  489. /* No path was found */
  490. AILog.Error(TimeStamp() + "Can't find path, skipping");
  491. return
  492. } else if (path.GetLength() >= builtLength) {
  493. AILog.Info(TimeStamp() + "No better path found, skipping");
  494. return
  495. }
  496. /* If a path was found, build a road over it */
  497. AILog.Info(TimeStamp() + "+ Path found after " + dateDiff + " days, building road between " + startName + " and " + endName);
  498. local bankBalance = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
  499. if (bankBalance < AICompany.GetMaxLoanAmount()) {
  500. local maxLoan = AICompany.GetMaxLoanAmount();
  501. local loan = AICompany.GetLoanAmount();
  502. local toLoan = maxLoan - loan;
  503. if (toLoan > 0) {
  504. //~ AILog.Info(TimeStamp() + "Loaning " + toLoan*this.settings.currencyMultiplier);
  505. AICompany.SetLoanAmount(maxLoan);
  506. this.Sleep(3);
  507. }
  508. if (!this.HasMoney(100)) {
  509. AILog.Error(TimeStamp() + "! No money left, sleeping until more money or reset");
  510. /* Make a code to kill AI so it can be reloaded */
  511. while (!this.HasMoney(20000)) {
  512. RepayLoan();
  513. ManageVehicles();
  514. Sleep(100);
  515. }
  516. }
  517. }
  518. AILog.Info(TimeStamp() + "+ The budget limit for this project is: " + FormatMoney(AICompany.GetBankBalance(AICompany.COMPANY_SELF)) + ".");
  519. local startBalance = AICompany.GetBankBalance(AICompany.COMPANY_SELF);
  520. while (path != null) {
  521. local par = path.GetParent();
  522. if (par != null) {
  523. local last_node = path.GetTile();
  524. if (AIMap.DistanceManhattan(path.GetTile(), par.GetTile()) == 1) {
  525. if (!AIRoad.BuildRoad(path.GetTile(), par.GetTile())) {
  526. /* An error occured while building a piece of road. TODO: handle it.
  527. * Note that this can also be the case of the road was already build */
  528. if (!this.HasMoney(100)) {
  529. AILog.Error(TimeStamp() + "! No money left, sleeping until more money or reset");
  530. /* Make a code to kill AI so it can be reloaded */
  531. while (!this.HasMoney(20000)) {
  532. RepayLoan();
  533. ManageVehicles();
  534. Sleep(100);
  535. }
  536. }
  537. }
  538. } else {
  539. /* Build a bridge or tunnel */
  540. // AILog.Info(TimeStamp() + "We need to build a bridge or tunnel");
  541. if (!AIBridge.IsBridgeTile(path.GetTile()) && !AITunnel.IsTunnelTile(path.GetTile())) {
  542. /* If it was a road tile, demolish it first. Do this to work
  543. * around expended roadbits */
  544. if (AIRoad.IsRoadTile(path.GetTile())) AITile.DemolishTile(path.GetTile());
  545. if (AITunnel.GetOtherTunnelEnd(path.GetTile()) == par.GetTile()) {
  546. if (!AITunnel.BuildTunnel(AIVehicle.VT_ROAD, path.GetTile())) {
  547. /* An error occured while building a tunnel. TODO: handle it */
  548. // AILog.Warning(TimeStamp() + "Error building tunnel");
  549. }
  550. } else {
  551. local bridge_list = AIBridgeList_Length(AIMap.DistanceManhattan(path.GetTile(),
  552. par.GetTile()) + 1);
  553. bridge_list.Valuate(AIBridge.GetPrice, AIMap.DistanceManhattan(path.GetTile(),
  554. par.GetTile()));
  555. //bridge_list.Sort(AIList.SORT_BY_VALUE, AIList.SORT_ASCENDING);
  556. if (!AIBridge.BuildBridge(AIVehicle.VT_ROAD, bridge_list.Begin(), path.GetTile(),
  557. par.GetTile())) {
  558. /* An error occured while building a bridge. TODO: handle it */
  559. // AILog.Warning(TimeStamp() + "Error building bridge");
  560. }
  561. }
  562. }
  563. }
  564. }
  565. path = par;
  566. }
  567. local spent = startBalance - AICompany.GetBankBalance(AICompany.COMPANY_SELF);
  568. AILog.Info(TimeStamp() + "= Project completed! Total spent: " + FormatMoney(spent));
  569. /* Repay loan as best we can to make us last longer */
  570. this.RepayLoan();
  571. }
  572. /*
  573. * Pathfinds and builds road between two defined points
  574. */
  575. function CountryRoads::BuildRoadTowns(start, end)
  576. {
  577. BuildRoad(AITown.GetLocation(start), AITown.GetLocation(end), AITown.GetName(start), AITown.GetName(end));
  578. }
  579. /*------------------------------------------------------------------*/
  580. function CountryRoads::BusLine(towns)
  581. {
  582. if (current_engine==null) {
  583. AILog.Info(TimeStamp() + "No road passenger vehicles for bus route");
  584. return;
  585. }
  586. if(!towns || towns.len() != 2) {
  587. AILog.Info(TimeStamp() + "Invalid town pair");
  588. return;
  589. }
  590. foreach (line_it in lines) {
  591. if (line_it.ready
  592. && min(line_it.towns[0],line_it.towns[1]) == min(towns[0],towns[1])
  593. && max(line_it.towns[0],line_it.towns[1]) == max(towns[0],towns[1])) {
  594. return true;
  595. }
  596. //AILog.Info("towns " + line_it.towns[0] + " " + line_it.towns[1] + " " + towns[0] + " " +towns[1]);
  597. }
  598. //AILog.Info("Towns " + AITown.GetName(towns[0]) + " " + AITown.GetName(towns[1]));
  599. //local connectionfinder = RoadPathFinder();
  600. this.connectionfinder.PresetCheckExisting();
  601. this.connectionfinder.InitializePath([AITown.GetLocation(min(towns[0],towns[1]))], [AITown.GetLocation(max(towns[0],towns[1]))]);
  602. local path = this.connectionfinder.FindPath(this.settings.pathfinderIterations);
  603. while (path == false) {
  604. path = this.connectionfinder.FindPath(this.settings.pathfinderIterations);
  605. }
  606. if ( path == null ) {
  607. AILog.Info(TimeStamp() + "No Path for bus route");
  608. return;
  609. }
  610. local cargo_list = AICargoList();
  611. cargo_list.Valuate(AICargo.HasCargoClass, passenger_cargo_id);
  612. if (cargo_list.Count() == 0) {
  613. /* There is no passenger cargo, so adding buses is useless. */
  614. this.passenger_cargo_id = null;
  615. return;
  616. }
  617. local line = Line(this.passenger_cargo_id);
  618. if (this.HasMoney(25000)) {
  619. if (this.GetIncome() <= 0) {
  620. AICompany.SetLoanAmount(AICompany.GetMaxLoanAmount());
  621. }
  622. //ManageVehicles();
  623. //AILog.Info(TimeStamp() + "Start building");
  624. local conn = line.CreateNewLine(towns);
  625. if(conn) {
  626. AILog.Info(TimeStamp() + "= Bus line completed");
  627. line.ready = true;
  628. line.AddVehicles(current_engine, this.settings.zerorccars);
  629. }
  630. // Pay back unused money
  631. RepayLoan();
  632. }
  633. if (!line.ready) {
  634. AILog.Info(TimeStamp() + "! Failed to add new bus line, queueing for later");
  635. }
  636. lines.append(line);
  637. return true;
  638. }
  639. /*------------------------------------------------------------------*/
  640. function CountryRoads::CargoLine(station, industry, cargo_id)
  641. {
  642. this.connectionfinder.PresetCheckExisting();
  643. this.connectionfinder.InitializePath([AITown.GetLocation(min(towns[0],towns[1]))], [AITown.GetLocation(max(towns[0],towns[1]))]);
  644. local path = this.connectionfinder.FindPath(this.settings.pathfinderIterations);
  645. while (path == false) {
  646. path = this.connectionfinder.FindPath(this.settings.pathfinderIterations);
  647. }
  648. if ( path == null ) {
  649. AILog.Info(TimeStamp() + "No Path for bus route");
  650. return;
  651. }
  652. local cargo_list = AICargoList();
  653. cargo_list.Valuate(AICargo.HasCargoClass, passenger_cargo_id);
  654. if (cargo_list.Count() == 0) {
  655. /* There is no passenger cargo, so adding buses is useless. */
  656. this.passenger_cargo_id = null;
  657. return;
  658. }
  659. local line = Line(this.passenger_cargo_id);
  660. if (this.HasMoney(25000)) {
  661. if (this.GetIncome() <= 0) {
  662. AICompany.SetLoanAmount(AICompany.GetMaxLoanAmount());
  663. }
  664. //ManageVehicles();
  665. //AILog.Info(TimeStamp() + "Start building");
  666. local conn = line.CreateNewLine(towns);
  667. if(conn) {
  668. AILog.Info(TimeStamp() + "= Bus line completed");
  669. line.ready = true;
  670. line.AddVehicles(current_engine, this.settings.zerorccars);
  671. }
  672. // Pay back unused money
  673. RepayLoan();
  674. }
  675. if (!line.ready) {
  676. AILog.Info(TimeStamp() + "! Failed to add new bus line, queueing for later");
  677. }
  678. lines.append(line);
  679. return true;
  680. }
  681. /*------------------------------------------------------------------*/
  682. function CountryRoads::ManageVehicles()
  683. {
  684. this.settings.zerorccars = CountryRoads.GetSetting("zerorccars");
  685. local list = AIVehicleList();
  686. list.Valuate(AIVehicle.GetAge);
  687. list.KeepAboveValue(5*365);
  688. list.Valuate(AIVehicle.GetProfitLastYear);
  689. for (local i = list.Begin(); !list.IsEnd(); i = list.Next()) {
  690. local profit = list.GetValue(i) + AIVehicle.GetProfitThisYear(i);
  691. if (profit < -100) {
  692. local station_lst = AIStationList_Vehicle(i);
  693. local station0 = null;
  694. local station1 = null;
  695. if (station_lst) {
  696. station0 = station_lst.Begin();
  697. if (!station_lst.IsEnd()){
  698. station1 = station_lst.Next();
  699. }
  700. }
  701. if (station0 && station1) {
  702. local rating_st0 = AIStation.GetCargoRating(station0, passenger_cargo_id);
  703. local rating_st1 = AIStation.GetCargoRating(station1, passenger_cargo_id);
  704. if ((rating_st0 >= 60) && (rating_st1 >= 60)){
  705. /* Send the vehicle to depot if we didn't do so yet */
  706. if (!this.vehicle_to_depot.rawin(i) || this.vehicle_to_depot.rawget(i) != true) {
  707. //AILog.Info(TimeStamp() + "Sending " + AIVehicle.GetName(i) + " to depot as profit is: " + profit );
  708. AIVehicle.SendVehicleToDepot(i);
  709. this.vehicle_to_depot.rawset(i, true);
  710. }
  711. }
  712. }
  713. }
  714. /* Try to sell it over and over till it really is in the depot */
  715. if (this.vehicle_to_depot.rawin(i) && this.vehicle_to_depot.rawget(i) == true) {
  716. //AILog.Info(TimeStamp() + "- Selling " + AIVehicle.GetName(i) + " as it finally is in a depot.");
  717. if (AIVehicle.SellVehicle(i)) {
  718. this.vehicle_to_depot.rawdelete(i);
  719. }
  720. }
  721. }
  722. // check unready lines
  723. if ( lines.len() > 0 )
  724. {
  725. //AILog.Info(TimeStamp() + "Checking Vehicles on Lines");
  726. local max_cars_per_line = AIGameSettings.GetValue("vehicle.max_roadveh") / lines.len();
  727. local car_capacity = AIEngine.GetCapacity(current_engine);
  728. //AILog.Info(TimeStamp() + "Cars: " + max_cars_per_line + " capacity: " + car_capacity );
  729. foreach (line_idx, line_it in lines) {
  730. if ((AIDate.GetCurrentDate() - line_it.date_last_vehicle) < 14 || !line_it.ready)
  731. continue;
  732. if (this.HasMoney(AICompany.GetMaxLoanAmount()*0.2) && AIDate.GetCurrentDate() - line_it.date_last_vehicle > 30)
  733. {
  734. local waiting = AIStation.GetCargoWaitingVia(AIStation.GetStationID(line_it.stations[0][0]),AIStation.GetStationID(line_it.stations[1][0]), passenger_cargo_id) +
  735. AIStation.GetCargoWaitingVia(AIStation.GetStationID(line_it.stations[1][0]),AIStation.GetStationID(line_it.stations[0][0]), passenger_cargo_id);
  736. local rating0 = AIStation.GetCargoRating(AIStation.GetStationID(line_it.stations[0][0]), passenger_cargo_id);
  737. local rating1 = AIStation.GetCargoRating(AIStation.GetStationID(line_it.stations[1][0]), passenger_cargo_id);
  738. if ((waiting > car_capacity && rating0 < 60 || rating1 < 60) || rating0 < 50 || rating1 < 50) {
  739. //AILog.Info(TimeStamp() + "Waiting: " + waiting + " ("+rating0+"/"+rating1+")" );
  740. if (line_it.vehicles.len()>=max_cars_per_line)
  741. {
  742. line_it.date_last_vehicle = AIDate.GetCurrentDate()+30;
  743. //AILog.Info(TimeStamp() + "- Line " + line_idx + " has max vehicles: " + line_it.vehicles.len() + "/" + max_cars_per_line );
  744. continue;
  745. }
  746. if (this.GetIncome() <= 0)
  747. AICompany.SetLoanAmount(AICompany.GetMaxLoanAmount());
  748. if(line_it.vehicles.len()>0) {
  749. local new_veh = AIVehicle.CloneVehicle (line_it.depot[0], line_it.vehicles[0], true);
  750. if (AIVehicle.IsValidVehicle(new_veh)) {
  751. line_it.vehicles.append(new_veh);
  752. line_it.pending_vehicles = line_it.pending_vehicles + 1;
  753. //line_it.date_last_vehicle = AIDate.GetCurrentDate();
  754. //AILog.Info("Adding bus to " + line_it.);
  755. //AILog.Info(TimeStamp() + "+ Line " + line_idx + " Added vehicle: " + (line_it.vehicles.len()+1) + "/" + max_cars_per_line );
  756. }
  757. } else {
  758. line_it.AddVehicles(current_engine, this.settings.zerorccars);
  759. }
  760. RepayLoan();
  761. }
  762. line_it.date_last_vehicle = AIDate.GetCurrentDate();
  763. }
  764. //AILog.Info(TimeStamp() + "Line " + line_idx + " Pending: " + line_it.pending_vehicles + " last "+ (AIDate.GetCurrentDate() - line_it.date_last_vehicle) );
  765. if (line_it.pending_vehicles) {
  766. local veh_to_start = line_it.vehicles[line_it.vehicles.len() - line_it.pending_vehicles];
  767. AIVehicle.StartStopVehicle(veh_to_start);
  768. line_it.pending_vehicles = line_it.pending_vehicles - 1;
  769. line_it.date_last_vehicle = AIDate.GetCurrentDate();
  770. }
  771. }
  772. }
  773. local best_engine = Line.GetBestEngine(this.settings.zerorccars);
  774. if (current_engine == null) {
  775. current_engine = best_engine;
  776. } else if (current_engine != best_engine && best_engine != null) {
  777. AILog.Info(TimeStamp() + "New best engine: " + AIEngine.GetName(best_engine) + " replacing: " + AIEngine.GetName(current_engine));
  778. local engine_list = AIEngineList(AIVehicle.VT_ROAD);
  779. engine_list.Valuate(AIEngine.GetRoadType);
  780. engine_list.KeepValue(AIRoad.ROADTYPE_ROAD);
  781. for (local engine = engine_list.Begin(); !engine_list.IsEnd(); engine = engine_list.Next()) {
  782. if (AIEngine.IsValidEngine(AIGroup.GetEngineReplacement(AIGroup.GROUP_ALL, engine))) {
  783. AIGroup.SetAutoReplace(AIGroup.GROUP_ALL, engine, best_engine);
  784. AILog.Info(TimeStamp() + " and: " + AIEngine.GetName(engine));
  785. }
  786. }
  787. AIGroup.SetAutoReplace(AIGroup.GROUP_ALL, current_engine, best_engine);
  788. current_engine = best_engine;
  789. }
  790. }
  791. /*--------------------------------------------------------------------
  792. | |
  793. | Tile |
  794. | |
  795. --------------------------------------------------------------------*/
  796. class Tile
  797. {
  798. constructor() {
  799. }
  800. function GetAdjacentTiles(tile);
  801. function IsRoadBuildable(tile); // used for valuator
  802. }
  803. function Tile::GetAdjacentTiles(tile)
  804. {
  805. local adjTiles = AITileList();
  806. adjTiles.AddTile(tile - AIMap.GetTileIndex(1,0));
  807. adjTiles.AddTile(tile - AIMap.GetTileIndex(0,1));
  808. adjTiles.AddTile(tile - AIMap.GetTileIndex(-1,0));
  809. adjTiles.AddTile(tile - AIMap.GetTileIndex(0,-1));
  810. return adjTiles;
  811. }
  812. function Tile::IsRoadBuildable(tile)
  813. {
  814. if (AITile.IsBuildable(tile) || AIRoad.IsRoadTile(tile)) return true;
  815. return false;
  816. }