new formula_ai-related stages

This commit is contained in:
Iurii Chernyi 2009-08-08 12:03:40 +00:00
parent 5e0e72324e
commit 2ce5d43784
21 changed files with 805 additions and 365 deletions

View file

@ -40,38 +40,38 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
{DEFAULT_SCHEDULE}
[label]
x,y=16,5
text="Patrol waypoint 1"
x,y=16,5
text="Patrol waypoint 1"
[/label]
[label]
x,y=16,15
text="Patrol waypoint 2"
x,y=16,15
text="Patrol waypoint 2"
[/label]
[label]
x,y=3,14
text="Formula priorities test"
x,y=3,14
text="Formula priorities test"
[/label]
[label]
x,y=2,12
text="first"
x,y=2,12
text="first"
[/label]
[label]
x,y=3,11
text="second"
x,y=3,11
text="second"
[/label]
[label]
x,y=3,13
text="third"
x,y=3,13
text="third"
[/label]
[label]
x,y=8,5
text="Location guarded (range = 3)"
x,y=8,5
text="Location guarded (range = 3)"
[/label]
[side]
@ -92,26 +92,25 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
[unit]
x,y=3,12
type="Elvish Fighter"
random_traits=no
random_traits=no
generate_name=yes
[modifications]
[trait]
id=move
[effect]
apply_to=movement
set=0
[/effect]
[/trait]
[trait]
id=hp
[effect]
apply_to=hitpoints
increase_total=120
[/effect]
[/trait]
[/modifications]
[modifications]
[trait]
id=move
[effect]
apply_to=movement
set=0
[/effect]
[/trait]
[trait]
id=hp
[effect]
apply_to=hitpoints
increase_total=120
[/effect]
[/trait]
[/modifications]
[/unit]
[/side]
[side]
@ -128,69 +127,68 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
x,y=8,5
type="Orcish Archer"
generate_name=yes
[ai]
formula="if(attack, attack, move(me.loc, me.vars.guard_loc))
[ai]
formula="if(attack, attack, move(me.loc, me.vars.guard_loc))
where attack = choose(filter(attacks, units = [me.loc] and distance_between(me.vars.guard_loc, target) <= me.vars.guard_radius), avg_damage_inflicted)"
[vars]
guard_radius=3
guard_loc="loc(8,5)"
[/vars]
[/ai]
[vars]
guard_radius=3
guard_loc="loc(8,5)"
[/vars]
[/ai]
[/unit]
[unit]
x,y=3,8
type="Walking Corpse"
generate_name=yes
[ai]
formula="move(me.loc, nearest_loc(nearest_loc(me.loc,map(filter(map.terrain,id='castle'),loc)),unit_moves(me.loc)))"
[/ai]
[ai]
formula="move(me.loc, nearest_loc(nearest_loc(me.loc,map(filter(map.terrain,id='castle'),loc)),unit_moves(me.loc)))"
[/ai]
[/unit]
[unit]
x,y=16,5
type="Wolf Rider"
generate_name=yes
[ai]
loop_formula="{ai/formula/patrol.fai}"
[vars]
guard_radius=3
waypoints=[ loc(16,5) -> loc(16,15), loc(16,15) -> loc(16,5) ]
next_step="loc(16,5)"
[/vars]
[/ai]
[ai]
loop_formula="{ai/formula/patrol.fai}"
[vars]
guard_radius=3
waypoints=[ loc(16,5) -> loc(16,15), loc(16,15) -> loc(16,5) ]
next_step="loc(16,5)"
[/vars]
[/ai]
[/unit]
[unit]
x,y=3,11
type="Goblin Spearman"
generate_name=yes
[ai]
formula="attack(me.loc, me.loc, loc(3,12))"
priority=10
[/ai]
[ai]
formula="attack(me.loc, me.loc, loc(3,12))"
priority=10
[/ai]
[/unit]
[unit]
x,y=3,13
type="Goblin Spearman"
generate_name=yes
[ai]
priority=9
formula="attack(me.loc, me.loc, loc(3,12))"
[/ai]
[ai]
priority=9
formula="attack(me.loc, me.loc, loc(3,12))"
[/ai]
[/unit]
[unit]
x,y=2,12
type="Goblin Spearman"
generate_name=yes
[ai]
priority=11
formula="attack(me.loc, me.loc, loc(3,12))"
[/ai]
[ai]
priority=11
formula="attack(me.loc, me.loc, loc(3,12))"
[/ai]
[/unit]
[unit]
x,y=7,20
type="Silver Mage"
@ -219,26 +217,36 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
generate_name=yes
[/unit]
ai_algorithm=formula_ai
[ai]
version=10703
[stage]
engine=fai
name=unit_formulas
[/stage]
eval_list=yes
[register_candidate_move]
name=scouting
type=movement
action="{ai/formula/scouting_move.fai}"
evaluation="{ai/formula/scouting_eval.fai}"
[/register_candidate_move]
[stage]
engine=fai
name=move
move="{ai/formula/opening.fai}"
[/stage]
[register_candidate_move]
name=level_up_attack
type=attack
action="{ai/formula/level_up_attack_move.fai}"
evaluation="{ai/formula/level_up_attack_eval.fai}"
[/register_candidate_move]
[stage]
engine=fai
name=rca
[register_candidate_move]
name=scouting
type=movement
action="{ai/formula/scouting_move.fai}"
evaluation="{ai/formula/scouting_eval.fai}"
[/register_candidate_move]
move="{ai/formula/opening.fai}"
[register_candidate_move]
name=level_up_attack
type=attack
action="{ai/formula/level_up_attack_move.fai}"
evaluation="{ai/formula/level_up_attack_eval.fai}"
[/register_candidate_move]
[/stage]
[/ai]
[/side]
[/test]

