bin/ai/wrightai/main.nut
branchnoai
changeset 9667 053afa64f977
child 9668 6fe3d2cb9655
--- /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();