43 #define LOG_LUA LOG_STREAM(info, log_ai_engine_lua)
44 #define WRN_LUA LOG_STREAM(warn, log_ai_engine_lua)
45 #define ERR_LUA LOG_STREAM(err, log_ai_engine_lua)
47 static char const aisKey[] =
"ai contexts";
57 lua_setfield(
L, LUA_REGISTRYINDEX,
aisKey);
62 int top = lua_gettop(
L);
64 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
65 lua_rawgeti(
L, -1,
num_);
67 lua_getfield(
L, -1,
"params");
75 int top = lua_gettop(
L);
77 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
78 lua_rawgeti(
L, -1,
num_);
81 lua_setfield(
L, -2,
"params");
88 int top = lua_gettop(
L);
90 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
91 lua_rawgeti(
L, -1,
num_);
93 lua_getfield(
L, -1,
"data");
101 int top = lua_gettop(
L);
103 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
104 lua_rawgeti(
L, -1,
num_);
107 lua_setfield(
L, -2,
"data");
115 lua_touserdata(L, lua_upvalueindex(1))));
132 lua_setfield(L, -2,
"ok");
134 lua_setfield(L, -2,
"gamestate_changed");
136 lua_setfield(L, -2,
"status");
138 lua_setfield(L, -2,
"result");
147 unit* leader =
nullptr;
148 if (lua_isuserdata(L,
index))
151 if (!leader)
return luaL_argerror(L, 1,
"unknown unit");
161 lua_pushnumber(L, res.
wml_x());
162 lua_pushnumber(L, res.
wml_y());
167 static int ai_move(lua_State *L,
bool exec,
bool remove_movement)
172 bool unreach_is_ok =
false;
173 if (lua_isboolean(L, 3)) {
187 return ai_move(L,
true,
false);
192 return ai_move(L,
false,
false);
203 int attacker_weapon = -1;
206 if (!lua_isnoneornil(L, 3)) {
207 attacker_weapon = lua_tointeger(L, 3);
208 if (attacker_weapon != -1) {
217 if (!lua_isnoneornil(L, 4) && lua_isnumber(L,4)) {
218 aggression = lua_tonumber(L, 4);
266 const char *
unit_name = luaL_checkstring(L, 1);
287 const char *unit_id = luaL_checkstring(L, 1);
319 lua_createtable(L, 0, 0);
322 lua_pushinteger(L,
i);
325 lua_createtable(L, 3, 0);
327 lua_pushstring(L,
"type");
331 lua_pushstring(L,
"loc");
335 lua_pushstring(L,
"value");
336 lua_pushnumber(L, it->value);
349 lua_pushnumber(L, aggression);
358 lua_createtable(L, attacks.size(), 0);
359 int table_index = lua_gettop(L);
361 ai::attacks_vector::const_iterator it = attacks.begin();
362 for (
int i = 1; it != attacks.end(); ++it, ++
i)
366 lua_rawseti(L, table_index,
i);
373 std::set<map_location> locs;
375 avoid.get_locations(locs);
384 lua_pushnumber(L, caution);
391 lua_pushstring(L, grouping.c_str());
398 lua_pushnumber(L, leader_aggression);
412 void visit_helper(lua_State* L,
const utils::variant<
bool, std::vector<std::string>>& input)
417 lua_pushboolean(L, v);
419 lua_createtable(L, v.size(), 0);
420 for(
const std::string& str : v) {
421 lua_pushlstring(L, str.c_str(), str.size());
422 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
439 lua_pushnumber(L, leader_value);
458 int size = recruiting.size();
459 lua_createtable(L,
size, 0);
462 lua_pushinteger(L,
i + 1);
463 lua_pushstring(L, recruiting[
i].c_str());
472 lua_pushnumber(L, scout_village_targeting);
479 lua_pushboolean(L, simple_targeting);
486 lua_pushboolean(L, support_villages);
493 lua_pushnumber(L, village_value);
500 lua_pushnumber(L, villages_per_scout);
507 int top = lua_gettop(L);
509 lua_getfield(L, -1,
"att_ptr");
521 lua_pushnumber(L, rating);
525 static void push_movements(lua_State *L,
const std::vector< std::pair < map_location, map_location > > & moves)
527 lua_createtable(L, moves.size(), 0);
529 int table_index = lua_gettop(L);
531 std::vector< std::pair < map_location, map_location > >::const_iterator move = moves.begin();
533 for (
int i = 1; move != moves.end(); ++move, ++
i)
535 lua_createtable(L, 2, 0);
537 lua_pushstring(L,
"src");
541 lua_pushstring(L,
"dst");
545 lua_rawseti(L, table_index,
i);
555 lua_pushstring(L,
"att_ptr");
560 lua_pushstring(L,
"rating");
565 lua_pushstring(L,
"movements");
569 lua_pushstring(L,
"target");
573 lua_pushstring(L,
"target_value");
577 lua_pushstring(L,
"avg_losses");
581 lua_pushstring(L,
"chance_to_kill");
585 lua_pushstring(L,
"avg_damage_inflicted");
589 lua_pushstring(L,
"target_starting_damage");
593 lua_pushstring(L,
"avg_damage_taken");
597 lua_pushstring(L,
"resources_used");
601 lua_pushstring(L,
"terrain_quality");
605 lua_pushstring(L,
"alternative_terrain_quality");
609 lua_pushstring(L,
"vulnerability");
613 lua_pushstring(L,
"support");
617 lua_pushstring(L,
"leader_threat");
621 lua_pushstring(L,
"uses_leader");
625 lua_pushstring(L,
"is_surrounded");
632 lua_createtable(L, 0, 0);
639 move_map::const_iterator it = m.begin();
648 lua_pushinteger(L, lhash(key));
650 lua_createtable(L, 0, 0);
652 while (key == it->first) {
655 lua_rawseti(L, -2,
index);
666 }
while (it != m.end());
704 lua_pushboolean(L, valid);
711 lua_pushboolean(L, valid);
718 lua_pushboolean(L, valid);
725 lua_pushboolean(L, valid);
744 return std::dynamic_pointer_cast<typesafe_aspect<T> >(
p).
get();
750 aspect_map::const_iterator iter = aspects.find(luaL_checkstring(L, 2));
751 if(iter == aspects.end()) {
758 aspect_attacks_base* real_aspect =
dynamic_cast<aspect_attacks_base*
>(aspect_as_attacks_vector);
759 while(real_aspect ==
nullptr) {
763 real_aspect =
dynamic_cast<aspect_attacks_base*
>(aspect_as_attacks_vector);
766 std::vector<unit_const_ptr> attackers, enemies;
771 if(u->side() == my_side && real_aspect->is_allowed_attacker(*u)) {
772 attackers.push_back(u.get_shared_ptr());
773 }
else if(u->side() != my_side && real_aspect->is_allowed_enemy(*u)) {
774 enemies.push_back(u.get_shared_ptr());
777 lua_createtable(L, 0, 2);
778 lua_createtable(L, attackers.size(), 0);
779 for(
size_t i = 0;
i < attackers.size();
i++) {
781 lua_rawseti(L, -2,
i + 1);
783 lua_setfield(L, -2,
"own");
784 lua_createtable(L, enemies.size(), 0);
785 for(
size_t i = 0;
i < enemies.size();
i++) {
787 lua_rawseti(L, -2,
i + 1);
789 lua_setfield(L, -2,
"enemy");
796 if (!u.valid() || u->side() != my_side) {
799 lua_pushinteger(L, lhash(u->get_location()));
804 iter->second->get_lua(L);
812 std::vector<std::string> aspect_names;
813 std::transform(aspects.begin(), aspects.end(), std::back_inserter(aspect_names), std::mem_fn(&aspect_map::value_type::first));
820 lua_pushstring(L,
"attempted to write to the ai.aspects table, which is read-only");
839 if(!lua_isstring(L,2)) {
843 std::string m = lua_tostring(L,2);
851 lua_pushlightuserdata(L, &
engine);
853 lua_setfield(L, -2,
"__index");
855 lua_setfield(L, -2,
"__newindex");
856 lua_pushlightuserdata(L, &
engine);
858 lua_setfield(L, -2,
"__dir");
859 lua_setmetatable(L, -2);
862 lua_pushstring(L,
"read_only");
871 lua_pushlightuserdata(L, &
engine);
872 lua_pushcclosure(L,
p->func, 1);
881 auto callbacks = lua_check<std::vector<std::string>>(L, 2);
882 callbacks.push_back(
"side");
883 callbacks.push_back(
"aspects");
886 callbacks.push_back(
c->name);
896 static luaL_Reg
const callbacks[] = {
940 for (
const luaL_Reg*
p = callbacks;
p->name; ++
p) {
941 lua_pushlightuserdata(L,
engine);
942 lua_pushcclosure(L,
p->func, 1);
943 lua_pushstring(L,
p->name);
944 lua_pushvalue(L, -2);
949 lua_pushlightuserdata(L,
engine);
951 lua_setfield(L, -2,
"__index");
953 lua_setfield(L, -2,
"__dir");
954 lua_setmetatable(L, -2);
960 lua_getfield(L, LUA_REGISTRYINDEX,
aisKey);
961 size_t length_ai = lua_rawlen(L, -1);
964 lua_setfield(L, -2,
"ai");
965 lua_pushvalue(L, -1);
966 lua_rawseti(L, -3, length_ai + 1);
968 return length_ai + 1;
980 int res_ai = luaL_loadbufferx(
L, code, strlen(code), code,
"t");
984 char const *m = lua_tostring(
L, -1);
985 ERR_LUA <<
"error while initializing ai: " <<m;
991 lua_pushvalue(
L, -2);
992 lua_setfield(
L, -2,
"update_self");
993 lua_pushlightuserdata(
L,
engine);
994 lua_setfield(
L, -2,
"engine");
1004 lua_getfield(
L, -1,
"update_self");
1005 lua_getfield(
L, -2,
"params");
1006 lua_getfield(
L, -3,
"data");
1014 lua_setfield(
L, -2,
"self");
1022 int res = luaL_loadbufferx(
L, code, strlen(code), code,
"t");
1025 char const *m = lua_tostring(
L, -1);
1026 ERR_LUA <<
"error while creating ai function: " <<m;
1032 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1034 size_t length = lua_rawlen(
L, -1);
1035 lua_pushvalue(
L, -2);
1036 lua_rawseti(
L, -2, length + 1);
1048 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1049 lua_rawgeti(
L, -1, ctx.
num_);
1053 lua_getglobal(
L,
"ai");
1054 if(!lua_isnoneornil(
L, -1)) {
1056 lua_getfield(
L, -1,
"read_only");
1060 lua_pushstring(
L,
"read_only");
1061 lua_pushboolean(
L, read_only);
1067 lua_getfield(
L, -1,
"ai");
1068 lua_pushstring(
L,
"read_only");
1069 lua_pushboolean(
L, read_only);
1071 lua_setglobal(
L,
"ai");
1081 lua_setglobal(
L,
"ai");
1084 lua_getglobal(
L,
"ai");
1085 lua_pushstring(
L,
"read_only");
1095 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1097 lua_rawseti(
L, -2,
num_);
1103 int initial_top = lua_gettop(
L);
1109 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1110 lua_rawgeti(
L, -1,
num_);
1114 int iState = lua_absindex(
L, -2);
1115 lua_getfield(
L, iState,
"self");
1117 lua_getfield(
L, iState,
"data");
1120 if (!filter_own.
empty()) {
1128 l_obj->store(
L, -1);
1131 lua_settop(
L, initial_top);
1137 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1139 lua_rawseti(
L, -2,
num_);
Managing the AI-Game interaction - AI actions and their results.
bool is_gamestate_changed() const
static recall_result_ptr execute_recall_action(side_number side, bool execute, const std::string &unit_id, const map_location &where, const map_location &from)
Ask the game to recall a unit for us on specified location.
static const std::string & get_error_name(int error_code)
get human-readable name of the error by code.
static attack_result_ptr execute_attack_action(side_number side, bool execute, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression)
Ask the game to attack an enemy defender using our unit attacker from attackers current location,...
static move_result_ptr execute_move_action(side_number side, bool execute, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok=false)
Ask the game to move our unit from location 'from' to location 'to', optionally - doing a partial mov...
static recruit_result_ptr execute_recruit_action(side_number side, bool execute, const std::string &unit_name, const map_location &where, const map_location &from)
Ask the game to recruit a unit for us on specified location.
static stopunit_result_ptr execute_stopunit_action(side_number side, bool execute, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Ask the game to remove unit movements and/or attack.
std::vector< std::pair< map_location, map_location > > movements
bool uses_leader
Is true if this attack sequence makes use of the leader.
double target_value
The value of the unit being targeted.
double avg_damage_inflicted
The average hitpoints damage inflicted.
double chance_to_kill
Estimated % chance to kill the unit.
double terrain_quality
The weighted average of the % chance to hit each attacking unit.
double avg_damage_taken
The average hitpoints damage taken.
double alternative_terrain_quality
The weighted average of the % defense of the best possible terrain that the attacking units could rea...
bool leader_threat
Is true if the unit is a threat to our leader.
double avg_losses
The value on average, of units lost in the combat.
double vulnerability
The vulnerability is the power projection of enemy units onto the hex we're standing on.
double resources_used
The sum of the values of units used in the attack.
double rating(double aggression, const readonly_context &ai_obj) const
int target_starting_damage
bool is_surrounded
Is true if the units involved in this attack sequence are surrounded.
virtual std::vector< target > find_targets(const move_map &enemy_dstsrc)=0
virtual ai_context_ptr get_ai_context()
readonly_context & get_readonly_context()
Proxy class for calling AI action handlers defined in Lua.
lua_ai_context & context_
lua_ai_action_handler(lua_State *l, lua_ai_context &context, int num)
void handle(const config &cfg, const config &filter_own, bool read_only, lua_object_ptr l_obj)
static lua_ai_action_handler * create(lua_State *L, char const *code, lua_ai_context &context)
Proxy table for the AI context.
void set_persistent_data(const config &)
void get_persistent_data(config &) const
lua_ai_context(lua_State *l, int num, int side)
static void init(lua_State *L)
static lua_ai_context * create(lua_State *L, char const *code, engine_lua *engine)
void set_arguments(const config &)
void get_arguments(config &) const
void apply_micro_ai(const config &cfg)
lua_ai_load(lua_ai_context &ctx, bool read_only)
virtual int get_villages_per_scout() const =0
virtual std::string get_grouping() const =0
virtual const terrain_filter & get_avoid() const =0
virtual bool get_simple_targeting() const =0
virtual void recalculate_move_maps() const =0
virtual const aspect_map & get_aspects() const =0
virtual const attacks_vector & get_attacks() const =0
virtual void set_dst_src_valid_lua()=0
virtual const move_map & get_enemy_srcdst() const =0
virtual const move_map & get_enemy_dstsrc() const =0
virtual bool is_src_dst_enemy_valid_lua() const =0
virtual config get_leader_goal() const =0
virtual double get_aggression() const =0
virtual bool is_src_dst_valid_lua() const =0
virtual bool is_dst_src_enemy_valid_lua() const =0
virtual const team & current_team() const =0
virtual const map_location & suitable_keep(const map_location &leader_location, const pathfind::paths &leader_paths) const =0
get most suitable keep for leader - nearest free that can be reached in 1 turn, if none - return near...
virtual void set_src_dst_enemy_valid_lua()=0
virtual const move_map & get_srcdst() const =0
virtual void set_dst_src_enemy_valid_lua()=0
virtual double get_caution() const =0
virtual void recalculate_move_maps_enemy() const =0
virtual bool is_dst_src_valid_lua() const =0
virtual const std::vector< std::string > get_recruitment_pattern() const =0
virtual double get_scout_village_targeting() const =0
virtual double get_leader_aggression() const =0
virtual bool get_support_villages() const =0
virtual const move_map & get_dstsrc() const =0
virtual void set_src_dst_valid_lua()=0
virtual double get_village_value() const =0
virtual double get_leader_value() const =0
virtual side_number get_side() const =0
Get the side number.
const std::vector< std::string > get_advancements(const unit_map::const_iterator &unit) const
A config object defines a single node in a WML file, with access to child nodes.
virtual const unit_map & units() const override
This class represents a single unit of a specific type.
static lg::log_domain log_ai_engine_lua("ai/engine/lua")
static char const aisKey[]
LUA AI Support engine - creating specific ai components from config.
const map_location & get_location() const
The current map location this unit is at.
Standard logging facilities (interface).
void luaW_pushconfig(lua_State *L, const config &cfg)
Converts a config object to a Lua table pushed at the top of the stack.
void luaW_pushlocation(lua_State *L, const map_location &ml)
Converts a map location object to a Lua table pushed at the top of the stack.
bool luaW_toboolean(lua_State *L, int n)
int luaW_type_error(lua_State *L, int narg, const char *tname)
bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
Calls a Lua function stored below its nArgs arguments at the top of the stack.
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
int luaW_push_locationset(lua_State *L, const std::set< map_location > &locs)
Converts a set of map locations to a Lua table pushed at the top of the stack.
bool luaW_tolocation(lua_State *L, int index, map_location &loc)
Converts an optional table or pair of integers to a map location object.
map_location luaW_checklocation(lua_State *L, int index)
Converts an optional table or pair of integers to a map location object.
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
Lua object(value) wrapper implementation.
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
lua_unit * luaW_pushunit(lua_State *L, Args... args)
A small explanation about what's going on here: Each action has access to two game_info objects First...
static int cfun_ai_is_src_dst_valid(lua_State *L)
static int cfun_ai_get_leader_value(lua_State *L)
static int cfun_ai_get_simple_targeting(lua_State *L)
static int cfun_ai_execute_stopunit_all(lua_State *L)
std::vector< attack_analysis > attacks_vector
std::shared_ptr< recruit_result > recruit_result_ptr
static int ai_recruit(lua_State *L, bool exec)
static int impl_ai_aspect_set(lua_State *L)
static int cfun_ai_get_recruitment_pattern(lua_State *L)
typesafe_aspect< T > * try_aspect_as(aspect_ptr p)
static ai::engine_lua & get_engine(lua_State *L)
static int cfun_ai_get_villages_per_scout(lua_State *L)
static int cfun_ai_execute_stopunit_moves(lua_State *L)
static int impl_ai_list(lua_State *L)
static int transform_ai_action(lua_State *L, ai::action_result_ptr action_result)
static int cfun_ai_get_targets(lua_State *L)
static int ai_move(lua_State *L, bool exec, bool remove_movement)
static size_t generate_and_push_ai_state(lua_State *L, ai::engine_lua *engine)
static void generate_and_push_ai_table(lua_State *L, ai::engine_lua *engine)
static int cfun_ai_fallback_human(lua_State *)
static int cfun_ai_recalculate_move_maps_enemy(lua_State *L)
static int cfun_ai_get_enemy_srcdst(lua_State *L)
static int cfun_attack_rating(lua_State *L)
static int cfun_ai_check_attack(lua_State *L)
static int cfun_ai_is_dst_src_enemy_valid(lua_State *L)
static int cfun_ai_is_dst_src_valid(lua_State *L)
std::shared_ptr< lua_object_base > lua_object_ptr
std::shared_ptr< aspect > aspect_ptr
static int cfun_ai_get_avoid(lua_State *L)
std::shared_ptr< attack_result > attack_result_ptr
static int cfun_ai_get_scout_village_targeting(lua_State *L)
static int cfun_ai_check_stopunit(lua_State *L)
static int cfun_ai_execute_attack(lua_State *L)
static int cfun_ai_get_leader_goal(lua_State *L)
static void push_attack_analysis(lua_State *L, const attack_analysis &)
std::shared_ptr< stopunit_result > stopunit_result_ptr
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
static int cfun_ai_execute_recall(lua_State *L)
static int cfun_ai_check_move(lua_State *L)
static int cfun_ai_execute_stopunit_attacks(lua_State *L)
static int cfun_ai_get_caution(lua_State *L)
static int cfun_ai_execute_recruit(lua_State *L)
static int cfun_ai_execute_move_partial(lua_State *L)
static int ai_recall(lua_State *L, bool exec)
static int cfun_ai_get_enemy_dstsrc(lua_State *L)
static void push_move_map(lua_State *L, const move_map &m)
static int impl_ai_aspect_list(lua_State *L)
static int impl_ai_aspect_get(lua_State *L)
static int cfun_ai_check_recruit(lua_State *L)
static int ai_attack(lua_State *L, bool exec)
static int cfun_ai_get_leader_ignores_keep(lua_State *L)
static int cfun_ai_get_leader_aggression(lua_State *L)
static int cfun_ai_get_passive_leader_shares_keep(lua_State *L)
static int cfun_ai_is_src_dst_enemy_valid(lua_State *L)
static int cfun_ai_recalculate_move_maps(lua_State *L)
static int impl_ai_get(lua_State *L)
static int cfun_ai_get_passive_leader(lua_State *L)
static int cfun_ai_get_dstsrc(lua_State *L)
static int cfun_ai_get_suitable_keep(lua_State *L)
static int cfun_ai_get_aggression(lua_State *L)
static int cfun_ai_execute_move_full(lua_State *L)
std::shared_ptr< move_result > move_result_ptr
static int ai_stopunit_select(lua_State *L, bool exec, bool remove_movement, bool remove_attacks)
static void push_movements(lua_State *L, const std::vector< std::pair< map_location, map_location > > &moves)
static int cfun_ai_get_attacks(lua_State *L)
std::shared_ptr< action_result > action_result_ptr
static int cfun_ai_check_recall(lua_State *L)
static int cfun_ai_get_srcdst(lua_State *L)
std::shared_ptr< recall_result > recall_result_ptr
static int cfun_ai_get_grouping(lua_State *L)
std::map< std::string, aspect_ptr > aspect_map
static int cfun_ai_get_support_villages(lua_State *L)
static ai::readonly_context & get_readonly_context(lua_State *L)
static int cfun_ai_get_village_value(lua_State *L)
static luaL_Reg const mutating_callbacks[]
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
constexpr bool decayed_is_same
Equivalent to as std::is_same_v except both types are passed through std::decay first.
std::string::const_iterator iterator
This module contains various pathfinding functions and utilities.
void lua_push(lua_State *L, const T &val)
std::decay_t< T > luaW_table_get_def(lua_State *L, int index, std::string_view k, const T &def)
returns t[k] where k is the table at index index and k is k or def if it is not convertible to the co...
static config unit_name(const unit *u)
Encapsulates the map of the game.
static const map_location & null_location()
Object which contains all the possible locations a unit can move to, with associated best routes to t...
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.