View file

@ -88,6 +88,12 @@
<Unit filename="..\..\src\ai\formula\candidates.hpp" />
<Unit filename="..\..\src\ai\formula\function_table.cpp" />
<Unit filename="..\..\src\ai\formula\function_table.hpp" />
<Unit filename="..\..\src\ai\formula\stage_rca_formulas.cpp" />
<Unit filename="..\..\src\ai\formula\stage_rca_formulas.hpp" />
<Unit filename="..\..\src\ai\formula\stage_side_formulas.cpp" />
<Unit filename="..\..\src\ai\formula\stage_side_formulas.hpp" />
<Unit filename="..\..\src\ai\formula\stage_unit_formulas.cpp" />
<Unit filename="..\..\src\ai\formula\stage_unit_formulas.hpp" />
<Unit filename="..\..\src\ai\game_info.cpp" />
<Unit filename="..\..\src\ai\game_info.hpp" />
<Unit filename="..\..\src\ai\interface.cpp" />

View file

@ -117,6 +117,12 @@
<Unit filename="..\..\src\ai\formula\candidates.hpp" />
<Unit filename="..\..\src\ai\formula\function_table.cpp" />
<Unit filename="..\..\src\ai\formula\function_table.hpp" />
<Unit filename="..\..\src\ai\formula\stage_rca_formulas.cpp" />
<Unit filename="..\..\src\ai\formula\stage_rca_formulas.hpp" />
<Unit filename="..\..\src\ai\formula\stage_side_formulas.cpp" />
<Unit filename="..\..\src\ai\formula\stage_side_formulas.hpp" />
<Unit filename="..\..\src\ai\formula\stage_unit_formulas.cpp" />
<Unit filename="..\..\src\ai\formula\stage_unit_formulas.hpp" />
<Unit filename="..\..\src\ai\game_info.cpp" />
<Unit filename="..\..\src\ai\game_info.hpp" />
<Unit filename="..\..\src\ai\interface.cpp" />

View file

@ -4391,6 +4391,90 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\ai\formula\stage_rca_formulas.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug (fast)|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\ai\formula\stage_side_formulas.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug (fast)|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\ai\formula\stage_unit_formulas.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug (fast)|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\ai\formula\"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="testing"
@ -5682,6 +5766,18 @@
RelativePath="..\..\src\ai\formula\function_table.hpp"
>
</File>
<File
RelativePath="..\..\src\ai\formula\stage_rca_formulas.hpp"
>
</File>
<File
RelativePath="..\..\src\ai\formula\stage_side_formulas.hpp"
>
</File>
<File
RelativePath="..\..\src\ai\formula\stage_unit_formulas.hpp"
>
</File>
</Filter>
<Filter
Name="testing"

View file

