The Battle for Wesnoth  1.19.1+dev
engine_lua.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
3  by Yurii Chernyi <terraninfo@terraninfo.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * LUA AI Support engine - creating specific ai components from config
18  * @file
19  */
20 
21 #include "ai/lua/engine_lua.hpp"
22 #include "ai/composite/goal.hpp"
23 #include "ai/composite/rca.hpp"
24 #include "ai/composite/stage.hpp"
25 #include "ai/composite/aspect.hpp"
26 
28 
29 #include "log.hpp"
30 #include "resources.hpp"
31 #include "ai/lua/core.hpp"
32 #include "ai/lua/lua_object.hpp"
33 #include "game_board.hpp"
35 #include "units/unit.hpp"
36 #include "units/map.hpp"
37 #include "deprecation.hpp"
38 #include "game_version.hpp"
39 
40 namespace ai {
41 
42 static lg::log_domain log_ai_engine_lua("ai/engine/lua");
43 #define DBG_AI_LUA LOG_STREAM(debug, log_ai_engine_lua)
44 #define LOG_AI_LUA LOG_STREAM(info, log_ai_engine_lua)
45 #define WRN_AI_LUA LOG_STREAM(warn, log_ai_engine_lua)
46 #define ERR_AI_LUA LOG_STREAM(err, log_ai_engine_lua)
47 
48 #ifdef _MSC_VER
49 #pragma warning(push)
50 //silence "inherits via dominance" warnings
51 #pragma warning(disable:4250)
52 #endif
53 
55 
56 public:
58  : candidate_action(context, cfg),evaluation_action_handler_(),execution_action_handler_(),serialized_evaluation_state_(cfg.child_or_empty("args")),serialized_filterown_(cfg.child_or_empty("filter_own"))
59  {
60  // do nothing
61  }
62 
64 
65  virtual double evaluate()
66  {
67  auto l_obj = std::make_shared<lua_object<double>>();
68 
71  } else {
72  return BAD_SCORE;
73  }
74 
75  std::shared_ptr<double> result = l_obj->get();
76 
77  return result ? *result : 0.0;
78  }
79 
80  virtual void execute() {
82  lua_object_ptr nil;
84  }
85  }
86 
87  virtual config to_config() const {
90  cfg.add_child("filter_own",serialized_filterown_);
91  return cfg;
92  }
93 
94 protected:
95  std::shared_ptr<lua_ai_action_handler> evaluation_action_handler_;
96  std::shared_ptr<lua_ai_action_handler> execution_action_handler_;
99 };
100 
102 
103 public:
104  lua_candidate_action_wrapper( rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
105  : lua_candidate_action_wrapper_base(context,cfg),evaluation_(cfg["evaluation"]),execution_(cfg["execution"])
106  {
107  evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(evaluation_.c_str(),lua_ai_ctx));
108  execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(execution_.c_str(),lua_ai_ctx));
109  }
110 
112 
113  virtual config to_config() const
114  {
116  cfg["evaluation"] = evaluation_;
117  cfg["execution"] = execution_;
118  return cfg;
119  }
120 
121 private:
122  std::string evaluation_;
123  std::string execution_;
124 };
125 
127 public:
129  : lua_candidate_action_wrapper_base(context,cfg), location_(cfg["location"]), use_parms_(false)
130  {
131  if (cfg.has_attribute("exec_parms") || cfg.has_attribute("eval_parms")) {
132  deprecated_message("[candidate_action]eval_parms,exec_parms=", DEP_LEVEL::PREEMPTIVE, "1.17", "Use [args] instead - this data is passed to both the evaluation and the execution");
133  use_parms_ = true;
134  exec_parms_ = cfg["exec_parms"].str();
135  eval_parms_ = cfg["eval_parms"].str();
136  }
137  std::string eval_code;
138  std::string exec_code;
139  generate_code(eval_code, exec_code);
140 
141  evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(eval_code.c_str(),lua_ai_ctx));
142  execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(exec_code.c_str(),lua_ai_ctx));
143  }
144 
146 
147  virtual config to_config() const
148  {
150  cfg["location"] = location_;
151  if (use_parms_) {
152  cfg["eval_parms"] = eval_parms_;
153  cfg["exec_parms"] = exec_parms_;
154  }
155  return cfg;
156  }
157 
158 private:
159  std::string location_;
160  std::string eval_parms_;
161  std::string exec_parms_;
163 
164  void generate_code(std::string& eval, std::string& exec) {
165  std::string preamble = "local self, params, data, filter_own = ...\n";
166  std::string load = "wesnoth.require(\"" + location_ + "\")";
167  if (use_parms_) {
168  eval = preamble + "return " + load + ":evaluation(ai, {" + eval_parms_ + "}, {data = data})";
169  exec = preamble + load + ":execution(ai, {" + exec_parms_ + "}, {data = data})";
170  } else {
171  eval = preamble + "return " + load + ".evaluation(self, params, data, filter_own)";
172  exec = preamble + load + ".execution(self, params, data, filter_own)";
173  }
174  }
175 };
176 
178 public:
180  : lua_candidate_action_wrapper(context, cfg, lua_ai_ctx)
181  , bound_unit_()
182  {
183  map_location loc(cfg["unit_x"], cfg["unit_y"], wml_loc()); // lua and c++ coords differ by one
184  bound_unit_ = (*resources::gameboard->units().find(loc)).clone();
185  }
186 
187  virtual double evaluate()
188  {
189  if (resources::gameboard->units().find(bound_unit_->underlying_id()).valid())
190  {
192  }
193  else
194  {
195  this->set_to_be_removed();
196  return 0; // Is 0 what we return when we don't want the action to be executed?
197  }
198  }
199 
200  virtual void execute()
201  {
203  this->disable(); // we do not want to execute the same sticky CA twice -> will be moved out to Lua later
204  }
205 
206  virtual config to_config() const
207  {
209  cfg["sticky"] = true;
210  cfg["unit_x"] = bound_unit_->get_location().wml_x();
211  cfg["unit_y"] = bound_unit_->get_location().wml_y();
212  return cfg;
213  }
214 private:
216 
217 };
218 
219 class lua_stage_wrapper : public stage {
220 public:
221  lua_stage_wrapper( ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx )
222  : stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("args"))
223  {
224  action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx));
225  }
226 
228  {
229  }
230 
231  virtual bool do_play_stage()
232  {
233  gamestate_observer gs_o;
234 
235  if (action_handler_) {
236  lua_object_ptr nil;
237  const config empty_cfg;
238  action_handler_->handle(serialized_evaluation_state_, empty_cfg, false, nil);
239  }
240 
241  return gs_o.is_gamestate_changed();
242  }
243 
244  virtual config to_config() const
245  {
246  config cfg = stage::to_config();
247  cfg["code"] = code_;
249  return cfg;
250  }
251 private:
252  std::shared_ptr<lua_ai_action_handler> action_handler_;
253  std::string code_;
255 };
256 
257 /**
258  * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack.
259  * We inject full ai_context later.
260  */
262  : engine(context,cfg)
263  , code_(get_engine_code(cfg))
264  , lua_ai_context_(resources::lua_kernel->create_lua_ai_context(
265  get_engine_code(cfg).c_str(), this))
266 {
267  name_ = "lua";
268  config data(cfg.child_or_empty("data"));
269  config args(cfg.child_or_empty("args"));
270 
271  if (lua_ai_context_) { // The context might be nullptr if the config contains errors
272  lua_ai_context_->set_persistent_data(data);
273  lua_ai_context_->set_arguments(args);
274  lua_ai_context_->update_state();
275  }
276 }
277 
278 std::string engine_lua::get_engine_code(const config &cfg) const
279 {
280  if (cfg.has_attribute("code")) {
281  return cfg["code"].str();
282  }
283  // If there is no engine defined we create a dummy engine
284  std::string code = "wesnoth.require(\"ai/lua/dummy_engine_lua.lua\")";
285  return code;
286 }
287 
289 {
290 }
291 
292 bool engine_lua::is_ok() const
293 {
294  return lua_ai_context_ ? true : false;
295 }
296 
298 {
299  if (game_config::debug)
300  {
301  lua_ai_context_->push_ai_table();
302  }
303 }
304 
305 void engine_lua::do_parse_candidate_action_from_config( rca_context &context, const config &cfg, std::back_insert_iterator<std::vector< candidate_action_ptr > > b )
306 {
307  if (!lua_ai_context_) {
308  return;
309  }
310 
312  if (!cfg["sticky"].to_bool())
313  {
314  if (cfg.has_attribute("location")) {
316  } else {
317  ca_ptr.reset(new lua_candidate_action_wrapper(context,cfg,*lua_ai_context_));
318  }
319  }
320  else
321  {
323  }
324 
325  if (ca_ptr) {
326  *b = ca_ptr;
327  }
328 }
329 
330 void engine_lua::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b )
331 {
332  if (!lua_ai_context_) {
333  return;
334  }
335 
336  if(auto st_ptr = std::make_shared<lua_stage_wrapper>(context, cfg, *lua_ai_context_)) {
337  st_ptr->on_create();
338  *b = st_ptr;
339  }
340 }
341 
342 void engine_lua::do_parse_aspect_from_config( const config &cfg, const std::string &id, std::back_insert_iterator<std::vector< aspect_ptr > > b )
343 {
344  const std::string aspect_factory_key = id+"*lua_aspect"; // @note: factory key for a lua_aspect
346 
347  if (f == lua_aspect_factory::get_list().end()){
348  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN aspect["<<aspect_factory_key<<"]";
349  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
350  return;
351  }
352  aspect_ptr new_aspect = f->second->get_new_instance(ai_,cfg,id,lua_ai_context_);
353  if (!new_aspect) {
354  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE aspect, key=["<<aspect_factory_key<<"]";
355  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
356  return;
357  }
358  *b = new_aspect;
359 }
360 
361 void engine_lua::do_parse_goal_from_config(const config &cfg, std::back_insert_iterator<std::vector< goal_ptr > > b )
362 {
364  if (f == goal_factory::get_list().end()){
365  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN goal["<<cfg["name"]<<"]";
366  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
367  return;
368  }
369  goal_ptr new_goal = f->second->get_new_instance(ai_,cfg);
370  new_goal->on_create(lua_ai_context_);
371  if (!new_goal || !new_goal->ok()) {
372  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE goal["<<cfg["name"]<<"]";
373  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
374  return;
375  }
376  *b = new_goal;
377 }
378 
379 std::string engine_lua::evaluate(const std::string &/*str*/)
380 {
381  // TODO: this is not mandatory, but if we want to allow lua to evaluate
382  // something 'in context' of this ai, this will be useful
383  return "";
384 }
385 
387 {
388  lua_ai_context_->apply_micro_ai(cfg);
389 }
390 
392 {
393  config cfg = engine::to_config();
394 
395  cfg["id"] = get_id();
396  cfg["code"] = this->code_;
397 
398  if (lua_ai_context_) {
399  config data = config();
400  lua_ai_context_->get_persistent_data(data);
401  cfg.add_child("data") = data;
402  }
403 
404  return cfg;
405 }
406 
407 #ifdef _MSC_VER
408 #pragma warning(pop)
409 #endif
410 
411 } //end of namespace ai
virtual void set_to_be_removed()
Definition: rca.cpp:117
void disable()
Disable the candidate action.
Definition: rca.cpp:68
static const double BAD_SCORE
Definition: rca.hpp:33
virtual config to_config() const
serialize
Definition: rca.cpp:101
void apply_micro_ai(const config &cfg)
Definition: engine_lua.cpp:386
std::string get_engine_code(const config &) const
Definition: engine_lua.cpp:278
virtual void do_parse_stage_from_config(ai_context &context, const config &cfg, std::back_insert_iterator< std::vector< stage_ptr > > b)
Taka a config (with engine=lua in it) and parse several (usually, 1) stages out of it.
Definition: engine_lua.cpp:330
bool is_ok() const
Definition: engine_lua.cpp:292
virtual void do_parse_aspect_from_config(const config &cfg, const std::string &id, std::back_insert_iterator< std::vector< aspect_ptr > > b)
Taka a config (with engine=lua in it) and parse several (usually, 1) aspects out of it.
Definition: engine_lua.cpp:342
virtual void do_parse_candidate_action_from_config(rca_context &context, const config &cfg, std::back_insert_iterator< std::vector< candidate_action_ptr > > b)
Taka a config (with engine=lua in it) and parse several (usually, 1) candidate actions out of it.
Definition: engine_lua.cpp:305
virtual config to_config() const
Serialize to config.
Definition: engine_lua.cpp:391
std::shared_ptr< lua_ai_context > lua_ai_context_
Definition: engine_lua.hpp:89
virtual std::string evaluate(const std::string &str)
Definition: engine_lua.cpp:379
engine_lua(readonly_context &context, const config &cfg)
Note that initially we get access only to readonly context (engine is created rather early,...
Definition: engine_lua.cpp:261
virtual ~engine_lua()
Definition: engine_lua.cpp:288
virtual void do_parse_goal_from_config(const config &cfg, std::back_insert_iterator< std::vector< goal_ptr > > b)
Definition: engine_lua.cpp:361
std::string code_
The underlying lua code.
Definition: engine_lua.hpp:86
virtual void push_ai_table()
Method that pushes the AI table of the lua_context on the stack for debugging purposes.
Definition: engine_lua.cpp:297
std::string name_
Definition: engine.hpp:99
virtual std::string get_id() const
Definition: engine.hpp:83
virtual config to_config() const
serialize
Definition: engine.cpp:134
readonly_context & ai_
Definition: engine.hpp:93
bool is_gamestate_changed()
Check if the gamestate has changed since last reset reset is done once on construction,...
static factory_map & get_list()
Definition: goal.hpp:158
Proxy table for the AI context.
Definition: core.hpp:34
static factory_map & get_list()
Definition: aspect.hpp:499
virtual void execute()
Execute the candidate action.
Definition: engine_lua.cpp:80
lua_candidate_action_wrapper_base(rca_context &context, const config &cfg)
Definition: engine_lua.cpp:57
virtual config to_config() const
serialize
Definition: engine_lua.cpp:87
std::shared_ptr< lua_ai_action_handler > evaluation_action_handler_
Definition: engine_lua.cpp:95
std::shared_ptr< lua_ai_action_handler > execution_action_handler_
Definition: engine_lua.cpp:96
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:65
lua_candidate_action_wrapper_external(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:128
void generate_code(std::string &eval, std::string &exec)
Definition: engine_lua.cpp:164
virtual config to_config() const
serialize
Definition: engine_lua.cpp:147
virtual config to_config() const
serialize
Definition: engine_lua.cpp:113
lua_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:104
virtual config to_config() const
serialize
Definition: engine_lua.cpp:244
lua_stage_wrapper(ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:221
virtual bool do_play_stage()
Play the turn - implementation.
Definition: engine_lua.cpp:231
virtual ~lua_stage_wrapper()
Definition: engine_lua.cpp:227
config serialized_evaluation_state_
Definition: engine_lua.cpp:254
std::shared_ptr< lua_ai_action_handler > action_handler_
Definition: engine_lua.cpp:252
lua_sticky_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:179
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:187
virtual void execute()
Execute the candidate action.
Definition: engine_lua.cpp:200
virtual config to_config() const
serialize
Definition: engine_lua.cpp:206
virtual side_number get_side() const =0
Get the side number.
virtual config to_config() const
serialize
Definition: stage.cpp:65
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:395
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
config & add_child(config_key_type key)
Definition: config.cpp:441
virtual const unit_map & units() const override
Definition: game_board.hpp:106
unit_iterator find(std::size_t id)
Definition: map.cpp:302
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
#define ERR_AI_LUA
Definition: engine_lua.cpp:46
#define DBG_AI_LUA
Definition: engine_lua.cpp:43
LUA AI Support engine - creating specific ai components from config.
Interfaces for manipulating version numbers of engine, add-ons, etc.
A helper class to observe the game state.
Standard logging facilities (interface).
Lua object(value) wrapper implementation.
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
wfl::candidate_action_ptr ca_ptr
Definition: ai.cpp:64
std::shared_ptr< lua_object_base > lua_object_ptr
Definition: core.hpp:27
std::shared_ptr< aspect > aspect_ptr
Definition: game_info.hpp:95
std::shared_ptr< goal > goal_ptr
Definition: game_info.hpp:100
std::shared_ptr< candidate_action > candidate_action_ptr
Definition: rca.hpp:145
static lg::log_domain log_ai_engine_lua("ai/engine/lua")
const bool & debug
Definition: game_config.cpp:91
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
game_board * gameboard
Definition: resources.cpp:20
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::string_view data
Definition: picture.cpp:194
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
candidate action framework
Composite AI stages.
Encapsulates the map of the game.
Definition: location.hpp:38
#define f
#define b