--- a/bin/ai/regression/regression.txt Mon Mar 31 17:20:44 2008 +0000
+++ b/bin/ai/regression/regression.txt Mon Mar 31 18:33:33 2008 +0000
@@ -6309,3 +6309,4 @@
GetFromIndustryID: 65535
GetToIndustryID: 21
IsEventWaiting: false
+ERROR: We've got a suicidal AI for player 1
--- a/bin/ai/regression/run.sh Mon Mar 31 17:20:44 2008 +0000
+++ b/bin/ai/regression/run.sh Mon Mar 31 18:33:33 2008 +0000
@@ -18,7 +18,7 @@
if [ -n "$gdb" ]; then
$gdb ./openttd -a regression -x -c ai/regression/regression.cfg $params -g ai/regression/regression.sav
else
- ./openttd -a regression -x -c ai/regression/regression.cfg $params -g ai/regression/regression.sav 2>tmp.stderr | awk '{ gsub("0x\\(nil\\)", "0x00000000", $0); print $0; }' > tmp.regression
+ ./openttd -a regression -x -c ai/regression/regression.cfg $params -g ai/regression/regression.sav -d ai=2 2>&1 | awk '{ gsub("0x\\(nil\\)", "0x00000000", $0); gsub("^dbg: \\[ai\\]", "", $0); gsub("^ ", "ERROR: ", $0); gsub("ERROR: \\[1\\] ", "", $0); print $0; }' > tmp.regression
fi
if [ -z "$gdb" ]; then
@@ -30,16 +30,10 @@
echo "$res"
fi
echo ""
- stderr="`cat tmp.stderr`"
- if [ -n "$stderr" ]; then
- echo "OpenTTD gave this on stderr:"
- echo "$stderr"
- fi
- echo ""
echo "Regression test done"
fi
-rm -f ai/regression/main.nut tmp.stderr
+rm -f ai/regression/main.nut
if [ "$1" != "-k" ]; then
rm -f tmp.regression
--- a/bin/ai/wrightai/main.nut Mon Mar 31 17:20:44 2008 +0000
+++ b/bin/ai/wrightai/main.nut Mon Mar 31 18:33:33 2008 +0000
@@ -37,7 +37,7 @@
local loan = money - AICompany.GetBankBalance(AICompany.MY_COMPANY) + AICompany.GetLoanInterval() + AICompany.GetLoanAmount();
loan = loan - loan % AICompany.GetLoanInterval();
- print(this.name + ": [INFO] Need a loan to get " + money + ": " + loan);
+ AILog.Info("Need a loan to get " + money + ": " + loan);
AICompany.SetLoanAmount(loan);
}
@@ -52,7 +52,7 @@
/* Get enough money to work with */
this.GetMoney(150000);
- print(this.name + ": [INFO] Trying to build an airport route");
+ AILog.Info("Trying to build an airport route");
local tile_1 = this.FindSuitableAirportSpot(airport_type, 0);
if (tile_1 < 0) return -1;
@@ -64,13 +64,13 @@
/* Build the airports for real */
if (!AIAirport.BuildAirport(tile_1, airport_type)) {
- 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 + ".");
+ AILog.Error("Although the testing told us we could build 2 airports, it still failed on the first airport at tile " + tile_1 + ".");
this.towns_used.RemoveValue(tile_1);
this.towns_used.RemoveValue(tile_2);
return -3;
}
if (!AIAirport.BuildAirport(tile_2, airport_type)) {
- 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 + ".");
+ AILog.Error("Although the testing told us we could build 2 airports, it still failed on the second airport at tile " + tile_2 + ".");
AIAirport.RemoveAirport(tile_1);
this.towns_used.RemoveValue(tile_1);
this.towns_used.RemoveValue(tile_2);
@@ -86,7 +86,7 @@
return ret;
}
- print(this.name + ": [INFO] Done building a route");
+ AILog.Info("Done building a route");
return ret;
}
@@ -116,12 +116,12 @@
engine = engine_list.Begin();
if (!AIEngine.IsValidEngine(engine)) {
- print(this.name + ": [ERROR] Couldn't find a suitable engine");
+ AILog.Error("Couldn't find a suitable engine");
return -5;
}
local vehicle = AIVehicle.BuildVehicle(hangar, engine);
if (!AIVehicle.IsValidVehicle(vehicle)) {
- print(this.name + ": [ERROR] Couldn't build the aircraft");
+ AILog.Error("Couldn't build the aircraft");
return -6;
}
/* Send him on his way */
@@ -132,7 +132,7 @@
this.route_1.AddItem(vehicle, tile_1);
this.route_2.AddItem(vehicle, tile_2);
- print(this.name + ": [INFO] Done building an aircraft");
+ AILog.Info("Done building an aircraft");
return 0;
}
@@ -199,7 +199,7 @@
if (good_tile == 0) continue;
}
- print(this.name + ": [INFO] Found a good spot for an airport in town " + town + " at tile " + tile);
+ AILog.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);
@@ -207,7 +207,7 @@
return tile;
}
- print(this.name + ": [INFO] Couldn't find a suitable town to build an airport in");
+ AILog.Info("Couldn't find a suitable town to build an airport in");
return -1;
}
@@ -225,7 +225,7 @@
if (profit < 10000 && AIVehicle.GetProfitThisYear(i) < 10000) {
/* Send the vehicle to depot if we didn't do so yet */
if (!vehicle_to_depot.rawin(i) || vehicle_to_depot.rawget(i) != true) {
- print(this.name + ": [INFO] Sending " + i + " to depot as profit is: " + profit + " / " + AIVehicle.GetProfitThisYear(i));
+ AILog.Info("Sending " + i + " to depot as profit is: " + profit + " / " + AIVehicle.GetProfitThisYear(i));
AIVehicle.SendVehicleToDepot(i);
vehicle_to_depot.rawset(i, true);
}
@@ -233,7 +233,7 @@
/* Try to sell it over and over till it really is in the depot */
if (vehicle_to_depot.rawin(i) && vehicle_to_depot.rawget(i) == true) {
if (AIVehicle.SellVehicle(i)) {
- print(this.name + ": [INFO] Selling " + i + " as it finally is in a depot.");
+ AILog.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 = AIVehicleList_Station(AIStation.GetStationID(this.route_1.GetValue(i)));
if (list2.Count() == 0) this.SellAirports(i);
@@ -266,7 +266,7 @@
/* 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 + " (" + AIStation.GetLocation(i) + ") has too many cargo, adding a new vehicle for the route.");
+ AILog.Info("Station " + i + " (" + AIStation.GetLocation(i) + ") has too many cargo, adding a new vehicle for the route.");
/* Make sure we have enough money */
this.GetMoney(50000);
@@ -281,7 +281,7 @@
*/
function WrightAI::SellAirports(i) {
/* Remove the airports */
- print(this.name + ": [INFO] Removing airports as nobody serves them anymore.");
+ AILog.Info("Removing airports as nobody serves them anymore.");
AIAirport.RemoveAirport(this.route_1.GetValue(i));
AIAirport.RemoveAirport(this.route_2.GetValue(i));
/* Free the towns_used entries */
@@ -300,7 +300,7 @@
case AIEvent.AI_ET_CRASHED_VEHICLE:
local ec = AIEventVehicleCrash.Convert(e);
local v = ec.GetVehicleID();
- print(this.name + ": [INFO] We have a crashed vehicle (" + v + "), buying a new one as replacement");
+ AILog.Info("We have a crashed vehicle (" + v + "), buying a new one as replacement");
this.BuildAircraft(this.route_1.GetValue(v), this.route_2.GetValue(v));
this.route_1.RemoveItem(v);
this.route_2.RemoveItem(v);
@@ -326,7 +326,7 @@
}
this.name = AICompany.GetCompanyName(AICompany.MY_COMPANY);
/* Say hello to the user */
- print(this.name + ": Welcome to WrightAI. I will be building airports all day long.");
+ AILog.Info("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 */
AICompany.SetLoanAmount(AICompany.GetLoanInterval());
@@ -350,7 +350,7 @@
else if (ret < 0 && ticker == 0) {
/* The AI failed to build a first airport and is deemed */
AICompany.SetCompanyName("FailedWrightAI");
- print(this.name + ": Failed to build first airport route, now giving up building. Repaying loan. Have a nice day!");
+ AILog.Error("Failed to build first airport route, now giving up building. Repaying loan. Have a nice day!");
AICompany.SetLoanAmount(0);
return;
}
--- a/source.list Mon Mar 31 17:20:44 2008 +0000
+++ b/source.list Mon Mar 31 18:33:33 2008 +0000
@@ -488,6 +488,7 @@
ai/api/ai_industry.hpp
ai/api/ai_industrylist.hpp
ai/api/ai_list.hpp
+ai/api/ai_log.hpp
ai/api/ai_map.hpp
ai/api/ai_marine.hpp
ai/api/ai_object.hpp
@@ -528,6 +529,7 @@
ai/api/ai_industry.cpp
ai/api/ai_industrylist.cpp
ai/api/ai_list.cpp
+ai/api/ai_log.cpp
ai/api/ai_map.cpp
ai/api/ai_marine.cpp
ai/api/ai_object.cpp
--- a/src/ai/ai_gui.cpp Mon Mar 31 17:20:44 2008 +0000
+++ b/src/ai/ai_gui.cpp Mon Mar 31 18:33:33 2008 +0000
@@ -6,6 +6,7 @@
#include "../openttd.h"
#include "../gui.h"
#include "../window_gui.h"
+#include "../player_func.h"
#include "../player_base.h"
#include "../player_gui.h"
#include "../economy_func.h"
@@ -18,6 +19,8 @@
#include "../gfx_func.h"
#include "../debug.h"
#include "ai_factory.hpp"
+#include "api/ai_object.hpp"
+#include "api/ai_log.hpp"
#include "table/strings.h"
#include "../table/sprites.h"
@@ -112,6 +115,37 @@
AIFactoryBase *factory = AI_GetPlayerFactory(_ai_debug_player);
DoDrawString(factory->GetAIName(), 7, 34, TC_BLACK);
+ PlayerID old_cp = _current_player;
+ _current_player = _ai_debug_player;
+
+ if (AIObject::GetLogPointer() == NULL) {
+ _current_player = old_cp;
+ break;
+ }
+ AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer();
+
+ for (int i = 0; i < log->count; i++) {
+ uint pos = (log->count + log->pos - i) % log->count;
+ if (log->type[pos] == 0) break;
+
+ int y = 12 * i;
+ if (y >= w->widget[AID_WIDGET_LOG_PANEL].bottom - w->widget[AID_WIDGET_LOG_PANEL].top - 12) break;
+
+ uint colour;
+ switch (log->type[pos]) {
+ case AILog::LOG_SQ_INFO: colour = TC_BLACK; break;
+ case AILog::LOG_SQ_ERROR: colour = TC_RED; break;
+ case AILog::LOG_INFO: colour = TC_BLACK; break;
+ case AILog::LOG_WARNING: colour = TC_YELLOW; break;
+ case AILog::LOG_ERROR: colour = TC_RED; break;
+ default: colour = TC_BLACK; break;
+ }
+
+ DoDrawStringTruncated(log->lines[pos], 7, w->widget[AID_WIDGET_LOG_PANEL].top + 6 + y, colour, w->widget[AID_WIDGET_LOG_PANEL].right - w->widget[AID_WIDGET_LOG_PANEL].left - 14);
+ }
+
+ _current_player = old_cp;
+
break;
}
--- a/src/ai/ai_squirrel.cpp Mon Mar 31 17:20:44 2008 +0000
+++ b/src/ai/ai_squirrel.cpp Mon Mar 31 18:33:33 2008 +0000
@@ -43,6 +43,7 @@
#include "api/ai_industry.hpp.sq"
#include "api/ai_industrylist.hpp.sq"
#include "api/ai_list.hpp.sq"
+#include "api/ai_log.hpp.sq"
#include "api/ai_map.hpp.sq"
#include "api/ai_marine.hpp.sq"
#include "api/ai_order.hpp.sq"
@@ -238,6 +239,7 @@
SQAIIndustryList_CargoAccepting_Register(this->engine);
SQAIIndustryList_CargoProducing_Register(this->engine);
SQAIList_Register(this->engine);
+ SQAILog_Register(this->engine);
SQAIMap_Register(this->engine);
SQAIMarine_Register(this->engine);
SQAIOrder_Register(this->engine);
--- a/src/ai/api/ai_controller.cpp Mon Mar 31 17:20:44 2008 +0000
+++ b/src/ai/api/ai_controller.cpp Mon Mar 31 18:33:33 2008 +0000
@@ -3,6 +3,8 @@
/** @file ai_controller.cpp Implementation of AIControler. */
#include "ai_controller.hpp"
+#include "ai_object.hpp"
+#include "ai_log.hpp"
#include "../../stdafx.h"
#include "../../openttd.h"
#include "../../player_func.h"
@@ -15,6 +17,5 @@
/* static */ void AIController::Print(bool error_msg, const char *message)
{
- if (error_msg) fprintf(stderr, "%s", message);
- else printf("%s", message);
+ AILog::Log(error_msg ? AILog::LOG_SQ_ERROR : AILog::LOG_SQ_INFO, message);
}
--- a/src/ai/api/ai_controller.hpp Mon Mar 31 17:20:44 2008 +0000
+++ b/src/ai/api/ai_controller.hpp Mon Mar 31 18:33:33 2008 +0000
@@ -72,9 +72,11 @@
static void Sleep(uint ticks);
/**
- * Print a message to the AI log.
- * @param error_msg If true, it is marked as error message.
- * @param message The message you want to log.
+ * When Squirrel triggers a print, this function is called.
+ * Squirrel calls this when 'print' is used, or when the script made an error.
+ * @param error_msg If true, it is a Squirrel error message.
+ * @param message The message Squirrel logged.
+ * @note Use AILog.Info/Warning/Error instead of 'print'.
*/
static void Print(bool error_msg, const char *message);
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_log.cpp Mon Mar 31 18:33:33 2008 +0000
@@ -0,0 +1,68 @@
+/* $Id$ */
+
+/** @file ai_log.cpp Implementation of AILog. */
+
+#include "ai_log.hpp"
+#include "../../core/alloc_func.hpp"
+#include "../../player_func.h"
+#include "../../debug.h"
+
+/* static */ void AILog::Info(const char *message)
+{
+ AILog::Log(LOG_INFO, message);
+}
+
+/* static */ void AILog::Warning(const char *message)
+{
+ AILog::Log(LOG_WARNING, message);
+}
+
+/* static */ void AILog::Error(const char *message)
+{
+ AILog::Log(LOG_ERROR, message);
+}
+
+/* static */ void AILog::Log(AILog::AILogType level, const char *message)
+{
+ if (AIObject::GetLogPointer() == NULL) {
+ AIObject::GetLogPointer() = new LogData();
+ LogData *log = (LogData *)AIObject::GetLogPointer();
+
+ log->lines = CallocT<char *>(80);
+ log->type = CallocT<AILog::AILogType>(80);
+ log->count = 80;
+ log->pos = log->count;
+ }
+ LogData *log = (LogData *)AIObject::GetLogPointer();
+
+ /* Go to the next log-line */
+ log->pos = (log->pos + 1) % log->count;
+
+ /* Free last message, and write new message */
+ free(log->lines[log->pos]);
+ log->lines[log->pos] = strdup(message);
+ log->type[log->pos] = level;
+
+ /* Cut string after first \n */
+ char *p;
+ while ((p = strchr(log->lines[log->pos], '\n')) != NULL) {
+ *p = '\0';
+ break;
+ }
+
+ /* Also still print to debug window */
+ DEBUG(ai, level, "[%d] %s", (uint)_current_player, log->lines[log->pos]);
+}
+
+/* static */ void AILog::FreeLogPointer()
+{
+ LogData *log = (LogData *)AIObject::GetLogPointer();
+
+ for (int i = 0; i < log->count; i++) {
+ free(log->lines[i]);
+ }
+
+ free(log->lines);
+ free(log->type);
+ delete log;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_log.hpp Mon Mar 31 18:33:33 2008 +0000
@@ -0,0 +1,71 @@
+/* $Id$ */
+
+/** @file ai_log.hpp Everything to handle and issue log messages. */
+
+#ifndef AI_LOG_HPP
+#define AI_LOG_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class that handles all log related functions.
+ */
+class AILog : public AIObject {
+ /* AIController needs access to Enum and Log, in order to keep the flow from
+ * OpenTTD core to NoAI API clear and simple. */
+ friend class AIController;
+
+public:
+ static const char *GetClassName() { return "AILog"; }
+
+ /**
+ * Log levels; The value is also feed to DEBUG() lvl.
+ * This has no use for you, as AI writer.
+ */
+ enum AILogType {
+ LOG_SQ_ERROR = 0, //!< Squirrel printed an error.
+ LOG_ERROR = 1, //!< User printed an error.
+ LOG_SQ_INFO = 2, //!< Squirrel printed some info.
+ LOG_WARNING = 3, //!< User printed some warning.
+ LOG_INFO = 4, //!< User printed some info.
+ };
+
+ struct LogData {
+ char **lines;
+ AILog::AILogType *type;
+ int count;
+ int pos;
+ };
+
+ /**
+ * Print an Info message to the logs.
+ * @param message The message to log.
+ */
+ static void Info(const char *message);
+
+ /**
+ * Print a Warning message to the logs.
+ * @param message The message to log.
+ */
+ static void Warning(const char *message);
+
+ /**
+ * Print an Error message to the logs.
+ * @param message The message to log.
+ */
+ static void Error(const char *message);
+
+ /**
+ * Free the log pointer.
+ * @note DO NOT CALL YOURSELF; leave it to the internal AI programming.
+ */
+ static void FreeLogPointer();
+
+private:
+ /**
+ * Internal command to log the message in a common way.
+ */
+ static void Log(AILog::AILogType level, const char *message);
+};
+
+#endif /* AI_LOG_HPP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_log.hpp.sq Mon Mar 31 18:33:33 2008 +0000
@@ -0,0 +1,34 @@
+#include "ai_log.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AILog::AILogType GetParam(ForceType<AILog::AILogType>, HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AILog::AILogType)tmp; }
+ template <> int Return<AILog::AILogType>(HSQUIRRELVM vm, AILog::AILogType res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AILog to be used as Squirrel parameter */
+ template <> AILog *GetParam(ForceType<AILog *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AILog *)instance; }
+ template <> AILog &GetParam(ForceType<AILog &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AILog *)instance; }
+ template <> const AILog *GetParam(ForceType<const AILog *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AILog *)instance; }
+ template <> const AILog &GetParam(ForceType<const AILog &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AILog *)instance; }
+ template <> int Return<AILog *>(HSQUIRRELVM vm, AILog *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AILog", res, NULL, DefSQDestructorCallback<AILog>); return 1; }
+}; // namespace SQConvert
+
+void SQAILog_Register(Squirrel *engine) {
+ DefSQClass <AILog> SQAILog("AILog");
+ SQAILog.PreRegister(engine);
+ SQAILog.AddConstructor<void (AILog::*)(), 1>(engine, "x");
+
+ SQAILog.DefSQConst(engine, AILog::LOG_SQ_ERROR, "LOG_SQ_ERROR");
+ SQAILog.DefSQConst(engine, AILog::LOG_ERROR, "LOG_ERROR");
+ SQAILog.DefSQConst(engine, AILog::LOG_SQ_INFO, "LOG_SQ_INFO");
+ SQAILog.DefSQConst(engine, AILog::LOG_WARNING, "LOG_WARNING");
+ SQAILog.DefSQConst(engine, AILog::LOG_INFO, "LOG_INFO");
+
+ SQAILog.DefSQStaticMethod(engine, &AILog::GetClassName, "GetClassName", 1, "x");
+ SQAILog.DefSQStaticMethod(engine, &AILog::Info, "Info", 2, "xs");
+ SQAILog.DefSQStaticMethod(engine, &AILog::Warning, "Warning", 2, "xs");
+ SQAILog.DefSQStaticMethod(engine, &AILog::Error, "Error", 2, "xs");
+ SQAILog.DefSQStaticMethod(engine, &AILog::FreeLogPointer, "FreeLogPointer", 1, "x");
+
+ SQAILog.PostRegister(engine);
+}
--- a/src/ai/api/ai_object.cpp Mon Mar 31 17:20:44 2008 +0000
+++ b/src/ai/api/ai_object.cpp Mon Mar 31 18:33:33 2008 +0000
@@ -3,6 +3,7 @@
/** @file ai_object.cpp Implementation of AIObject. */
#include "ai_object.hpp"
+#include "ai_log.hpp"
#include "table/strings.h"
#include "../../command_func.h"
#include "../../network/network.h"
@@ -99,6 +100,11 @@
return AIObject::GetDoCommandStruct(_current_player)->event_data;
}
+void *&AIObject::GetLogPointer()
+{
+ return AIObject::GetDoCommandStruct(_current_player)->log_data;
+}
+
AIObject::AIDoCommandStruct *AIObject::GetDoCommandStruct(PlayerID player)
{
/* Storage for data on per-AI level */
@@ -122,6 +128,8 @@
command_struct->costs = CommandCost();
if (command_struct->event_data != NULL) AIEventController::FreeEventPointer();
command_struct->event_data = NULL;
+ if (command_struct->log_data != NULL) AILog::FreeLogPointer();
+ command_struct->log_data = NULL;
}
bool AIObject::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint procc, bool water_protection)
--- a/src/ai/api/ai_object.hpp Mon Mar 31 17:20:44 2008 +0000
+++ b/src/ai/api/ai_object.hpp Mon Mar 31 18:33:33 2008 +0000
@@ -45,6 +45,7 @@
VehicleID new_vehicle_id;
SignID new_sign_id;
void *event_data;
+ void *log_data;
};
/**
@@ -156,6 +157,12 @@
* @note NEVER use this yourself in your AI!
*/
static void ResetInternalPlayerData();
+
+ /**
+ * Get the pointer to store log message in.
+ * @note NEVER use this yourself in your AI!
+ */
+ static void *&GetLogPointer();
};
#endif /* AI_OBJECT_HPP */