@ -241,6 +241,9 @@ SET(wesnoth-main_SRC
ai/formula/callable_objects.cpp
ai/formula/candidates.cpp
ai/formula/function_table.cpp
ai/formula/stage_rca_formulas.cpp
ai/formula/stage_side_formulas.cpp
ai/formula/stage_unit_formulas.cpp
ai/game_info.cpp
ai/interface.cpp
ai/manager.cpp

View file

@ -64,6 +64,9 @@ wesnoth_source = \
ai/formula/callable_objects.cpp \
ai/formula/candidates.cpp \
ai/formula/function_table.cpp \
ai/formula/stage_rca_formulas.cpp \
ai/formula/stage_side_formulas.cpp \
ai/formula/stage_unit_formulas.cpp \
ai/game_info.cpp \
ai/interface.cpp \
ai/manager.cpp \
@ -227,7 +230,7 @@ wesnoth_source = \
widgets/combo_drag.cpp \
widgets/drop_target.cpp \
widgets/scrollpane.cpp
# used with editor option in the wesnoth target
wesnoth_editor_SOURCES = \
gui/dialogs/editor_generate_map.cpp \

View file

@ -169,6 +169,9 @@ wesnoth_sources = Split("""
ai/formula/callable_objects.cpp
ai/formula/candidates.cpp
ai/formula/function_table.cpp
ai/formula/stage_rca_formulas.cpp
ai/formula/stage_side_formulas.cpp
ai/formula/stage_unit_formulas.cpp
ai/game_info.cpp
ai/interface.cpp
ai/manager.cpp

View file

@ -19,6 +19,9 @@
#include "ai.hpp"
#include "engine_fai.hpp"
#include "../formula/stage_rca_formulas.hpp"
#include "../formula/stage_side_formulas.hpp"
#include "../formula/stage_unit_formulas.hpp"
#include "rca.hpp"
#include "../../foreach.hpp"
#include "../../log.hpp"
@ -70,9 +73,9 @@ private:
};
engine_fai::engine_fai( readonly_context &context, const config &cfg )
: engine(context,cfg), formula_ai_(context,cfg.child("formula_ai"))
: engine(context,cfg), formula_ai_(context,cfg.child_or_empty("formula_ai"))
{
formula_ai_.on_create();
}
@ -93,6 +96,20 @@ void engine_fai::do_parse_candidate_action_from_config( rca_context &context, co
}
void engine_fai::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b )
{
if (!cfg) {
return;
}
const std::string &name = cfg["name"];
if (name=="rca_formulas") {
*b = stage_ptr(new stage_rca_formulas(context,cfg,formula_ai_));
} else if (name=="side_formulas") {
*b = stage_ptr(new stage_side_formulas(context,cfg,formula_ai_));
} else if (name=="unit_formulas") {
*b = stage_ptr(new stage_unit_formulas(context,cfg,formula_ai_));
}
}
std::string engine_fai::evaluate(const std::string &str)
{

View file

@ -38,6 +38,8 @@ public:
virtual void do_parse_candidate_action_from_config( rca_context &context, const config &cfg, std::back_insert_iterator<std::vector< candidate_action_ptr > > b );
virtual void do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b );
virtual std::string evaluate(const std::string &str);
virtual std::string get_name() const;

View file

@ -293,9 +293,6 @@ void ai_default::new_turn()
unit_combat_scores_.clear();
invalidate_keeps_cache();
unit_stats_cache().clear();
if (formula_ai_ != NULL){
formula_ai_->new_turn();
}
}
std::string ai_default::describe_self(){
@ -1774,18 +1771,13 @@ bool ai_default::is_accessible(const location& loc, const move_map& dstsrc) cons
}
variant ai_default::get_value(const std::string& key) const
variant ai_default::get_value(const std::string &/*key*/) const
{
if(key == "map") {
return variant(new gamemap_callable(get_info().map));
}
return variant();
}
void ai_default::get_inputs(std::vector<game_logic::formula_input>* inputs) const
void ai_default::get_inputs(std::vector<game_logic::formula_input> */*inputs*/) const
{
using game_logic::FORMULA_READ_ONLY;
inputs->push_back(game_logic::formula_input("map", FORMULA_READ_ONLY));
}

View file

@ -34,6 +34,7 @@
#include "../../menu_events.hpp"
static lg::log_domain log_formula_ai("ai/formula_ai");
#define DBG_AI LOG_STREAM(debug, log_formula_ai)
#define LOG_AI LOG_STREAM(info, log_formula_ai)
#define WRN_AI LOG_STREAM(warn, log_formula_ai)
#define ERR_AI LOG_STREAM(err, log_formula_ai)
@ -45,11 +46,7 @@ namespace ai {
game_logic::candidate_action_ptr formula_ai::load_candidate_action_from_config(const config& cfg)
{
return candidate_action_manager_.load_candidate_action_from_config(cfg,this,&function_table);
}
std::string formula_ai::describe_self(){
return "[formula_ai]";
return candidate_action_manager_.load_candidate_action_from_config(cfg,this,&function_table_);
}
int formula_ai::get_recursion_count() const{
@ -57,20 +54,18 @@ int formula_ai::get_recursion_count() const{
}
formula_ai::formula_ai(readonly_context &context, const config &cfg) :
formula_ai::formula_ai(readonly_context &context, const config &cfg)
:
readonly_context_proxy(),
cfg_(cfg),
recursion_counter_(context.get_recursion_count()),
recruit_formula_(),
move_formula_(),
outcome_positions_(),
possible_moves_(),
move_maps_valid_(false),
attacks_cache_(),
keeps_cache_(),
infinite_loop_guardian_(),
vars_(),
function_table(*this),
function_table_(*this),
candidate_action_manager_()
{
add_ref();
@ -105,7 +100,7 @@ void formula_ai::display_message(const std::string& msg) const
formula_ptr formula_ai::create_optional_formula(const std::string& formula_string){
try{
return game_logic::formula::create_optional_formula(formula_string, &function_table);
return game_logic::formula::create_optional_formula(formula_string, &function_table_);
}
catch(formula_error& e) {
handle_exception(e);
@ -113,127 +108,6 @@ formula_ptr formula_ai::create_optional_formula(const std::string& formula_strin
}
}
void formula_ai::new_turn()
{
move_maps_valid_ = false;
//ai_default::new_turn();
}
void formula_ai::play_turn()
{
/*
//execute units formulas first
unit_formula_set units_with_formulas;
for(unit_map::unit_iterator i = units_.begin() ; i != units_.end() ; ++i)
{
if ( (i->second.side() == get_side()) )
{
if ( i->second.has_formula() || i->second.has_loop_formula()) {
int priority = 0;
if( i->second.has_priority_formula() ) {
try {
game_logic::const_formula_ptr priority_formula(new game_logic::formula(i->second.get_priority_formula(), &function_table));
game_logic::map_formula_callable callable(this);
callable.add_ref();
callable.add("me", variant(new unit_callable(*i)));
priority = (formula::evaluate(priority_formula, callable)).as_int();
} catch(formula_error& e) {
if(e.filename == "formula")
e.line = 0;
handle_exception( e, "Unit priority formula error for unit: '" + i->second.type_id() + "' standing at (" + boost::lexical_cast<std::string>(i->first.x+1) + "," + boost::lexical_cast<std::string>(i->first.y+1) + ")");
priority = 0;
} catch(type_error& e) {
priority = 0;
ERR_AI << "formula type error while evaluating unit priority formula " << e.message << "\n";
}
}
units_with_formulas.insert( unit_formula_pair( i, priority ) );
}
}
}
for(unit_formula_set::iterator pair_it = units_with_formulas.begin() ; pair_it != units_with_formulas.end() ; ++pair_it)
{
unit_map::iterator i = pair_it->first;
if( i.valid() ) {
if ( i->second.has_formula() ) {
try {
game_logic::const_formula_ptr formula(new game_logic::formula(i->second.get_formula(), &function_table));
game_logic::map_formula_callable callable(this);
callable.add_ref();
callable.add("me", variant(new unit_callable(*i)));
make_action(formula, callable);
}
catch(formula_error& e) {
if(e.filename == "formula")
e.line = 0;
handle_exception( e, "Unit formula error for unit: '" + i->second.type_id() + "' standing at (" + boost::lexical_cast<std::string>(i->first.x+1) + "," + boost::lexical_cast<std::string>(i->first.y+1) + ")");
}
}
}
if( i.valid() ) {
if( i->second.has_loop_formula() )
{
try {
game_logic::const_formula_ptr loop_formula(new game_logic::formula(i->second.get_loop_formula(), &function_table));
game_logic::map_formula_callable callable(this);
callable.add_ref();
callable.add("me", variant(new unit_callable(*i)));
while ( !make_action(loop_formula, callable).is_empty() && i.valid() ) {}
}
catch(formula_error& e) {
if(e.filename == "formula")
e.line = 0;
handle_exception( e, "Unit loop formula error for unit: '" + i->second.type_id() + "' standing at (" + boost::lexical_cast<std::string>(i->first.x+1) + "," + boost::lexical_cast<std::string>(i->first.y+1) + ")");
}
}
}
}
try {
if( candidate_action_manager_.has_candidate_actions() ) {
move_maps_valid_ = false;
while( candidate_action_manager_.evaluate_candidate_actions(this, units_) )
{
game_logic::map_formula_callable callable(this);
callable.add_ref();
candidate_action_manager_.update_callable_map( callable );
const_formula_ptr move_formula(candidate_action_manager_.get_best_action_formula());
make_action(move_formula, callable);
move_maps_valid_ = false;
}
}
}
catch(formula_error& e) {
if(e.filename == "formula")
e.line = 0;
handle_exception( e, "Formula error in RCA loop");
}
game_logic::map_formula_callable callable(this);
callable.add_ref();
try {
while( !make_action(move_formula_,callable).is_empty() ) { }
}
catch(formula_error& e) {
if(e.filename == "formula")
e.line = 0;
handle_exception( e, "Formula error");
}
*/
}
void formula_ai::set_ai_context(ai_context *context)
{
@ -246,7 +120,7 @@ std::string formula_ai::evaluate(const std::string& formula_str)
try{
move_maps_valid_ = false;
game_logic::formula f(formula_str, &function_table);
game_logic::formula f(formula_str, &function_table_);
game_logic::map_formula_callable callable(this);
callable.add_ref();
@ -289,17 +163,6 @@ void formula_ai::prepare_move() const
variant formula_ai::make_action(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables)
{
// if(!formula_) {
// if(get_recursion_count()<recursion_counter::MAX_COUNTER_VALUE) {
// LOG_AI << "Falling back to default AI.\n";
// ai_ptr fallback( manager::create_transient_ai(manager::AI_TYPE_DEFAULT, config(), this));
// if (fallback){
// fallback->play_turn();
// }
// }
// return variant();
// }
move_maps_valid_ = false;
LOG_AI << "do move...\n";
@ -392,51 +255,43 @@ std::set<map_location> formula_ai::get_allowed_teleports(unit_map::iterator& uni
}
map_location formula_ai::path_calculator(const map_location& src, const map_location& dst, unit_map::iterator& unit_it) const{
std::map<map_location,paths>::iterator path = possible_moves_.find(src);
std::map<map_location,paths>::const_iterator path = get_possible_moves().find(src);
map_location destination = dst;
map_location destination = dst;
//check if destination is within unit's reach, if not, calculate where to move
//check if destination is within unit's reach, if not, calculate where to move
if (!path->second.destinations.contains(dst))
{
std::set<map_location> allowed_teleports = get_allowed_teleports(unit_it);
//destination is too far, check where unit can go
std::set<map_location> allowed_teleports = get_allowed_teleports(unit_it);
//destination is too far, check where unit can go
plain_route route = shortest_path_calculator( src, dst, unit_it, allowed_teleports );
if( route.steps.size() == 0 ) {
emergency_path_calculator em_calc(unit_it->second, get_info().map);
route = a_star_search(src, dst, 1000.0, &em_calc, get_info().map.w(), get_info().map.h(), &allowed_teleports);
if( route.steps.size() < 2 ) {
return map_location();
}
}
destination = map_location();
for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
typedef move_map::const_iterator Itor;
std::pair<Itor,Itor> range = get_srcdst().equal_range(src);
bool found = false;
for(Itor i = range.first; i != range.second; ++i) {
if (i->second == *loc_iter ) {
found = true;
break;
}
if( route.steps.size() == 0 ) {
emergency_path_calculator em_calc(unit_it->second, get_info().map);
route = a_star_search(src, dst, 1000.0, &em_calc, get_info().map.w(), get_info().map.h(), &allowed_teleports);
if( route.steps.size() < 2 ) {
return map_location();
}
}
if ( !found ) {
continue;
}
destination = *loc_iter;
}
return destination;
}
return destination;
destination = map_location();
for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
typedef move_map::const_iterator Itor;
std::pair<Itor,Itor> range = get_srcdst().equal_range(src);
bool found = false;
for(Itor i = range.first; i != range.second; ++i) {
if (i->second == *loc_iter ) {
found = true;
break;
}
}
if ( !found ) {
continue;
}
destination = *loc_iter;
}
return destination;
}
return destination;
}
//commandline=true when we evaluate formula from commandline, false otherwise (default)
@ -592,7 +447,7 @@ variant formula_ai::execute_variant(const variant& var, ai_context &ai_, bool co
//is_ok() must be checked or the code will complain :)
if (!recruit_result->is_ok()) {
//get_status() can be used to fetch the error code
LOG_AI << "ERROR #" <<recruit_result->get_status() << " while executing 'recruit' formula function\n"<<std::endl;
ERR_AI << "ERROR #" <<recruit_result->get_status() << " while executing 'recruit' formula function\n"<<std::endl;
if(safe_call) {
//safe call was called, prepare error information
@ -612,7 +467,7 @@ variant formula_ai::execute_variant(const variant& var, ai_context &ai_, bool co
made_moves.push_back(action);
} else {
//too many calls in a row - possible infinite loop
LOG_AI << "ERROR #" << 5001 << " while executing 'set_var' formula function\n";
ERR_AI << "ERROR #" << 5001 << " while executing 'set_var' formula function\n";
if( safe_call )
error = variant(new safe_call_result(set_var_command, 5001));
@ -621,7 +476,7 @@ variant formula_ai::execute_variant(const variant& var, ai_context &ai_, bool co
int status = 0;
unit_map::iterator unit;
if( infinite_loop_guardian_.set_unit_var_check() ) {
if( !infinite_loop_guardian_.set_unit_var_check() ) {
status = 5001; //exceeded nmber of calls in a row - possible infinite loop
} else if( (unit = get_info().units.find(set_unit_var_command->loc())) == get_info().units.end() ) {
status = 5002; //unit not found
@ -634,21 +489,23 @@ variant formula_ai::execute_variant(const variant& var, ai_context &ai_, bool co
unit->second.add_formula_var(set_unit_var_command->key(), set_unit_var_command->value());
made_moves.push_back(action);
} else {
LOG_AI << "ERROR #" << status << " while executing 'set_unit_var' formula function\n";
ERR_AI << "ERROR #" << status << " while executing 'set_unit_var' formula function\n";
if(safe_call)
error = variant(new safe_call_result(set_unit_var_command,
status));
}
} else if( action.is_string() && action.as_string() == "recruit") {
if( do_recruitment() )
made_moves.push_back(action);
//:} else if( action.is_string() && action.as_string() == "recruit") {
// recruitment temporary disabled
// @todo 1.7.3 rework recruitment as aspect
//if( do_recruitment() )
// made_moves.push_back(action);
} else if( action.is_string() && action.as_string() == "continue") {
if( infinite_loop_guardian_.continue_check() ) {
made_moves.push_back(action);
} else {
//too many calls in a row - possible infinite loop
LOG_AI << "ERROR #" << 5001 << " while executing 'continue' formula keyword\n";
ERR_AI << "ERROR #" << 5001 << " while executing 'continue' formula keyword\n";
if( safe_call )
error = variant(new safe_call_result(NULL, 5001));
@ -713,30 +570,9 @@ variant formula_ai::execute_variant(const variant& var, ai_context &ai_, bool co
return variant(&made_moves);
}
bool formula_ai::do_recruitment()
void formula_ai::add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
{
if(!recruit_formula_) {
return false;
}
bool ret = false;
game_logic::map_formula_callable callable(this);
callable.add_ref();
try {
while( !make_action(recruit_formula_,callable).is_empty() )
{
ret = true;
}
}
catch(formula_error& e) {
if(e.filename == "recruit formula")
e.line = 0;
handle_exception( e, "Formula error");
}
return ret;
function_table_.add_formula_function(name,formula,precondition,args);
}
namespace {
@ -937,11 +773,12 @@ variant formula_ai::get_value(const std::string& key) const
} else if(key == "keeps")
{
return get_keeps();
} else if(key == "map")
{
return variant(new gamemap_callable(get_info().map));
} else if(key == "villages")
{
return villages_from_set(get_info().map.villages());
} else if(key == "villages_of_side")
{
std::vector<variant> vars;
@ -964,7 +801,6 @@ variant formula_ai::get_value(const std::string& key) const
return villages_from_set(get_info().map.villages(), &current_team().villages());
}
//return ai_default::get_value(key);
return variant();
}
@ -980,7 +816,7 @@ void formula_ai::get_inputs(std::vector<formula_input>* inputs) const
inputs->push_back(game_logic::formula_input("vars", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("allies", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("enemies", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("my_moves", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("map", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("my_attacks", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("enemy_moves", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("my_leader", FORMULA_READ_ONLY));
@ -994,8 +830,6 @@ void formula_ai::get_inputs(std::vector<formula_input>* inputs) const
inputs->push_back(game_logic::formula_input("my_villages", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("villages_of_side", FORMULA_READ_ONLY));
inputs->push_back(game_logic::formula_input("enemy_and_unowned_villages", FORMULA_READ_ONLY));
//ai_default::get_inputs(inputs);
}
variant formula_ai::get_keeps() const
@ -1046,49 +880,28 @@ bool formula_ai::can_reach_unit(map_location unit_A, map_location unit_B) const
void formula_ai::on_create(){
//make sure we don't run out of refcount
vars_.add_ref();
const config& ai_param = cfg_;
// load candidate actions from config
candidate_action_manager_.load_config(ai_param, this, &function_table);
foreach (const config &func, ai_param.child_range("function"))
foreach (const config &func, cfg_.child_range("function"))
{
const t_string &name = func["name"];
const t_string &inputs = func["inputs"];
const t_string &formula_str = func["formula"];
std::vector<std::string> args = utils::split(inputs);
try {
function_table.add_formula_function(name,
game_logic::const_formula_ptr(new game_logic::formula(formula_str, &function_table)),
game_logic::formula::create_optional_formula(func["precondition"], &function_table),
args);
}
catch(formula_error& e) {
handle_exception(e, "Error while registering function '" + name + "'");
}
add_formula_function(name,
create_optional_formula(formula_str),
create_optional_formula(func["precondition"]),
args);
}
try{
recruit_formula_ = game_logic::formula::create_optional_formula(cfg_["recruitment"], &function_table);
}
catch(formula_error& e) {
handle_exception(e);
recruit_formula_ = game_logic::formula_ptr();
}
try{
move_formula_ = game_logic::formula::create_optional_formula(cfg_["move"], &function_table);
}
catch(formula_error& e) {
handle_exception(e);
move_formula_ = game_logic::formula_ptr();
}
catch(game_logic::formula_error& e) {
handle_exception(e, "Error while registering function '" + name + "'");
}
}
}
void formula_ai::evaluate_candidate_action(game_logic::candidate_action_ptr fai_ca)
{
fai_ca->evaluate(this,get_info().units);
@ -1152,6 +965,7 @@ config formula_ai::to_config() const
{
return config();
}
DBG_AI << "formula_ai::to_config(): "<< cfg_<<std::endl;
return cfg_;//@todo 1.7 add a proper serialization
}

View file

@ -27,7 +27,7 @@
#include "../composite/contexts.hpp"
#include "../default/ai.hpp"
#include "../../formula.hpp"
#include "../../formula_fwd.hpp"
#include "../../formula_callable.hpp"
@ -60,15 +60,12 @@ class formula_ai : public readonly_context_proxy, public game_logic::formula_cal
public:
explicit formula_ai(readonly_context &context, const config &cfg);
virtual ~formula_ai() {};
virtual void play_turn();
virtual void new_turn();
virtual std::string describe_self();
virtual config to_config() const;
const move_map& srcdst() const { if(!move_maps_valid_) { prepare_move(); } return srcdst_; }
std::string evaluate(const std::string& formula_str);
virtual void add_formula_function(const std::string& name, game_logic::const_formula_ptr formula, game_logic::const_formula_ptr precondition, const std::vector<std::string>& args);
//class responsible for looking for possible infinite loops when calling set_var or set_unit_var
class gamestate_change_observer : public events::observer
{
@ -104,8 +101,6 @@ public:
// Check if given unit can reach another unit
bool can_reach_unit(map_location unit_A, map_location unit_B) const;
const std::map<map_location,paths>& get_possible_moves() const { prepare_move(); return possible_moves_; }
void handle_exception(game_logic::formula_error& e) const;
void handle_exception(game_logic::formula_error& e, const std::string& failed_operation) const;
@ -137,34 +132,29 @@ public:
bool execute_candidate_action(game_logic::candidate_action_ptr fai_ca);
void set_ai_context(ai_context *context);
variant make_action(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables);
private:
ai_context *ai_ptr_;
const config &cfg_;
const config cfg_;
recursion_counter recursion_counter_;
void display_message(const std::string& msg) const;
bool do_recruitment();
variant make_action(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables);
variant execute_variant(const variant& var, ai_context &ai_, bool commandline=false);
virtual variant get_value(const std::string& key) const;
virtual void get_inputs(std::vector<game_logic::formula_input>* inputs) const;
game_logic::const_formula_ptr recruit_formula_;
game_logic::const_formula_ptr move_formula_;
std::vector<variant> outcome_positions_;
mutable std::map<map_location,paths> possible_moves_;
void prepare_move() const;
map_location path_calculator(const map_location& src, const map_location& dst, unit_map::iterator& unit_it) const;
mutable bool move_maps_valid_;
mutable move_map srcdst_, dstsrc_, full_srcdst_, full_dstsrc_, enemy_srcdst_, enemy_dstsrc_;
mutable variant attacks_cache_;
mutable variant keeps_cache_;
gamestate_change_observer infinite_loop_guardian_;
game_logic::map_formula_callable vars_;
game_logic::ai_function_symbol_table function_table;
game_logic::ai_function_symbol_table function_table_;
game_logic::candidate_action_manager candidate_action_manager_;
friend class ai_default;

View file

@ -24,6 +24,12 @@
#include "../../formula_fwd.hpp"
#include "../../formula_function.hpp"
namespace ai {
class formula_ai;
}
namespace game_logic {
class base_candidate_action;

View file

@ -947,7 +947,7 @@ private:
}
const map_location& loc = convert_variant<location_callable>(res)->loc();
const ai::move_map& srcdst = ai_.srcdst();
const ai::move_map& srcdst = ai_.get_srcdst();
typedef ai::move_map::const_iterator Itor;
std::pair<Itor,Itor> range = srcdst.equal_range(loc);

View file

@ -0,0 +1,90 @@
/* $Id$ */
/*
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file ai/formula/stage_rca_formulas.cpp
* Defines formula ai rca formulas stage
* */
#include "stage_rca_formulas.hpp"
#include "ai.hpp"
#include "../../formula.hpp"
#include "../../formula_function.hpp"
#include "../../log.hpp"
#include <boost/lexical_cast.hpp>
static lg::log_domain log_ai("ai/stage/rca_formulas");
#define LOG_AI LOG_STREAM(info, log_ai)
#define WRN_AI LOG_STREAM(warn, log_ai)
#define ERR_AI LOG_STREAM(err, log_ai)
namespace ai {
stage_rca_formulas::stage_rca_formulas(ai_context &context, const config &cfg, formula_ai &fai)
: stage(context,cfg), cfg_(cfg), fai_(fai)
{
/* try {
if( candidate_action_manager_.has_candidate_actions() ) {
while( candidate_action_manager_.evaluate_candidate_actions(&fai_, get_info().units) )
{
game_logic::map_formula_callable callable(&fai_);
callable.add_ref();
candidate_action_manager_.update_callable_map( callable );
game_logic::const_formula_ptr move_formula(candidate_action_manager_.get_best_action_formula());
fai_.make_action(move_formula, callable);
}
}
}
catch(game_logic::formula_error& e) {
if(e.filename == "formula") {
e.line = 0;
}
fai_.handle_exception( e, "Formula error in RCA loop");
}
*/
}
stage_rca_formulas::~stage_rca_formulas()
{
}
void stage_rca_formulas::do_play_stage()
{
}
void stage_rca_formulas::on_create()
{
//@todo parse the formulas
//candidate_action_manager_.load_config(ai_param, this, &function_table);
}
config stage_rca_formulas::to_config() const
{
config cfg = stage::to_config();
//@todo: serialize to config
cfg.append(cfg_);
return cfg;
}
} // end of namespace ai

View file

@ -0,0 +1,63 @@
/* $Id$ */
/*
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file ai/formula/stage_rca_formulas.hpp
* Stage which executes rca formulas
* */
#ifndef AI_FORMULA_STAGE_RCA_FORMULAS_HPP_INCLUDED
#define AI_FORMULA_STAGE_RCA_FORMULAS_HPP_INCLUDED
#include "../composite/stage.hpp"
#include "candidates.hpp"
#ifdef _MSC_VER
#pragma warning(push)
//silence "inherits via dominance" warnings
#pragma warning(disable:4250)
#endif
namespace ai {
class formula_ai;
class stage_rca_formulas: public stage {
public:
stage_rca_formulas( ai_context &context, const config &cfg, formula_ai &fai );
virtual ~stage_rca_formulas();
void do_play_stage();
void on_create();
config to_config() const;
private:
const config &cfg_;
formula_ai &fai_;
game_logic::candidate_action_manager candidate_action_manager_;
};
} //end of namespace ai
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif

View file

@ -0,0 +1,78 @@
/* $Id$ */
/*
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file ai/formula/stage_stage_formulas.cpp
* Defines formula ai side formulas stage
* */
#include "stage_side_formulas.hpp"
#include "ai.hpp"
#include "../../foreach.hpp"
#include "../../formula.hpp"
#include "../../formula_function.hpp"
#include "../../log.hpp"
#include <boost/lexical_cast.hpp>
static lg::log_domain log_ai("ai/stage/side_formulas");
#define LOG_AI LOG_STREAM(info, log_ai)
#define WRN_AI LOG_STREAM(warn, log_ai)
#define ERR_AI LOG_STREAM(err, log_ai)
namespace ai {
stage_side_formulas::stage_side_formulas(ai_context &context, const config &cfg, formula_ai &fai)
: stage(context,cfg), cfg_(cfg), fai_(fai), move_formula_()
{
}
stage_side_formulas::~stage_side_formulas()
{
}
void stage_side_formulas::do_play_stage()
{
game_logic::map_formula_callable callable(&fai_);
callable.add_ref();
try {
while( !fai_.make_action(move_formula_,callable).is_empty() ) { }
}
catch(game_logic::formula_error& e) {
if(e.filename == "formula") {
e.line = 0;
}
fai_.handle_exception( e, "Formula error");
}
}
void stage_side_formulas::on_create()
{
move_formula_ = fai_.create_optional_formula(cfg_["move"]);
}
config stage_side_formulas::to_config() const
{
config cfg = stage::to_config();
//@todo: serialize to config
cfg.append(cfg_);
return cfg;
}
} // end of namespace ai

View file

@ -0,0 +1,62 @@
/* $Id$ */
/*
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file ai/formula/stage_side_formulas.hpp
* Stage which executes side formulas
* */
#ifndef AI_FORMULA_STAGE_SIDE_FORMULAS_HPP_INCLUDED
#define AI_FORMULA_STAGE_SIDE_FORMULAS_HPP_INCLUDED
#include "../composite/stage.hpp"
#include "../../formula_fwd.hpp"
#ifdef _MSC_VER
#pragma warning(push)
//silence "inherits via dominance" warnings
#pragma warning(disable:4250)
#endif
namespace ai {
class formula_ai;
class stage_side_formulas: public stage {
public:
stage_side_formulas( ai_context &context, const config &cfg, formula_ai &fai );
virtual ~stage_side_formulas();
void do_play_stage();
void on_create();
config to_config() const;
private:
const config &cfg_;
formula_ai &fai_;
game_logic::const_formula_ptr move_formula_;
};
} //end of namespace ai
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif

View file

@ -0,0 +1,142 @@
/* $Id$ */
/*
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file ai/formula/stage_unit_formulas.cpp
* Defines formula ai unit formulas stage
* */
#include "stage_unit_formulas.hpp"
#include "ai.hpp"
#include "../../formula.hpp"
#include "../../formula_function.hpp"
#include "../../log.hpp"
#include <boost/lexical_cast.hpp>
static lg::log_domain log_formula_ai("ai/stage/unit_formulas");
#define LOG_AI LOG_STREAM(info, log_formula_ai)
#define WRN_AI LOG_STREAM(warn, log_formula_ai)
#define ERR_AI LOG_STREAM(err, log_formula_ai)
namespace ai {
stage_unit_formulas::stage_unit_formulas(ai_context &context, const config &cfg, formula_ai &fai)
: stage(context,cfg), cfg_(cfg), fai_(fai)
{
}
stage_unit_formulas::~stage_unit_formulas()
{
}
void stage_unit_formulas::do_play_stage()
{
//execute units formulas first
game_logic::unit_formula_set units_with_formulas;
unit_map &units_ = get_info().units;
for(unit_map::unit_iterator i = units_.begin() ; i != units_.end() ; ++i)
{
if ( (i->second.side() == get_side()) ) {
if ( i->second.has_formula() || i->second.has_loop_formula()) {
int priority = 0;
if( i->second.has_priority_formula() ) {
try {
game_logic::const_formula_ptr priority_formula(fai_.create_optional_formula(i->second.get_priority_formula()));
game_logic::map_formula_callable callable(&fai_);
callable.add_ref();
callable.add("me", variant(new unit_callable(*i)));
priority = (game_logic::formula::evaluate(priority_formula, callable)).as_int();
} catch(game_logic::formula_error& e) {
if(e.filename == "formula")
e.line = 0;
fai_.handle_exception( e, "Unit priority formula error for unit: '" + i->second.type_id() + "' standing at (" + boost::lexical_cast<std::string>(i->first.x+1) + "," + boost::lexical_cast<std::string>(i->first.y+1) + ")");
priority = 0;
} catch(type_error& e) {
priority = 0;
ERR_AI << "formula type error while evaluating unit priority formula " << e.message << "\n";
}
}
units_with_formulas.insert( game_logic::unit_formula_pair( i, priority ) );
}
}
}
for(game_logic::unit_formula_set::iterator pair_it = units_with_formulas.begin() ; pair_it != units_with_formulas.end() ; ++pair_it)
{
unit_map::iterator i = pair_it->first;
if( i.valid() ) {
if ( i->second.has_formula() ) {
try {
game_logic::const_formula_ptr formula(fai_.create_optional_formula(i->second.get_formula()));
game_logic::map_formula_callable callable(&fai_);
callable.add_ref();
callable.add("me", variant(new unit_callable(*i)));
fai_.make_action(formula, callable);
}
catch(game_logic::formula_error& e) {
if(e.filename == "formula") {
e.line = 0;
}
fai_.handle_exception( e, "Unit formula error for unit: '" + i->second.type_id() + "' standing at (" + boost::lexical_cast<std::string>(i->first.x+1) + "," + boost::lexical_cast<std::string>(i->first.y+1) + ")");
}
}
}
if( i.valid() ) {
if( i->second.has_loop_formula() )
{
try {
game_logic::const_formula_ptr loop_formula(fai_.create_optional_formula(i->second.get_loop_formula()));
game_logic::map_formula_callable callable(&fai_);
callable.add_ref();
callable.add("me", variant(new unit_callable(*i)));
while ( !fai_.make_action(loop_formula, callable).is_empty() && i.valid() )
{
}
} catch(game_logic::formula_error& e) {
if (e.filename == "formula") {
e.line = 0;
}
fai_.handle_exception( e, "Unit loop formula error for unit: '" + i->second.type_id() + "' standing at (" + boost::lexical_cast<std::string>(i->first.x+1) + "," + boost::lexical_cast<std::string>(i->first.y+1) + ")");
}
}
}
}
}
void stage_unit_formulas::on_create()
{
//@todo parse the formulas
}
config stage_unit_formulas::to_config() const
{
config cfg = stage::to_config();
//@todo: serialize to config
return cfg;
}
} // end of namespace ai

View file

@ -0,0 +1,61 @@
/* $Id$ */
/*
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file ai/formula/stage_unit_formulas.hpp
* Stage which executes unit formulas
* */
#ifndef AI_FORMULA_STAGE_UNIT_FORMULAS_HPP_INCLUDED
#define AI_FORMULA_STAGE_UNIT_FORMULAS_HPP_INCLUDED
#include "../composite/stage.hpp"
#ifdef _MSC_VER
#pragma warning(push)
//silence "inherits via dominance" warnings
#pragma warning(disable:4250)
#endif
namespace ai {
class formula_ai;
class stage_unit_formulas: public stage {
public:
stage_unit_formulas( ai_context &context, const config &cfg, formula_ai &fai );
virtual ~stage_unit_formulas();
void do_play_stage();
void on_create();
config to_config() const;
private:
const config &cfg_;
formula_ai &fai_;
};
} //end of namespace ai
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif

View file

@ -40,10 +40,8 @@ fallback_to_other_ai::fallback_to_other_ai( ai_context &context, const config &c
void fallback_to_other_ai::on_create()
{
const config &ai_cfg = cfg_.child("ai");
if (ai_cfg) {
fallback_ai_ = manager::create_transient_ai(ai_cfg["ai_algorithm"], ai_cfg, this);
}
const config &ai_cfg = cfg_.child_or_empty("ai");
fallback_ai_ = manager::create_transient_ai(ai_cfg["ai_algorithm"], ai_cfg, this);
}