The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
engine_lua.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 2025
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"
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_config.hpp"
34#include "game_board.hpp"
36#include "units/unit.hpp"
37#include "units/map.hpp"
38#include "deprecation.hpp"
39#include "game_version.hpp"
40
41namespace ai {
42
43static lg::log_domain log_ai_engine_lua("ai/engine/lua");
44#define DBG_AI_LUA LOG_STREAM(debug, log_ai_engine_lua)
45#define LOG_AI_LUA LOG_STREAM(info, log_ai_engine_lua)
46#define WRN_AI_LUA LOG_STREAM(warn, log_ai_engine_lua)
47#define ERR_AI_LUA LOG_STREAM(err, log_ai_engine_lua)
48
49#ifdef _MSC_VER
50#pragma warning(push)
51//silence "inherits via dominance" warnings
52#pragma warning(disable:4250)
53#endif
54
56
57public:
60 {
61 // do nothing
62 }
63
65
66 virtual double evaluate()
67 {
68 auto l_obj = std::make_shared<lua_object<double>>();
69
72 } else {
73 return BAD_SCORE;
74 }
75
76 std::shared_ptr<double> result = l_obj->get();
77
78 return result ? *result : 0.0;
79 }
80
87
94
95protected:
96 std::shared_ptr<lua_ai_action_handler> evaluation_action_handler_;
97 std::shared_ptr<lua_ai_action_handler> execution_action_handler_;
100};
101
103
104public:
106 : lua_candidate_action_wrapper_base(context,cfg),evaluation_(cfg["evaluation"]),execution_(cfg["execution"])
107 {
108 evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(evaluation_.c_str(),lua_ai_ctx));
109 execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(execution_.c_str(),lua_ai_ctx));
110 }
111
113
114 virtual config to_config() const
115 {
117 cfg["evaluation"] = evaluation_;
118 cfg["execution"] = execution_;
119 return cfg;
120 }
121
122private:
123 std::string evaluation_;
124 std::string execution_;
125};
126
128public:
130 : lua_candidate_action_wrapper_base(context,cfg), location_(cfg["location"]), use_parms_(false)
131 {
132 if (cfg.has_attribute("exec_parms") || cfg.has_attribute("eval_parms")) {
133 deprecated_message("[candidate_action]eval_parms,exec_parms=", DEP_LEVEL::FOR_REMOVAL, "1.21", "Use [args] instead - this data is passed to both the evaluation and the execution");
134 use_parms_ = true;
135 exec_parms_ = cfg["exec_parms"].str();
136 eval_parms_ = cfg["eval_parms"].str();
137 }
138 std::string eval_code;
139 std::string exec_code;
140 generate_code(eval_code, exec_code);
141
142 evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(eval_code.c_str(),lua_ai_ctx));
143 execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(exec_code.c_str(),lua_ai_ctx));
144 }
145
147
148 virtual config to_config() const
149 {
151 cfg["location"] = location_;
152 if (use_parms_) {
153 cfg["eval_parms"] = eval_parms_;
154 cfg["exec_parms"] = exec_parms_;
155 }
156 return cfg;
157 }
158
159private:
160 std::string location_;
161 std::string eval_parms_;
162 std::string exec_parms_;
164
165 void generate_code(std::string& eval, std::string& exec) {
166 std::string preamble = "local self, params, data, filter_own = ...\n";
167 std::string load = "wesnoth.require(\"" + location_ + "\")";
168 if (use_parms_) {
169 eval = preamble + "return " + load + ":evaluation(ai, {" + eval_parms_ + "}, {data = data})";
170 exec = preamble + load + ":execution(ai, {" + exec_parms_ + "}, {data = data})";
171 } else {
172 eval = preamble + "return " + load + ".evaluation(self, params, data, filter_own)";
173 exec = preamble + load + ".execution(self, params, data, filter_own)";
174 }
175 }
176};
177
179public:
181 : lua_candidate_action_wrapper(context, cfg, lua_ai_ctx)
182 , bound_unit_()
183 {
184 map_location loc(cfg["unit_x"], cfg["unit_y"], wml_loc()); // lua and c++ coords differ by one
186 }
187
188 virtual double evaluate()
189 {
190 if (resources::gameboard->units().find(bound_unit_->underlying_id()).valid())
191 {
193 }
194 else
195 {
196 this->set_to_be_removed();
197 return 0; // Is 0 what we return when we don't want the action to be executed?
198 }
199 }
200
201 virtual void execute()
202 {
204 this->disable(); // we do not want to execute the same sticky CA twice -> will be moved out to Lua later
205 }
206
207 virtual config to_config() const
208 {
210 cfg["sticky"] = true;
211 cfg["unit_x"] = bound_unit_->get_location().wml_x();
212 cfg["unit_y"] = bound_unit_->get_location().wml_y();
213 return cfg;
214 }
215private:
217
218};
219
220class lua_stage_wrapper : public stage {
221public:
222 lua_stage_wrapper( ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx )
223 : stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("args"))
224 {
225 action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx));
226 }
227
229 {
230 }
231
232 virtual bool do_play_stage()
233 {
235
236 if (action_handler_) {
237 lua_object_ptr nil;
238 const config empty_cfg;
239 action_handler_->handle(serialized_evaluation_state_, empty_cfg, false, nil);
240 }
241
242 return gs_o.is_gamestate_changed();
243 }
244
245 virtual config to_config() const
246 {
248 cfg["code"] = code_;
250 return cfg;
251 }
252private:
253 std::shared_ptr<lua_ai_action_handler> action_handler_;
254 std::string code_;
256};
257
258/**
259 * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack.
260 * We inject full ai_context later.
261 */
263 : engine(context,cfg)
264 , code_(get_engine_code(cfg))
265 , lua_ai_context_(resources::lua_kernel->create_lua_ai_context(
266 get_engine_code(cfg).c_str(), this))
267{
268 name_ = "lua";
269 config data(cfg.child_or_empty("data"));
270 config args(cfg.child_or_empty("args"));
271
272 if (lua_ai_context_) { // The context might be nullptr if the config contains errors
273 lua_ai_context_->set_persistent_data(data);
274 lua_ai_context_->set_arguments(args);
275 lua_ai_context_->update_state();
276 }
277}
278
279std::string engine_lua::get_engine_code(const config &cfg) const
280{
281 if (cfg.has_attribute("code")) {
282 return cfg["code"].str();
283 }
284 // If there is no engine defined we create a dummy engine
285 std::string code = "wesnoth.require(\"ai/lua/dummy_engine_lua.lua\")";
286 return code;
287}
288
292
294{
295 return lua_ai_context_ ? true : false;
296}
297
299{
301 {
302 lua_ai_context_->push_ai_table();
303 }
304}
305
306void engine_lua::do_parse_candidate_action_from_config( rca_context &context, const config &cfg, std::back_insert_iterator<std::vector< candidate_action_ptr > > b )
307{
308 if (!lua_ai_context_) {
309 return;
310 }
311
313 if (!cfg["sticky"].to_bool())
314 {
315 if (cfg.has_attribute("location")) {
317 } else {
318 ca_ptr.reset(new lua_candidate_action_wrapper(context,cfg,*lua_ai_context_));
319 }
320 }
321 else
322 {
323 ca_ptr.reset(new lua_sticky_candidate_action_wrapper(context,cfg,*lua_ai_context_));
324 }
325
326 if (ca_ptr) {
327 *b = ca_ptr;
328 }
329}
330
331void engine_lua::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b )
332{
333 if (!lua_ai_context_) {
334 return;
335 }
336
337 if(auto st_ptr = std::make_shared<lua_stage_wrapper>(context, cfg, *lua_ai_context_)) {
338 st_ptr->on_create();
339 *b = st_ptr;
340 }
341}
342
343void engine_lua::do_parse_aspect_from_config( const config &cfg, const std::string &id, std::back_insert_iterator<std::vector< aspect_ptr > > b )
344{
345 const std::string aspect_factory_key = id+"*lua_aspect"; // @note: factory key for a lua_aspect
346 lua_aspect_factory::factory_map::iterator f = lua_aspect_factory::get_list().find(aspect_factory_key);
347
348 if (f == lua_aspect_factory::get_list().end()){
349 ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN aspect["<<aspect_factory_key<<"]";
350 DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
351 return;
352 }
353 aspect_ptr new_aspect = f->second->get_new_instance(ai_,cfg,id,lua_ai_context_);
354 if (!new_aspect) {
355 ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE aspect, key=["<<aspect_factory_key<<"]";
356 DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
357 return;
358 }
359 *b = new_aspect;
360}
361
362void engine_lua::do_parse_goal_from_config(const config &cfg, std::back_insert_iterator<std::vector< goal_ptr > > b )
363{
364 goal_factory::factory_map::iterator f = goal_factory::get_list().find(cfg["name"]);
365 if (f == goal_factory::get_list().end()){
366 ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN goal["<<cfg["name"]<<"]";
367 DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
368 return;
369 }
370 goal_ptr new_goal = f->second->get_new_instance(ai_,cfg);
371 new_goal->on_create(lua_ai_context_);
372 if (!new_goal || !new_goal->ok()) {
373 ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE goal["<<cfg["name"]<<"]";
374 DBG_AI_LUA << "config snippet contains: " << std::endl << cfg;
375 return;
376 }
377 *b = new_goal;
378}
379
381{
382 lua_ai_context_->apply_micro_ai(cfg);
383}
384
386{
388
389 cfg["id"] = get_id();
390 cfg["code"] = this->code_;
391
392 if (lua_ai_context_) {
393 config data = config();
394 lua_ai_context_->get_persistent_data(data);
395 cfg.add_child("data") = data;
396 }
397
398 return cfg;
399}
400
401#ifdef _MSC_VER
402#pragma warning(pop)
403#endif
404
405} //end of namespace ai
map_location loc
Definition move.cpp:172
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)
std::string get_engine_code(const config &) const
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.
bool is_ok() const
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.
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.
virtual config to_config() const
Serialize to config.
std::shared_ptr< lua_ai_context > lua_ai_context_
engine_lua(readonly_context &context, const config &cfg)
Note that initially we get access only to readonly context (engine is created rather early,...
virtual ~engine_lua()
virtual void do_parse_goal_from_config(const config &cfg, std::back_insert_iterator< std::vector< goal_ptr > > b)
std::string code_
The underlying lua code.
virtual void push_ai_table()
Method that pushes the AI table of the lua_context on the stack for debugging purposes.
std::string name_
Definition engine.hpp:96
virtual std::string get_id() const
Definition engine.hpp:80
virtual config to_config() const
serialize
Definition engine.cpp:129
readonly_context & ai_
Definition engine.hpp:90
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:462
virtual void execute()
Execute the candidate action.
lua_candidate_action_wrapper_base(rca_context &context, const config &cfg)
virtual config to_config() const
serialize
std::shared_ptr< lua_ai_action_handler > evaluation_action_handler_
std::shared_ptr< lua_ai_action_handler > execution_action_handler_
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
lua_candidate_action_wrapper_external(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
void generate_code(std::string &eval, std::string &exec)
virtual config to_config() const
serialize
virtual config to_config() const
serialize
lua_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
virtual config to_config() const
serialize
lua_stage_wrapper(ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
virtual bool do_play_stage()
Play the turn - implementation.
config serialized_evaluation_state_
std::shared_ptr< lua_ai_action_handler > action_handler_
lua_sticky_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
virtual void execute()
Execute the candidate action.
virtual config to_config() const
serialize
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:157
config & add_child(std::string_view key)
Definition config.cpp:436
bool has_attribute(std::string_view key) const
Definition config.cpp:157
const config & child_or_empty(std::string_view key) const
Returns the first child with the given key, or an empty config if there is none.
Definition config.cpp:390
virtual const unit_map & units() const override
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)
#define ERR_AI_LUA
#define DBG_AI_LUA
LUA AI Support engine - creating specific ai components from config.
const config * cfg
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
std::shared_ptr< lua_object_base > lua_object_ptr
Definition core.hpp:28
std::shared_ptr< aspect > aspect_ptr
Definition game_info.hpp:97
std::shared_ptr< goal > goal_ptr
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
game_board * gameboard
Definition resources.cpp:20
game_lua_kernel * lua_kernel
Definition resources.cpp:25
std::string_view data
Definition picture.cpp:188
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:46
#define f
#define b