made basic attacking work with formula AI

This commit is contained in:
David White 2008-01-31 06:15:05 +00:00
parent b36e9cf757
commit a0e8dffd80
8 changed files with 150 additions and 55 deletions

View File

@ -63,7 +63,11 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
inputs=a,b
formula="a*b"
[/function]
recruit="if(turn = 1, ['Skeleton Archer', null()], if(turn = 2, ['Dark Adept', null()], ['Dark Adept']))"
[function]
name=build_attacks
inputs=attack_move
formula="map(attack_move.movements, attack(src, dst, attack_move.target))"
[/function]
move="if(turn = 1,
#turn 1
[ recruit('Skeleton Archer', loc(11,21)),
@ -99,8 +103,8 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
recruit('Skeleton Archer'),
'end_turn' ],
#turns after turn 4
[ recruit('Skeleton Archer'),
'end_turn' ]
[ recruit('Skeleton Archer') ] +
if(size(attacks) > 0, build_attacks(head(attacks)), ['end_turn'])
))))"
[/ai]

View File

@ -2269,7 +2269,7 @@ variant ai::attack_analysis::get_value(const std::string& key) const
} else if(key == "avg_losses") {
return variant(static_cast<int>(avg_losses*1000));
} else if(key == "chance_to_kill") {
return variant(static_cast<int>(chance_to_kill));
return variant(static_cast<int>(chance_to_kill*100));
} else if(key == "avg_damage_inflicted") {
return variant(static_cast<int>(avg_damage_inflicted));
} else if(key == "target_starting_damage") {

View File

@ -126,6 +126,7 @@ protected:
const location& to, const location& via,
const std::map<location,paths>& possible_moves) const;
public:
struct attack_analysis : public game_logic::formula_callable
{
void analyze(const gamemap& map, unit_map& units,
@ -184,6 +185,8 @@ protected:
bool is_surrounded;
};
protected:
virtual void do_attack_analysis(
const location& loc,
const move_map& srcdst, const move_map& dstsrc,

View File

@ -45,7 +45,7 @@ map_formula_callable& map_formula_callable::add(const std::string& key,
variant map_formula_callable::get_value(const std::string& key) const
{
return map_get_value_default(values_, key,
fallback_ ? fallback_->query_value(key) : variant(0));
fallback_ ? fallback_->query_value(key) : variant());
}
void map_formula_callable::get_inputs(std::vector<formula_input>* inputs) const

View File

@ -8,6 +8,102 @@
namespace {
using namespace game_logic;
class position_callable : public formula_callable {
unit_map units_;
int chance_;
variant get_value(const std::string& key) const {
if(key == "chance") {
return variant(chance_);
} else {
return variant();
}
}
void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
inputs->push_back(game_logic::formula_input("chance", game_logic::FORMULA_READ_ONLY));
}
public:
position_callable(unit_map* units, int chance) : chance_(chance)
{
units->swap(units_);
}
void swap_position(formula_ai& ai) {
ai.get_info().units.swap(units_);
}
struct swapper {
formula_ai& ai;
unit_map& a;
unit_map& b;
void swap() {
a.swap(b);
ai.prepare_move();
}
swapper(formula_ai& ai, position_callable& pos)
: ai(ai), a(ai.get_info().units), b(pos.units_) {
swap();
}
~swapper() {
swap();
}
};
};
class outcomes_function : public function_expression {
public:
outcomes_function(const args_list& args, const formula_ai& ai)
: function_expression(args, 1, 1), ai_(ai) {
}
private:
variant execute(const formula_callable& variables) const {
variant attack = args()[0]->evaluate(variables);
ai::attack_analysis* analysis = attack.convert_to<ai::attack_analysis>();
unit_map units_with_moves(ai_.get_info().units);
for(int n = 0; n != analysis->movements.size(); ++n) {
std::pair<gamemap::location,unit>* pair = units_with_moves.extract(analysis->movements[n].first);
pair->first = analysis->movements[n].second;
units_with_moves.add(pair);
}
std::vector<variant> vars;
if(analysis->chance_to_kill > 0.0) {
unit_map units(units_with_moves);
units.erase(analysis->target);
vars.push_back(variant(new position_callable(&units, analysis->chance_to_kill*100)));
}
if(analysis->chance_to_kill < 1.0) {
unit_map units(units_with_moves);
vars.push_back(variant(new position_callable(&units, 100 - analysis->chance_to_kill*100)));
}
return variant(&vars);
}
const formula_ai& ai_;
};
class evaluate_for_position_function : public function_expression {
public:
evaluate_for_position_function(const args_list& args, formula_ai& ai)
: function_expression(args, 2, 2), ai_(ai) {
}
private:
variant execute(const formula_callable& variables) const {
variant position = args()[0]->evaluate(variables);
position_callable* pos = position.convert_to<position_callable>();
position_callable::swapper swapper(ai_, *pos);
return args()[1]->evaluate(variables);
}
formula_ai& ai_;
};
class recruit_callable : public formula_callable {
gamemap::location loc_;
std::string type_;
@ -102,13 +198,12 @@ public:
{}
private:
variant execute(const formula_callable& variables) const {
const gamemap::location& move_from = args().front()->evaluate(variables).convert_to<location_callable>()->loc();
const gamemap::location& src = args()[args().size()-3]->evaluate(variables).convert_to<location_callable>()->loc();
const gamemap::location& dst = args()[args().size()-2]->evaluate(variables).convert_to<location_callable>()->loc();
const int weapon = args()[args().size()-1]->evaluate(variables).as_int();
const gamemap::location& move_from = args()[0]->evaluate(variables).convert_to<location_callable>()->loc();
const gamemap::location& src = args()[1]->evaluate(variables).convert_to<location_callable>()->loc();
const gamemap::location& dst = args()[2]->evaluate(variables).convert_to<location_callable>()->loc();
const int weapon = args().size() == 4 ? args()[3]->evaluate(variables).as_int() : -1;
if(ai_.get_info().units.count(move_from) == 0 || ai_.get_info().units.count(dst) == 0) {
std::cerr << "AI ERROR: Formula produced illegal attack!\n";
std::cerr << "AI ERROR: Formula produced illegal attack: " << move_from.x << ", " << move_from.y << " -> " << dst.x << ", " << dst.y << "\n";
return variant();
}
return variant(new attack_callable(ai_, move_from, src, dst, weapon));
@ -183,11 +278,15 @@ private:
};
class ai_function_symbol_table : public function_symbol_table {
const formula_ai& ai_;
formula_ai& ai_;
expression_ptr create_function(const std::string& fn,
const std::vector<expression_ptr>& args) const {
if(fn == "move") {
if(fn == "outcomes") {
return expression_ptr(new outcomes_function(args, ai_));
} else if(fn == "evaluate_for_position") {
return expression_ptr(new evaluate_for_position_function(args, ai_));
} else if(fn == "move") {
return expression_ptr(new move_function(args));
} else if(fn == "attack") {
return expression_ptr(new attack_function(args, ai_));
@ -207,7 +306,7 @@ class ai_function_symbol_table : public function_symbol_table {
}
public:
explicit ai_function_symbol_table(const formula_ai& ai) : ai_(ai)
explicit ai_function_symbol_table(formula_ai& ai) : ai_(ai)
{}
};
}
@ -256,42 +355,6 @@ std::string formula_ai::evaluate(const std::string& formula_str)
return v.to_debug_string();
}
namespace {
void debug_console(const game_logic::formula_callable& info, const formula_ai& ai) {
std::cerr << "starting debug console. Type formula to evaluate. Type 'continue' when you're ready to continue\n";
std::cerr << variant(&info).to_debug_string() << "\n";
ai_function_symbol_table function_table(ai);
const config& ai_param = ai.current_team().ai_parameters();
config::const_child_itors functions = ai_param.child_range("function");
for(config::const_child_iterator i = functions.first; i != functions.second; ++i) {
const t_string& name = (**i)["name"];
const t_string& inputs = (**i)["inputs"];
const t_string& formula_str = (**i)["formula"];
std::vector<std::string> args = utils::split(inputs);
function_table.add_formula_function(name, game_logic::const_formula_ptr(new game_logic::formula(formula_str, &function_table)), args);
}
for(;;) {
std::cerr << "\n>>> ";
char buf[1024];
std::cin.getline(buf, sizeof(buf));
std::string cmd(buf);
if(cmd == "continue") {
break;
}
try {
formula f(cmd, &function_table);
const variant v = f.execute(info);
std::cerr << v.to_debug_string() << "\n";
} catch(formula_error& e) {
std::cerr << "ERROR IN FORMULA\n";
}
}
}
}
void formula_ai::prepare_move()
{
possible_moves_.clear();
@ -341,7 +404,7 @@ bool formula_ai::make_move()
if(move) {
std::cerr << "moving " << move->src().x << "," << move->src().y << " -> " << move->dst().x << "," << move->dst().y << "\n";
if(possible_moves_.count(move->src()) > 0) {
move_unit(move->src(), move->dst(), possible_moves_); made_move = true;
move_unit(move->src(), move->dst(), possible_moves_);
made_move = true;
}
} else if(attack) {

View File

@ -67,6 +67,27 @@ public:
virtual ~formula_callable_no_ref_count() {}
};
class formula_callable_with_backup : public formula_callable {
const formula_callable& main_;
const formula_callable& backup_;
variant get_value(const std::string& key) const {
variant var = main_.query_value(key);
if(var.is_null()) {
return backup_.query_value(key);
}
return var;
}
void get_inputs(std::vector<formula_input>* inputs) const {
main_.get_inputs(inputs);
backup_.get_inputs(inputs);
}
public:
formula_callable_with_backup(const formula_callable& main, const formula_callable& backup) : main_(main), backup_(backup)
{}
};
class map_formula_callable : public formula_callable {
public:
explicit map_formula_callable(const formula_callable* fallback=NULL);

View File

@ -205,7 +205,7 @@ private:
int max_index = -1;
variant max_value;
for(int n = 0; n != items.num_elements(); ++n) {
const variant val = args()[1]->evaluate(*items[n].as_callable());
const variant val = args()[1]->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
if(max_index == -1 || val > max_value) {
max_index = n;
max_value = val;
@ -299,7 +299,7 @@ private:
std::vector<variant> vars;
const variant items = args()[0]->evaluate(variables);
for(int n = 0; n != items.num_elements(); ++n) {
const variant val = args()[1]->evaluate(*items[n].as_callable());
const variant val = args()[1]->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
if(val.as_bool()) {
vars.push_back(items[n]);
}
@ -319,7 +319,7 @@ private:
variant execute(const formula_callable& variables) const {
const variant items = args()[0]->evaluate(variables);
for(int n = 0; n != items.num_elements(); ++n) {
const variant val = args()[1]->evaluate(*items[n].as_callable());
const variant val = args()[1]->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
if(val.as_bool()) {
return items[n];
}
@ -339,7 +339,7 @@ private:
std::vector<variant> vars;
const variant items = args()[0]->evaluate(variables);
for(int n = 0; n != items.num_elements(); ++n) {
const variant val = args()[1]->evaluate(*items[n].as_callable());
const variant val = args()[1]->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
vars.push_back(val);
}

View File

@ -147,6 +147,10 @@ public:
void erase(iterator pos);
size_t erase(const gamemap::location &loc);
void swap(unit_map& o) {
map_.swap(o.map_);
}
private:
void delete_all();