--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/ai/wrightai/main.nut Sun Jul 15 13:24:06 2007 +0000
@@ -0,0 +1,331 @@
+
+class WrightAI extends AIController {
+ airport = null;
+ company = null;
+ map = null;
+ order = null;
+ station = null;
+ vehicle = null;
+
+ name = null;
+ towns_used = null;
+ route_1 = null;
+ route_2 = null;
+ distance_of_route = {};
+ vehicle_to_depot = {};
+ vehicle_depot_tile = {};
+ delay_build_airport_route = 1000;
+
+ function Start();
+ function Stop();
+
+ constructor() {
+ this.airport = AIAirport();
+ this.company = AICompany();
+ this.map = AIMap();
+ this.order = AIOrder();
+ this.station = AIStation();
+ this.vehicle = AIVehicle();
+
+ this.towns_used = AIList();
+ this.route_1 = AIList();
+ this.route_2 = AIList();
+ }
+}
+
+/**
+ * Check if we have enough money (via loan and on bank).
+ */
+function WrightAI::HasMoney(money)
+{
+ if (this.company.GetBankBalance() + (this.company.GetMaxLoanAmount() - this.company.GetLoanAmount()) > money) return true;
+ return false;
+}
+
+/**
+ * Get the amount of money requested, loan if needed.
+ */
+function WrightAI::GetMoney(money)
+{
+ if (!this.HasMoney(money)) return;
+ if (this.company.GetBankBalance() > money) return;
+
+ local loan = money - this.company.GetBankBalance() + this.company.GetLoanInterval() + this.company.GetLoanAmount();
+ loan = loan - loan % this.company.GetLoanInterval();
+ print(this.name + ": Need a loan to get " + money + ": " + loan);
+ this.company.SetLoanAmount(loan);
+}
+
+/**
+ * Build an airport route. Find 2 cities that are big enough and try to build airport in both cities.
+ * Then we can build an aircraft and make some money.
+ */
+function WrightAI::BuildAirportRoute()
+{
+ /* Get enough money to work with */
+ this.GetMoney(150000);
+
+ print(this.name + ": [INFO] Trying to build an airport route");
+ local limit = 2500;
+ local tile_1 = -1;
+ local tile_2 = -1;
+
+ tile_1 = this.FindSuitableAirportSpot(0);
+ if (tile_1 < 0) return -1;
+ tile_2 = this.FindSuitableAirportSpot(tile_1);
+ /* XXX -- If tile_2 isn't found, tile_1 town is added to used list, but never really used */
+ if (tile_2 < 0) return -2;
+
+ /* Build the airports for real */
+ if (!this.airport.BuildAirport(tile_1, AIAirport.AT_LARGE)) {
+ print(this.name + ": [ERROR] Although the testing told us we could build 2 airports, it still failed on the first airport at tile " + tile_1 + ".");
+ /* XXX -- We should free the towns again for an other day */
+ return -3;
+ }
+ if (!this.airport.BuildAirport(tile_2, AIAirport.AT_LARGE)) {
+ print(this.name + ": [ERROR] Although the testing told us we could build 2 airports, it still failed on the second airport at tile " + tile_2 + ".");
+ this.airport.RemoveAirport(tile_1);
+ /* XXX -- We should free the towns again for an other day */
+ return -4;
+ }
+
+ local ret = this.BuildAircraft(tile_1, tile_2);
+ if (ret < 0) {
+ this.airport.RemoveAirport(tile_1);
+ this.airport.RemoveAirport(tile_2);
+ /* XXX -- We should free the towns again for an other day */
+ return ret;
+ }
+
+ print(this.name + ": [INFO] Done building a route");
+ return ret;
+}
+
+
+/**
+ * Build an aircraft with orders from tile_1 to tile_2.
+ * The best available aircraft of that time will be bought.
+ */
+function WrightAI::BuildAircraft(tile_1, tile_2)
+{
+ /* Build an aircraft */
+ local hangar = this.airport.GetHangarOfAirport(tile_1);
+ local engine = this.vehicle.FindBestAircraftVehicle(0, 0);
+ if (!this.vehicle.IsValidEngine(engine)) {
+ print(this.name + ": [ERROR] Couldn't find a suitable engine");
+ return -5;
+ }
+ local vehicle = this.vehicle.BuildVehicle(hangar, engine);
+ if (!this.vehicle.IsValidVehicle(vehicle)) {
+ print(this.name + ": [ERROR] Couldn't build the aircraft");
+ return -6;
+ }
+ /* Send him on his way */
+ this.order.AppendOrder(vehicle, tile_1, AIOrder.AIOF_NONE);
+ this.order.AppendOrder(vehicle, tile_2, AIOrder.AIOF_NONE);
+ this.vehicle.StartStopVehicle(vehicle);
+ this.distance_of_route.rawset(vehicle, AIMap.DistanceManhattan(tile_1, tile_2));
+ this.route_1.AddItem(vehicle, tile_1);
+ this.route_2.AddItem(vehicle, tile_2);
+
+ print(this.name + ": [INFO] Done building an aircraft");
+
+ return 0;
+}
+
+/**
+ * Find a suitable spot for an airport, walking all towns hoping to find one.
+ * When a town is used, it is marked as such and not re-used.
+ */
+function WrightAI::FindSuitableAirportSpot(center_tile)
+{
+ local town_list = AITownList();
+ /* Remove all the towns we already used */
+ town_list.RemoveList(this.towns_used);
+
+ town_list.Valuate(AITownListPopulation());
+ town_list.KeepAboveValue(500);
+ /* Keep the best 10, if we can't find 2 stations in there, just leave it anyway */
+ town_list.KeepTop(10);
+ town_list.Valuate(AIListRandomize());
+
+ /* Now find 2 suitable towns */
+ for (local town = town_list.Begin(); town_list.HasNext(); town = town_list.Next()) {
+ /* Don't make this a CPU hog */
+ Sleep(1);
+
+ local tile = AITown.GetLocation(town);
+
+ /* Create a 30x30 grid around the core of the town and see if we can find a spot for a small airport */
+ local list = AITileList();
+ /* XXX -- We assume we are more than 15 tiles away from the border! */
+ list.AddRectangle(tile - this.map.GetTileIndex(15, 15), tile + this.map.GetTileIndex(15, 15));
+ list.Valuate(AITileListBuildableRectangle(6, 6));
+ list.KeepValue(1);
+ if (center_tile != 0) {
+ /* If we have a tile defined, we don't want to be within 25 tiles of this tile */
+ list.Valuate(AITileListDistanceSquareToTile(center_tile));
+ list.KeepAboveValue(625);
+ }
+ /* Sort on acceptance, remove places that don't have acceptance */
+ list.Valuate(AITileListCargoAcceptance(0, 6, 6, 4));
+ list.RemoveBelowValue(10);
+
+ /* Couldn't find a suitable place for this town, skip to the next */
+ if (list.Count() == 0) continue;
+ /* Walk all the tiles and see if we can build the airport at all */
+ {
+ local test = AITestMode();
+ local good_tile = 0;
+
+ for (tile = list.Begin(); list.HasNext(); tile = list.Next()) {
+ Sleep(1);
+ if (!this.airport.BuildAirport(tile, AIAirport.AT_LARGE)) continue;
+ good_tile = tile;
+ break;
+ }
+
+ /* Did we found a place to build the airport on? */
+ if (good_tile == 0) continue;
+ }
+
+ print(this.name + ": [INFO] Found a good spot for an airport in town " + town + " at tile " + tile);
+
+ /* Make the town as used, so we don't use it again */
+ this.towns_used.AddItem(town, tile);
+ return tile;
+ }
+
+ print(this.name + ": [INFO] Couldn't find a suitable town to build an airport in");
+ return -1;
+}
+
+function WrightAI::ManageAirRoutes()
+{
+ local list = AIVehicleList();
+ list.Valuate(AIVehicleListAge());
+ /* Give the plane at least 2 years to make a difference */
+ list.KeepAboveValue(365 * 2);
+ list.Valuate(AIVehicleListProfitLastYear());
+
+ for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+ local profit = list.GetValue(i);
+ /* Profit last year and this year bad? Let's sell the vehicle */
+ if (profit < 10000 && this.vehicle.GetProfitThisYear(i) < 10000) {
+ /* Send the vehicle to depot if we didn't do so yet */
+ if (!this.vehicle_to_depot.rawin(i) || this.vehicle_to_depot.rawget(i) != true) {
+ print(this.name + ": [INFO] Sending " + i + " to depot as profit is: " + profit + " / " + this.vehicle.GetProfitThisYear(i));
+ this.vehicle.SendVehicleToDepot(i);
+ this.vehicle_to_depot.rawset(i, true);
+ }
+ }
+ /* Try to sell it over and over till it really is in the depot */
+ if (this.vehicle_to_depot.rawin(i) && this.vehicle_to_depot.rawget(i) == true) {
+ if (this.vehicle.SellVehicle(i)) {
+ print(this.name + ": [INFO] Selling " + i + " as it finally is in a depot.");
+ /* Check if we are the last one serving those airports; else sell the airports */
+ local list2 = AIStationVehicleList(this.station.GetStationID(this.route_1.GetValue(i)));
+ if (list2.Count() == 0) {
+ /* Remove the airports */
+ print(this.name + ": [INFO] Removing airports as nobody serves them anymore.");
+ this.airport.RemoveAirport(this.route_1.GetValue(i));
+ this.airport.RemoveAirport(this.route_2.GetValue(i));
+ this.route_1.RemoteItem(i);
+ this.route_2.RemoteItem(i);
+ /* XXX -- We should free the towns_used entries too */
+ }
+ this.vehicle_to_depot.rawdelete(i);
+ }
+ }
+ }
+
+ /* Don't try to add planes when we are short on cash */
+ if (!this.HasMoney(50000)) return;
+
+ list = AIStationList(AIStationList.STATION_AIRPORT);
+ list.Valuate(AIStationListCargoWaiting(0));
+ list.KeepAboveValue(250);
+
+ for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+ local list2 = AIStationVehicleList(i);
+ /* No vehicles going to this station, abort */
+ /* TODO -- Sell station */
+ if (list2.Count() == 0) continue;
+
+ /* Find the first vehicle that is going to this station */
+ local v = list2.Begin();
+ local dist = this.distance_of_route.rawget(v);
+
+ list2.Valuate(AIVehicleListAge());
+ list2.KeepBelowValue(dist);
+ /* Do not build a new vehicle if we bought a new one in the last DISTANCE days */
+ if (list2.Count() != 0) continue;
+
+ print(this.name + ": [INFO] Station " + i + " (" + this.station.GetLocation(i) + ") has too many cargo, adding a new vehicle for the route.");
+
+ /* Make sure we have enough money */
+ this.GetMoney(50000);
+
+ return this.BuildAircraft(this.route_1.GetValue(v), this.route_2.GetValue(v));
+ }
+}
+
+function WrightAI::Start()
+{
+ /* Sleep 1 tick, as we can't execute anything in tick 0 */
+ Sleep(1);
+
+ /* Give the boy a name */
+ if (!this.company.SetCompanyName("WrightAI")) {
+ local i = 2;
+ while (!this.company.SetCompanyName("WrightAI #" + i)) {
+ i++;
+ }
+ }
+ this.name = this.company.GetCompanyName();
+ print(this.name + ": Welcome to WrightAI. I will be building airports all day long.");
+
+ /* We start with almost no loan, and we take a loan when we want to build something */
+ this.company.SetLoanAmount(this.company.GetLoanInterval());
+
+ /* We need our local ticker, as GetTick() will skip ticks */
+ local ticker = 0;
+ /* Let's go on for ever */
+ while (true) {
+ ticker++;
+ /* Once in a while, with enough money, try to build something */
+ if (ticker % this.delay_build_airport_route == 0 && this.HasMoney(100000)) {
+ local ret = this.BuildAirportRoute();
+ if (ret == -1) {
+ /* No more route found, delay even more before trying to find an other */
+ this.delay_build_airport_route = 10000;
+ }
+ }
+ /* Manage the routes once in a while */
+ if (ticker % 2000 == 0) {
+ this.ManageAirRoutes();
+ }
+ /* Try to get ride of our loan once in a while */
+ if (ticker % 5000 == 0) {
+ this.company.SetLoanAmount(0);
+ }
+ /* Make sure we do not create infinite loops */
+ Sleep(1);
+ }
+}
+
+function WrightAI::Stop()
+{
+}
+
+class FWrightAI extends AIFactory {
+ function GetAuthor() { return "OpenTTD Dev Team"; }
+ function GetName() { return "WrightAI"; }
+ function GetDescription() { return "A simple AI that tries to beat you with only aircrafts"; }
+ function GetVersion() { return 1; }
+ function GetDate() { return "2007-07-15"; }
+ function CreateInstance() { return "WrightAI"; }
+}
+
+/* Tell the core we are an AI */
+iFWrightAI <- FWrightAI();