50 #define LOG_LUA LOG_STREAM(info, log_ai_engine_lua)
51 #define WRN_LUA LOG_STREAM(warn, log_ai_engine_lua)
52 #define ERR_LUA LOG_STREAM(err, log_ai_engine_lua)
54 static char const aisKey[] =
"ai contexts";
64 lua_setfield(
L, LUA_REGISTRYINDEX,
aisKey);
69 int top = lua_gettop(
L);
71 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
72 lua_rawgeti(
L, -1,
num_);
74 lua_getfield(
L, -1,
"params");
82 int top = lua_gettop(
L);
84 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
85 lua_rawgeti(
L, -1,
num_);
88 lua_setfield(
L, -2,
"params");
95 int top = lua_gettop(
L);
97 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
98 lua_rawgeti(
L, -1,
num_);
100 lua_getfield(
L, -1,
"data");
108 int top = lua_gettop(
L);
110 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
111 lua_rawgeti(
L, -1,
num_);
114 lua_setfield(
L, -2,
"data");
122 lua_touserdata(L, lua_upvalueindex(1))));
139 lua_setfield(L, -2,
"ok");
141 lua_setfield(L, -2,
"gamestate_changed");
143 lua_setfield(L, -2,
"status");
145 lua_setfield(L, -2,
"result");
154 unit* leader =
nullptr;
155 if (lua_isuserdata(L,
index))
158 if (!leader)
return luaL_argerror(L, 1,
"unknown unit");
168 lua_pushnumber(L, res.
wml_x());
169 lua_pushnumber(L, res.
wml_y());
174 static int ai_move(lua_State *L,
bool exec,
bool remove_movement)
179 bool unreach_is_ok =
false;
180 if (lua_isboolean(L, 3)) {
194 return ai_move(L,
true,
false);
199 return ai_move(L,
false,
false);
210 int attacker_weapon = -1;
213 if (!lua_isnoneornil(L, 3)) {
214 attacker_weapon = lua_tointeger(L, 3);
215 if (attacker_weapon != -1) {
224 if (!lua_isnoneornil(L, 4) && lua_isnumber(L,4)) {
225 aggression = lua_tonumber(L, 4);
273 const char *
unit_name = luaL_checkstring(L, 1);
294 const char *unit_id = luaL_checkstring(L, 1);
326 lua_createtable(L, 0, 0);
329 lua_pushinteger(L,
i);
332 lua_createtable(L, 3, 0);
334 lua_pushstring(L,
"type");
338 lua_pushstring(L,
"loc");
342 lua_pushstring(L,
"value");
343 lua_pushnumber(L, it->value);
356 lua_pushnumber(L, aggression);
365 lua_createtable(L, attacks.size(), 0);
366 int table_index = lua_gettop(L);
368 ai::attacks_vector::const_iterator it = attacks.begin();
369 for (
int i = 1; it != attacks.end(); ++it, ++
i)
373 lua_rawseti(L, table_index,
i);
380 std::set<map_location> locs;
382 avoid.get_locations(locs);
391 lua_pushnumber(L, caution);
398 lua_pushstring(L, grouping.c_str());
405 lua_pushnumber(L, leader_aggression);
419 void visit_helper(lua_State* L,
const utils::variant<
bool, std::vector<std::string>>& input)
424 lua_pushboolean(L, v);
426 lua_createtable(L, v.size(), 0);
427 for(
const std::string& str : v) {
428 lua_pushlstring(L, str.c_str(), str.size());
429 lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
446 lua_pushnumber(L, leader_value);
465 int size = recruiting.size();
466 lua_createtable(L,
size, 0);
469 lua_pushinteger(L,
i + 1);
470 lua_pushstring(L, recruiting[
i].c_str());
479 lua_pushnumber(L, scout_village_targeting);
486 lua_pushboolean(L, simple_targeting);
493 lua_pushboolean(L, support_villages);
500 lua_pushnumber(L, village_value);
507 lua_pushnumber(L, villages_per_scout);
514 int top = lua_gettop(L);
516 lua_getfield(L, -1,
"att_ptr");
528 lua_pushnumber(L, rating);
532 static void push_movements(lua_State *L,
const std::vector< std::pair < map_location, map_location > > & moves)
534 lua_createtable(L, moves.size(), 0);
536 int table_index = lua_gettop(L);
538 std::vector< std::pair < map_location, map_location > >::const_iterator move = moves.begin();
540 for (
int i = 1; move != moves.end(); ++move, ++
i)
542 lua_createtable(L, 2, 0);
544 lua_pushstring(L,
"src");
548 lua_pushstring(L,
"dst");
552 lua_rawseti(L, table_index,
i);
562 lua_pushstring(L,
"att_ptr");
567 lua_pushstring(L,
"rating");
572 lua_pushstring(L,
"movements");
576 lua_pushstring(L,
"target");
580 lua_pushstring(L,
"target_value");
584 lua_pushstring(L,
"avg_losses");
588 lua_pushstring(L,
"chance_to_kill");
592 lua_pushstring(L,
"avg_damage_inflicted");
596 lua_pushstring(L,
"target_starting_damage");
600 lua_pushstring(L,
"avg_damage_taken");
604 lua_pushstring(L,
"resources_used");
608 lua_pushstring(L,
"terrain_quality");
612 lua_pushstring(L,
"alternative_terrain_quality");
616 lua_pushstring(L,
"vulnerability");
620 lua_pushstring(L,
"support");
624 lua_pushstring(L,
"leader_threat");
628 lua_pushstring(L,
"uses_leader");
632 lua_pushstring(L,
"is_surrounded");
639 lua_createtable(L, 0, 0);
646 move_map::const_iterator it = m.begin();
655 lua_pushinteger(L, lhash(key));
657 lua_createtable(L, 0, 0);
659 while (key == it->first) {
662 lua_rawseti(L, -2,
index);
673 }
while (it != m.end());
711 lua_pushboolean(L, valid);
718 lua_pushboolean(L, valid);
725 lua_pushboolean(L, valid);
732 lua_pushboolean(L, valid);
751 return std::dynamic_pointer_cast<typesafe_aspect<T> >(
p).
get();
757 aspect_map::const_iterator iter = aspects.find(luaL_checkstring(L, 2));
758 if(iter == aspects.end()) {
765 aspect_attacks_base* real_aspect =
dynamic_cast<aspect_attacks_base*
>(aspect_as_attacks_vector);
766 while(real_aspect ==
nullptr) {
770 real_aspect =
dynamic_cast<aspect_attacks_base*
>(aspect_as_attacks_vector);
773 std::vector<unit_const_ptr> attackers, enemies;
778 if(u->side() == my_side && real_aspect->is_allowed_attacker(*u)) {
779 attackers.push_back(u.get_shared_ptr());
780 }
else if(u->side() != my_side && real_aspect->is_allowed_enemy(*u)) {
781 enemies.push_back(u.get_shared_ptr());
784 lua_createtable(L, 0, 2);
785 lua_createtable(L, attackers.size(), 0);
786 for(
size_t i = 0;
i < attackers.size();
i++) {
788 lua_rawseti(L, -2,
i + 1);
790 lua_setfield(L, -2,
"own");
791 lua_createtable(L, enemies.size(), 0);
792 for(
size_t i = 0;
i < enemies.size();
i++) {
794 lua_rawseti(L, -2,
i + 1);
796 lua_setfield(L, -2,
"enemy");
803 if (!u.valid() || u->side() != my_side) {
806 lua_pushinteger(L, lhash(u->get_location()));
811 iter->second->get_lua(L);
819 std::vector<std::string> aspect_names;
820 std::transform(aspects.begin(), aspects.end(), std::back_inserter(aspect_names), std::mem_fn(&aspect_map::value_type::first));
827 lua_pushstring(L,
"attempted to write to the ai.aspects table, which is read-only");
846 if(!lua_isstring(L,2)) {
850 std::string m = lua_tostring(L,2);
858 lua_pushlightuserdata(L, &
engine);
860 lua_setfield(L, -2,
"__index");
862 lua_setfield(L, -2,
"__newindex");
863 lua_pushlightuserdata(L, &
engine);
865 lua_setfield(L, -2,
"__dir");
866 lua_setmetatable(L, -2);
869 lua_pushstring(L,
"read_only");
878 lua_pushlightuserdata(L, &
engine);
879 lua_pushcclosure(L,
p->func, 1);
888 auto callbacks = lua_check<std::vector<std::string>>(L, 2);
889 callbacks.push_back(
"side");
890 callbacks.push_back(
"aspects");
893 callbacks.push_back(
c->name);
903 static luaL_Reg
const callbacks[] = {
947 for (
const luaL_Reg*
p = callbacks;
p->name; ++
p) {
948 lua_pushlightuserdata(L,
engine);
949 lua_pushcclosure(L,
p->func, 1);
950 lua_pushstring(L,
p->name);
951 lua_pushvalue(L, -2);
956 lua_pushlightuserdata(L,
engine);
958 lua_setfield(L, -2,
"__index");
960 lua_setfield(L, -2,
"__dir");
961 lua_setmetatable(L, -2);
967 lua_getfield(L, LUA_REGISTRYINDEX,
aisKey);
968 size_t length_ai = lua_rawlen(L, -1);
971 lua_setfield(L, -2,
"ai");
972 lua_pushvalue(L, -1);
973 lua_rawseti(L, -3, length_ai + 1);
975 return length_ai + 1;
987 int res_ai = luaL_loadbufferx(
L, code, strlen(code), code,
"t");
991 char const *m = lua_tostring(
L, -1);
992 ERR_LUA <<
"error while initializing ai: " <<m;
998 lua_pushvalue(
L, -2);
999 lua_setfield(
L, -2,
"update_self");
1000 lua_pushlightuserdata(
L,
engine);
1001 lua_setfield(
L, -2,
"engine");
1011 lua_getfield(
L, -1,
"update_self");
1012 lua_getfield(
L, -2,
"params");
1013 lua_getfield(
L, -3,
"data");
1021 lua_setfield(
L, -2,
"self");
1029 int res = luaL_loadbufferx(
L, code, strlen(code), code,
"t");
1032 char const *m = lua_tostring(
L, -1);
1033 ERR_LUA <<
"error while creating ai function: " <<m;
1039 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1041 size_t length = lua_rawlen(
L, -1);
1042 lua_pushvalue(
L, -2);
1043 lua_rawseti(
L, -2, length + 1);
1055 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1056 lua_rawgeti(
L, -1, ctx.
num_);
1060 lua_getglobal(
L,
"ai");
1061 if(!lua_isnoneornil(
L, -1)) {
1063 lua_getfield(
L, -1,
"read_only");
1067 lua_pushstring(
L,
"read_only");
1068 lua_pushboolean(
L, read_only);
1074 lua_getfield(
L, -1,
"ai");
1075 lua_pushstring(
L,
"read_only");
1076 lua_pushboolean(
L, read_only);
1078 lua_setglobal(
L,
"ai");
1088 lua_setglobal(
L,
"ai");
1091 lua_getglobal(
L,
"ai");
1092 lua_pushstring(
L,
"read_only");
1102 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1104 lua_rawseti(
L, -2,
num_);
1110 int initial_top = lua_gettop(
L);
1116 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1117 lua_rawgeti(
L, -1,
num_);
1121 int iState = lua_absindex(
L, -2);
1122 lua_getfield(
L, iState,
"self");
1124 lua_getfield(
L, iState,
"data");
1127 if (!filter_own.
empty()) {
1135 l_obj->store(
L, -1);
1138 lua_settop(
L, initial_top);
1144 lua_getfield(
L, LUA_REGISTRYINDEX,
aisKey);
1146 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.