1 #include "AIFramework.h"
2 
3 #include "AIClientApp.h"
4 #include "AIWrapper.h"
5 #include "../../universe/BuildingType.h"
6 #include "../../universe/Universe.h"
7 #include "../../util/Directories.h"
8 #include "../../util/Logger.h"
9 #include "../../util/i18n.h"
10 #include "../../util/OptionsDB.h"
11 #include "../../util/ScopedTimer.h"
12 #include "../../Empire/Empire.h"
13 #include "../../Empire/Diplomacy.h"
14 #include "../../python/CommonFramework.h"
15 #include "../../python/SetWrapper.h"
16 #include "../../python/CommonWrappers.h"
17 
18 #include <boost/python.hpp>
19 #include <boost/python/suite/indexing/vector_indexing_suite.hpp>
20 #include <boost/python/suite/indexing/map_indexing_suite.hpp>
21 #include <boost/python/docstring_options.hpp>
22 #include <boost/mpl/vector.hpp>
23 #include <boost/python/list.hpp>
24 #include <boost/python/extract.hpp>
25 #include <boost/python/scope.hpp>
26 #include <boost/filesystem.hpp>
27 
28 using boost::python::class_;
29 using boost::python::def;
30 using boost::python::return_value_policy;
31 using boost::python::copy_const_reference;
32 using boost::python::reference_existing_object;
33 using boost::python::return_by_value;
34 using boost::python::return_internal_reference;
35 
36 using boost::python::vector_indexing_suite;
37 using boost::python::map_indexing_suite;
38 
39 using boost::python::object;
40 using boost::python::import;
41 using boost::python::error_already_set;
42 using boost::python::exec;
43 using boost::python::dict;
44 using boost::python::list;
45 using boost::python::extract;
46 
47 namespace fs = boost::filesystem;
48 
49 
BOOST_PYTHON_MODULE(freeOrionAIInterface)50 BOOST_PYTHON_MODULE(freeOrionAIInterface)
51 {
52     boost::python::docstring_options doc_options(true, true, false);
53 
54     ///////////////////
55     //  Game client  //
56     ///////////////////
57     FreeOrionPython::WrapAI();
58 
59     //////////////////
60     //    Empire    //
61     //////////////////
62     FreeOrionPython::WrapEmpire();
63 
64     /////////////////////
65     // UniverseClasses //
66     /////////////////////
67     FreeOrionPython::WrapUniverseClasses();
68     FreeOrionPython::WrapGalaxySetupData();
69 
70     ///////////////////
71     //     Enums     //
72     ///////////////////
73     FreeOrionPython::WrapGameStateEnums();
74 
75     ///////////////////
76     //     Config    //
77     ///////////////////
78     FreeOrionPython::WrapConfig();
79 
80     ////////////////////
81     // STL Containers //
82     ////////////////////
83     class_<std::vector<int>>("IntVec")
84         .def(vector_indexing_suite<std::vector<int>>())
85     ;
86     class_<std::vector<std::string>>("StringVec")
87         .def(vector_indexing_suite<std::vector<std::string>>())
88     ;
89 
90     class_<std::map<int, bool>>("IntBoolMap")
91         .def(map_indexing_suite<std::map<int, bool>>())
92     ;
93 
94     FreeOrionPython::SetWrapper<int>::Wrap("IntSet");
95     FreeOrionPython::SetWrapper<std::string>::Wrap("StringSet");
96 }
97 
98 //////////////////////
99 //     PythonAI     //
100 //////////////////////
Initialize()101 bool PythonAI::Initialize() {
102     if (PythonBase::Initialize()) {
103         BuildingTypeManager& temp = GetBuildingTypeManager();  // Ensure buildings are initialized
104         (void)temp; // Hide unused variable warning
105         return true;
106     }
107     else
108         return false;
109 }
110 
InitImports()111 bool PythonAI::InitImports() {
112     DebugLogger() << "Initializing AI Python imports";
113     // allows the "freeOrionAIInterface" C++ module to be imported within Python code
114     return PyImport_AppendInittab("freeOrionAIInterface", &PyInit_freeOrionAIInterface) != -1;
115 }
116 
InitModules()117 bool PythonAI::InitModules() {
118     DebugLogger() << "Initializing AI Python modules";
119 
120     // Confirm existence of the directory containing the AI Python scripts
121     // and add it to Pythons sys.path to make sure Python will find our scripts
122     std::string ai_path = boost::filesystem::canonical(GetResourceDir() / GetOptionsDB().Get<std::string>("ai-path")).string();
123     DebugLogger() << "AI Python script path: " << ai_path;
124     if (!fs::exists(ai_path)) {
125         ErrorLogger() << "Can't find folder containing AI scripts";
126         return false;
127     }
128     AddToSysPath(ai_path);
129 
130     // import universe generator script file
131     m_python_module_ai = import("FreeOrionAI");
132 
133     DebugLogger() << "AI Python modules successfully initialized!";
134     return true;
135 }
136 
GenerateOrders()137 void PythonAI::GenerateOrders() {
138     DebugLogger() << "PythonAI::GenerateOrders : initializing turn";
139 
140     ScopedTimer order_timer;
141     try {
142         // call Python function that generates orders for current turn
143         //DebugLogger() << "PythonAI::GenerateOrders : getting generate orders object";
144         object generateOrdersPythonFunction = m_python_module_ai.attr("generateOrders");
145         //DebugLogger() << "PythonAI::GenerateOrders : generating orders";
146         generateOrdersPythonFunction();
147     } catch (const error_already_set& err) {
148         HandleErrorAlreadySet();
149         if (!IsPythonRunning() || GetOptionsDB().Get<bool>("testing"))
150             throw;
151 
152         ErrorLogger() << "PythonAI::GenerateOrders : Python error caught.  Partial orders sent to server";
153     }
154 
155     AIClientApp* app = AIClientApp::GetApp();
156     // encodes order sets and sends turn orders message.  "done" the turn for the client, but "starts" the turn for the server
157     app->StartTurn(app->GetAI()->GetSaveStateString());
158 
159     DebugLogger() << "PythonAI::GenerateOrders order generating time: " << order_timer.DurationString();
160 }
161 
HandleChatMessage(int sender_id,const std::string & msg)162 void PythonAI::HandleChatMessage(int sender_id, const std::string& msg) {
163     // call Python function that responds or ignores a chat message
164     object handleChatMessagePythonFunction = m_python_module_ai.attr("handleChatMessage");
165     handleChatMessagePythonFunction(sender_id, msg);
166 }
167 
HandleDiplomaticMessage(const DiplomaticMessage & msg)168 void PythonAI::HandleDiplomaticMessage(const DiplomaticMessage& msg) {
169     // call Python function to inform of diplomatic message change
170     object handleDiplomaticMessagePythonFunction = m_python_module_ai.attr("handleDiplomaticMessage");
171     handleDiplomaticMessagePythonFunction(msg);
172 }
173 
HandleDiplomaticStatusUpdate(const DiplomaticStatusUpdateInfo & u)174 void PythonAI::HandleDiplomaticStatusUpdate(const DiplomaticStatusUpdateInfo& u) {
175     // call Python function to inform of diplomatic status update
176     object handleDiplomaticStatusUpdatePythonFunction = m_python_module_ai.attr("handleDiplomaticStatusUpdate");
177     handleDiplomaticStatusUpdatePythonFunction(u);
178 }
179 
StartNewGame()180 void PythonAI::StartNewGame() {
181     FreeOrionPython::ClearStaticSaveStateString();
182     // call Python function that sets up the AI to be able to generate orders for a new game
183     object startNewGamePythonFunction = m_python_module_ai.attr("startNewGame");
184     startNewGamePythonFunction(m_aggression);
185 }
186 
ResumeLoadedGame(const std::string & save_state_string)187 void PythonAI::ResumeLoadedGame(const std::string& save_state_string) {
188     //DebugLogger() << "PythonAI::ResumeLoadedGame(" << save_state_string << ")";
189     FreeOrionPython::SetStaticSaveStateString(save_state_string);
190     // call Python function that deals with the new state string sent by the server
191     object resumeLoadedGamePythonFunction = m_python_module_ai.attr("resumeLoadedGame");
192     resumeLoadedGamePythonFunction(FreeOrionPython::GetStaticSaveStateString());
193 }
194 
GetSaveStateString() const195 const std::string& PythonAI::GetSaveStateString() const {
196     // call Python function that serializes AI state for storage in save file and sets s_save_state_string
197     // to contain that string
198     object prepareForSavePythonFunction = m_python_module_ai.attr("prepareForSave");
199     prepareForSavePythonFunction();
200     //DebugLogger() << "PythonAI::GetSaveStateString() returning: " << s_save_state_string;
201     return FreeOrionPython::GetStaticSaveStateString();
202 }
203 
SetAggression(int aggr)204 void PythonAI::SetAggression(int aggr)
205 { m_aggression = aggr; }
206