mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-07 19:43:26 +00:00
added very basic AI which uses the formula AI
This commit is contained in:
parent
a0e8dffd80
commit
d9d5905b69
@ -58,26 +58,19 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
|
||||
gold=100
|
||||
ai_algorithm=formula_ai
|
||||
[ai]
|
||||
[function]
|
||||
name=mymul
|
||||
inputs=a,b
|
||||
formula="a*b"
|
||||
[/function]
|
||||
[function]
|
||||
name=build_attacks
|
||||
inputs=attack_move
|
||||
formula="map(attack_move.movements, attack(src, dst, attack_move.target))"
|
||||
[/function]
|
||||
move="if(turn = 1,
|
||||
[function]
|
||||
name=opening
|
||||
inputs="ai"
|
||||
formula="
|
||||
if(ai.turn = 1,
|
||||
#turn 1
|
||||
[ recruit('Skeleton Archer', loc(11,21)),
|
||||
recruit('Dark Adept', loc(11,22)),
|
||||
recruit('Dark Adept', loc(10,22)),
|
||||
recruit('Skeleton Archer', loc(9,22)),
|
||||
recruit('Ghost', loc(11,24)),
|
||||
move(loc(11,23), loc(14,22)),
|
||||
'end_turn' ],
|
||||
if(turn = 2,
|
||||
move(loc(11,23), loc(14,22)) ],
|
||||
if(ai.turn = 2,
|
||||
#turn 2
|
||||
[ move(loc(11,21),loc(13,17)),
|
||||
if(unit_at(loc(11,22)).total_movement = 6,
|
||||
@ -88,24 +81,81 @@ Gs^Fp , Gs^Fp , Wwf , Wwf , Mm , Rd
|
||||
move(loc(11,24),loc(18,24)),
|
||||
move(loc(14,22),loc(11,23)),
|
||||
recruit('Dark Adept', loc(11,21)),
|
||||
recruit('Dark Adept', loc(11,22)),
|
||||
'end_turn'],
|
||||
if(turn = 3,
|
||||
recruit('Dark Adept', loc(11,22)) ],
|
||||
if(ai.turn = 3,
|
||||
#turn 3
|
||||
[ move(loc(18,24),loc(20,22)),
|
||||
move(loc(15,19),loc(17,17)),
|
||||
move(loc(4,22),loc(5,18)),
|
||||
recruit('Skeleton Archer'),
|
||||
'end_turn' ],
|
||||
if(turn = 4,
|
||||
recruit('Skeleton Archer') ],
|
||||
if(ai.turn = 4,
|
||||
#turn 4
|
||||
[ move(loc(20,22),loc(20,15)),
|
||||
recruit('Skeleton Archer'),
|
||||
'end_turn' ],
|
||||
#turns after turn 4
|
||||
[ recruit('Skeleton Archer') ] +
|
||||
if(size(attacks) > 0, build_attacks(head(attacks)), ['end_turn'])
|
||||
))))"
|
||||
recruit('Skeleton Archer') ],
|
||||
[]))))"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=build_attacks
|
||||
inputs=attack_move
|
||||
formula="map(attack_move.movements, attack(src, dst, attack_move.target))"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=targets
|
||||
inputs="ai"
|
||||
formula="
|
||||
ai.enemy_and_unowned_villages
|
||||
"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=distance_to_target
|
||||
inputs="ai,dst"
|
||||
formula="min(map(targets(ai), distance_between(dst, self)))"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=move_to_targets
|
||||
inputs=ai
|
||||
formula="
|
||||
if(moves, choose(moves, -distance_to_target(ai, dst)), null())
|
||||
where moves = filter(ai.my_moves.moves, src != ai.my_leader)
|
||||
"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=get_village_captures
|
||||
inputs="ai"
|
||||
formula="
|
||||
sum(map(ai.enemy_and_unowned_villages, 'village', map(units_can_reach(ai.my_moves, village), move(loc, village))), [])
|
||||
"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=uncontended_captures
|
||||
inputs=ai
|
||||
formula="filter(get_village_captures(ai), (src != ai.my_leader) and (units_can_reach(ai.enemy_moves, dst).empty))"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=eval_unit
|
||||
inputs="unit"
|
||||
formula="((100 + unit.level*100)*(100 + (100*unit.hitpoints)/unit.max_hitpoints))/100"
|
||||
[/function]
|
||||
|
||||
[function]
|
||||
name=eval
|
||||
inputs="pos"
|
||||
formula="sum(map(my_units, eval_unit(self))) + size(my_villages)*1000"
|
||||
[/function]
|
||||
|
||||
move="if(var.done_opening, [], opening(self) + [set_var('done_opening', 1)]) +
|
||||
if(uncontended_captures(self), [head(uncontended_captures(self))],
|
||||
[ recruit('Skeleton Archer') ] +
|
||||
if(size(attacks) > 0, build_attacks(head(attacks)), [move_to_targets(self) or 'end_turn'])
|
||||
|
||||
)"
|
||||
[/ai]
|
||||
|
||||
#make the AI a lot more aggressive at night
|
||||
|
@ -689,7 +689,7 @@ void ai::attack_enemy(const location& attacking_unit, const location& target,
|
||||
|
||||
void ai_interface::calculate_possible_moves(std::map<location,paths>& res, move_map& srcdst,
|
||||
move_map& dstsrc, bool enemy, bool assume_full_movement,
|
||||
const std::set<gamemap::location>* remove_destinations)
|
||||
const std::set<gamemap::location>* remove_destinations) const
|
||||
{
|
||||
calculate_moves(info_.units,res,srcdst,dstsrc,enemy,assume_full_movement,remove_destinations);
|
||||
}
|
||||
@ -698,7 +698,7 @@ void ai_interface::calculate_moves(const unit_map& units, std::map<location,path
|
||||
move_map& dstsrc, bool enemy, bool assume_full_movement,
|
||||
const std::set<gamemap::location>* remove_destinations,
|
||||
bool see_all
|
||||
)
|
||||
) const
|
||||
{
|
||||
|
||||
for(unit_map::const_iterator un_it = units.begin(); un_it != units.end(); ++un_it) {
|
||||
|
@ -245,7 +245,7 @@ void ai::do_attack_analysis(
|
||||
cur_analysis.analyze(map_, units_, teams_, state_, gameinfo_, *this, dstsrc, srcdst, enemy_dstsrc, current_team().aggression());
|
||||
|
||||
//Remove this short-circuiting logic for now.. --David
|
||||
if(true) { //cur_analysis.rating(current_team().aggression(),*this) > rating_to_beat) {
|
||||
if(cur_analysis.rating(current_team().aggression(),*this) > rating_to_beat) {
|
||||
|
||||
result.push_back(cur_analysis);
|
||||
used_locations[cur_position] = true;
|
||||
|
@ -130,12 +130,12 @@ protected:
|
||||
//! that all units can move their full movement allotment.
|
||||
//! 'remove_destinations': a pointer to a set of possible destinations to omit.
|
||||
void calculate_possible_moves(std::map<location,paths>& possible_moves, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement=false,
|
||||
const std::set<location>* remove_destinations=NULL);
|
||||
const std::set<location>* remove_destinations=NULL) const;
|
||||
|
||||
//! A more fundamental version of calculate_possible_moves
|
||||
//! which allows the use of a speculative unit map.
|
||||
void calculate_moves(const unit_map& units, std::map<location,paths>& possible_moves, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement=false,
|
||||
const std::set<location>* remove_destinations=NULL, bool see_all=false);
|
||||
const std::set<location>* remove_destinations=NULL, bool see_all=false) const;
|
||||
|
||||
//! Recruit a unit. It will recruit the unit with the given name,
|
||||
//! at the given location, or at an available location to recruit units
|
||||
|
@ -51,13 +51,13 @@ variant move_map_callable::get_value(const std::string& key) const
|
||||
if(key == "moves") {
|
||||
std::vector<variant> vars;
|
||||
for(move_map::const_iterator i = srcdst_.begin(); i != srcdst_.end(); ++i) {
|
||||
map_formula_callable* item = new map_formula_callable;
|
||||
item->add("src", variant(new location_callable(i->first)));
|
||||
item->add("dst", variant(new location_callable(i->second)));
|
||||
move_callable* item = new move_callable(i->first, i->second);
|
||||
vars.push_back(variant(item));
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
} else if(key == "has_moves") {
|
||||
return variant(!srcdst_.empty());
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
|
@ -53,6 +53,30 @@ public:
|
||||
const gamemap::location& loc() const { return loc_; }
|
||||
};
|
||||
|
||||
class move_callable : public game_logic::formula_callable {
|
||||
gamemap::location src_, dst_;
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "src") {
|
||||
return variant(new location_callable(src_));
|
||||
} else if(key == "dst") {
|
||||
return variant(new location_callable(dst_));
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
|
||||
inputs->push_back(game_logic::formula_input("src", game_logic::FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("dst", game_logic::FORMULA_READ_ONLY));
|
||||
}
|
||||
public:
|
||||
move_callable(const gamemap::location& src, const gamemap::location& dst) :
|
||||
src_(src), dst_(dst)
|
||||
{}
|
||||
|
||||
const gamemap::location& src() const { return src_; }
|
||||
const gamemap::location& dst() const { return dst_; }
|
||||
};
|
||||
|
||||
class move_map_callable : public game_logic::formula_callable {
|
||||
typedef std::multimap<gamemap::location, gamemap::location> move_map;
|
||||
const move_map& srcdst_;
|
||||
@ -64,6 +88,9 @@ public:
|
||||
move_map_callable(const move_map& srcdst, const move_map& dstsrc)
|
||||
: srcdst_(srcdst), dstsrc_(dstsrc)
|
||||
{}
|
||||
|
||||
const move_map& srcdst() const { return srcdst_; }
|
||||
const move_map& dstsrc() const { return dstsrc_; }
|
||||
};
|
||||
|
||||
class unit_callable : public game_logic::formula_callable {
|
||||
|
@ -54,10 +54,15 @@ void map_formula_callable::get_inputs(std::vector<formula_input>* inputs) const
|
||||
fallback_->get_inputs(inputs);
|
||||
}
|
||||
for(std::map<std::string,variant>::const_iterator i = values_.begin(); i != values_.end(); ++i) {
|
||||
inputs->push_back(formula_input(i->first, FORMULA_READ_ONLY));
|
||||
inputs->push_back(formula_input(i->first, FORMULA_READ_WRITE));
|
||||
}
|
||||
}
|
||||
|
||||
void map_formula_callable::set_value(const std::string& key, const variant& value)
|
||||
{
|
||||
values_[key] = value;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class list_expression : public formula_expression {
|
||||
@ -108,6 +113,35 @@ private:
|
||||
expression_ptr operand_;
|
||||
};
|
||||
|
||||
class list_callable : public formula_callable {
|
||||
variant list_;
|
||||
public:
|
||||
explicit list_callable(const variant& list) : list_(list)
|
||||
{}
|
||||
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "size") {
|
||||
return variant(list_.num_elements());
|
||||
} else if(key == "empty") {
|
||||
return variant(list_.num_elements() == 0);
|
||||
} else if(key == "first") {
|
||||
if(list_.num_elements() > 0) {
|
||||
return list_[0];
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
} else if(key == "last") {
|
||||
if(list_.num_elements() > 0) {
|
||||
return list_[list_.num_elements()-1];
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class dot_expression : public formula_expression {
|
||||
public:
|
||||
dot_expression(expression_ptr left, expression_ptr right)
|
||||
@ -118,8 +152,7 @@ private:
|
||||
const variant left = left_->evaluate(variables);
|
||||
if(!left.is_callable()) {
|
||||
if(left.is_list()) {
|
||||
const variant index = right_->evaluate(variables);
|
||||
return left[index.as_int()];
|
||||
return right_->evaluate(list_callable(left));
|
||||
}
|
||||
|
||||
return left;
|
||||
@ -155,8 +188,8 @@ private:
|
||||
const variant left = left_->evaluate(variables);
|
||||
const variant right = right_->evaluate(variables);
|
||||
switch(op_) {
|
||||
case AND: return left.as_bool() && right.as_bool() ? variant(1) : variant(0);
|
||||
case OR: return left.as_bool() || right.as_bool() ? variant(1) : variant(0);
|
||||
case AND: return left.as_bool() == false ? left : right;
|
||||
case OR: return left.as_bool() ? left : right;
|
||||
case ADD: return left + right;
|
||||
case SUB: return left - right;
|
||||
case MUL: return left * right;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "formula_ai.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
#include "formula_function.hpp"
|
||||
#include "pathutils.hpp"
|
||||
|
||||
namespace {
|
||||
using namespace game_logic;
|
||||
@ -36,9 +37,10 @@ public:
|
||||
formula_ai& ai;
|
||||
unit_map& a;
|
||||
unit_map& b;
|
||||
formula_ai::move_map_backup backup;
|
||||
void swap() {
|
||||
a.swap(b);
|
||||
ai.prepare_move();
|
||||
ai.swap_move_map(backup);
|
||||
}
|
||||
swapper(formula_ai& ai, position_callable& pos)
|
||||
: ai(ai), a(ai.get_info().units), b(pos.units_) {
|
||||
@ -51,6 +53,20 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
class distance_between_function : public function_expression {
|
||||
public:
|
||||
explicit distance_between_function(const args_list& args)
|
||||
: function_expression(args, 2, 2)
|
||||
{}
|
||||
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
const location_callable* loc1 = args()[0]->evaluate(variables).convert_to<location_callable>();
|
||||
const location_callable* loc2 = args()[1]->evaluate(variables).convert_to<location_callable>();
|
||||
return variant(distance_between(loc1->loc(), loc2->loc()));
|
||||
}
|
||||
};
|
||||
|
||||
class outcomes_function : public function_expression {
|
||||
public:
|
||||
outcomes_function(const args_list& args, const formula_ai& ai)
|
||||
@ -134,18 +150,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class move_callable : public formula_callable {
|
||||
gamemap::location src_, dst_;
|
||||
variant get_value(const std::string& key) const { return variant(); }
|
||||
public:
|
||||
move_callable(const gamemap::location& src, const gamemap::location& dst) :
|
||||
src_(src), dst_(dst)
|
||||
{}
|
||||
|
||||
const gamemap::location& src() const { return src_; }
|
||||
const gamemap::location& dst() const { return dst_; }
|
||||
};
|
||||
|
||||
class move_function : public function_expression {
|
||||
public:
|
||||
explicit move_function(const args_list& args)
|
||||
@ -159,34 +163,66 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class set_var_callable : public formula_callable {
|
||||
std::string key_;
|
||||
variant value_;
|
||||
variant get_value(const std::string& key) const { return variant(); }
|
||||
public:
|
||||
set_var_callable(const std::string& key, const variant& value)
|
||||
: key_(key), value_(value)
|
||||
{}
|
||||
|
||||
const std::string& key() const { return key_; }
|
||||
variant value() const { return value_; }
|
||||
};
|
||||
|
||||
class set_var_function : public function_expression {
|
||||
public:
|
||||
explicit set_var_function(const args_list& args)
|
||||
: function_expression(args, 2, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(new set_var_callable(args()[0]->evaluate(variables).as_string(), args()[1]->evaluate(variables)));
|
||||
}
|
||||
};
|
||||
|
||||
class attack_callable : public formula_callable {
|
||||
gamemap::location move_from_, src_, dst_;
|
||||
int weapon_;
|
||||
battle_context bc_;
|
||||
variant get_value(const std::string& key) const {
|
||||
if(key == "attacker") {
|
||||
return variant(new location_callable(src_));
|
||||
} else if(key == "defender") {
|
||||
return variant(new location_callable(dst_));
|
||||
} else if(key == "move_from") {
|
||||
return variant(new location_callable(move_from_));
|
||||
} else {
|
||||
return variant();
|
||||
}
|
||||
}
|
||||
|
||||
void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
|
||||
inputs->push_back(game_logic::formula_input("attacker", game_logic::FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("defender", game_logic::FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("move_from", game_logic::FORMULA_READ_ONLY));
|
||||
}
|
||||
public:
|
||||
attack_callable(const formula_ai& ai,
|
||||
const gamemap::location& move_from,
|
||||
const gamemap::location& src, const gamemap::location& dst,
|
||||
int weapon)
|
||||
: move_from_(move_from), src_(src), dst_(dst), weapon_(weapon),
|
||||
: move_from_(move_from), src_(src), dst_(dst),
|
||||
bc_(ai.get_info().map, ai.get_info().teams, ai.get_info().units,
|
||||
ai.get_info().state, ai.get_info().gameinfo,
|
||||
src, dst, weapon, -1, 1.0, NULL, &ai.get_info().units.find(move_from)->second)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
const gamemap::location& move_from() const { return move_from_; }
|
||||
const gamemap::location& src() const { return src_; }
|
||||
const gamemap::location& dst() const { return dst_; }
|
||||
int weapon() const { return weapon_; }
|
||||
int weapon() const { return bc_.get_attacker_stats().attack_num; }
|
||||
int defender_weapon() const { return bc_.get_defender_stats().attack_num; }
|
||||
};
|
||||
|
||||
@ -203,7 +239,7 @@ private:
|
||||
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: " << move_from.x << ", " << move_from.y << " -> " << dst.x << ", " << dst.y << "\n";
|
||||
std::cerr << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst << "\n";
|
||||
return variant();
|
||||
}
|
||||
return variant(new attack_callable(ai_, move_from, src, dst, weapon));
|
||||
@ -277,6 +313,31 @@ private:
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class units_can_reach_function : public function_expression {
|
||||
public:
|
||||
units_can_reach_function(const args_list& args, const formula_ai& ai_object)
|
||||
: function_expression(args, 2, 2), ai_(ai_object)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
std::vector<variant> vars;
|
||||
const ai::move_map& dstsrc = args()[0]->evaluate(variables).convert_to<move_map_callable>()->dstsrc();
|
||||
std::pair<ai::move_map::const_iterator,ai::move_map::const_iterator> range =
|
||||
dstsrc.equal_range(args()[1]->evaluate(variables).convert_to<location_callable>()->loc());
|
||||
while(range.first != range.second) {
|
||||
unit_map::const_iterator un = ai_.get_info().units.find(range.first->second);
|
||||
assert(un != ai_.get_info().units.end());
|
||||
const int side = un->second.side();
|
||||
vars.push_back(variant(new unit_callable(*un, ai_.get_info().teams[side-1], side)));
|
||||
++range.first;
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
}
|
||||
|
||||
const formula_ai& ai_;
|
||||
};
|
||||
|
||||
class ai_function_symbol_table : public function_symbol_table {
|
||||
formula_ai& ai_;
|
||||
|
||||
@ -300,6 +361,12 @@ class ai_function_symbol_table : public function_symbol_table {
|
||||
return expression_ptr(new unit_at_function(args, ai_));
|
||||
} else if(fn == "unit_moves") {
|
||||
return expression_ptr(new unit_moves_function(args, ai_));
|
||||
} else if(fn == "set_var") {
|
||||
return expression_ptr(new set_var_function(args));
|
||||
} else if(fn == "units_can_reach") {
|
||||
return expression_ptr(new units_can_reach_function(args, ai_));
|
||||
} else if(fn == "distance_between") {
|
||||
return expression_ptr(new distance_between_function(args));
|
||||
} else {
|
||||
return function_symbol_table::create_function(fn, args);
|
||||
}
|
||||
@ -311,8 +378,9 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
formula_ai::formula_ai(info& i) : ai(i)
|
||||
formula_ai::formula_ai(info& i) : ai(i), move_maps_valid_(false)
|
||||
{
|
||||
vars_.add_ref();
|
||||
}
|
||||
|
||||
void formula_ai::play_turn()
|
||||
@ -355,8 +423,25 @@ std::string formula_ai::evaluate(const std::string& formula_str)
|
||||
return v.to_debug_string();
|
||||
}
|
||||
|
||||
void formula_ai::prepare_move()
|
||||
void formula_ai::swap_move_map(move_map_backup& backup)
|
||||
{
|
||||
std::swap(move_maps_valid_, backup.move_maps_valid);
|
||||
std::swap(backup.attacks_cache, attacks_cache_);
|
||||
backup.move_maps_valid = move_maps_valid_;
|
||||
backup.srcdst.swap(srcdst_);
|
||||
backup.dstsrc.swap(dstsrc_);
|
||||
backup.full_srcdst.swap(full_srcdst_);
|
||||
backup.full_dstsrc.swap(full_dstsrc_);
|
||||
backup.enemy_srcdst.swap(enemy_srcdst_);
|
||||
backup.enemy_dstsrc.swap(enemy_dstsrc_);
|
||||
}
|
||||
|
||||
void formula_ai::prepare_move() const
|
||||
{
|
||||
if(move_maps_valid_) {
|
||||
return;
|
||||
}
|
||||
|
||||
possible_moves_.clear();
|
||||
srcdst_.clear();
|
||||
dstsrc_.clear();
|
||||
@ -383,7 +468,7 @@ bool formula_ai::make_move()
|
||||
return false;
|
||||
}
|
||||
|
||||
prepare_move();
|
||||
move_maps_valid_ = false;
|
||||
|
||||
std::cerr << "do move...\n";
|
||||
const variant var = move_formula_->execute(*this);
|
||||
@ -401,6 +486,7 @@ bool formula_ai::make_move()
|
||||
const move_callable* move = i->try_convert<move_callable>();
|
||||
const attack_callable* attack = i->try_convert<attack_callable>();
|
||||
const recruit_callable* recruit_command = i->try_convert<recruit_callable>();
|
||||
const set_var_callable* set_var_command = i->try_convert<set_var_callable>();
|
||||
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) {
|
||||
@ -418,6 +504,7 @@ bool formula_ai::make_move()
|
||||
if(attack->move_from() != attack->src()) {
|
||||
move_unit(attack->move_from(), attack->src(), possible_moves_);
|
||||
}
|
||||
std::cerr << "ATTACK: " << attack->src() << " -> " << attack->dst() << " " << attack->weapon() << "\n";
|
||||
attack_enemy(attack->src(), attack->dst(), attack->weapon(), attack->defender_weapon());
|
||||
made_move = true;
|
||||
} else if(recruit_command) {
|
||||
@ -425,6 +512,8 @@ bool formula_ai::make_move()
|
||||
if(recruit(recruit_command->type(), recruit_command->loc())) {
|
||||
made_move = true;
|
||||
}
|
||||
} else if(set_var_command) {
|
||||
vars_.add(set_var_command->key(), set_var_command->value());
|
||||
} else if(i->is_string() && i->as_string() == "recruit") {
|
||||
do_recruitment();
|
||||
made_move = true;
|
||||
@ -471,6 +560,7 @@ void formula_ai::do_recruitment()
|
||||
variant formula_ai::get_value(const std::string& key) const
|
||||
{
|
||||
if(key == "attacks") {
|
||||
prepare_move();
|
||||
if(attacks_cache_.is_null() == false) {
|
||||
return attacks_cache_;
|
||||
}
|
||||
@ -484,9 +574,20 @@ variant formula_ai::get_value(const std::string& key) const
|
||||
attacks_cache_ = variant(&vars);
|
||||
return attacks_cache_;
|
||||
} else if(key == "my_moves") {
|
||||
prepare_move();
|
||||
return variant(new move_map_callable(srcdst_, dstsrc_));
|
||||
} else if(key == "enemy_moves") {
|
||||
prepare_move();
|
||||
return variant(new move_map_callable(enemy_srcdst_, enemy_dstsrc_));
|
||||
} else if(key == "my_leader") {
|
||||
unit_map::const_iterator i = team_leader(get_info().team_num, get_info().units);
|
||||
if(i == get_info().units.end()) {
|
||||
return variant();
|
||||
}
|
||||
|
||||
return variant(new location_callable(i->first));
|
||||
} else if(key == "vars") {
|
||||
return variant(&vars_);
|
||||
}
|
||||
|
||||
return ai_interface::get_value(key);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "ai.hpp"
|
||||
#include "ai_interface.hpp"
|
||||
#include "formula_fwd.hpp"
|
||||
#include "formula_callable.hpp"
|
||||
|
||||
class formula_ai : public ai {
|
||||
public:
|
||||
@ -18,7 +19,15 @@ public:
|
||||
|
||||
std::string evaluate(const std::string& formula_str);
|
||||
|
||||
void prepare_move();
|
||||
struct move_map_backup {
|
||||
move_map_backup() : move_maps_valid(false) {}
|
||||
bool move_maps_valid;
|
||||
move_map srcdst, dstsrc, full_srcdst, full_dstsrc, enemy_srcdst, enemy_dstsrc;
|
||||
variant attacks_cache;
|
||||
};
|
||||
|
||||
void swap_move_map(move_map_backup& backup);
|
||||
|
||||
private:
|
||||
void do_recruitment();
|
||||
bool make_move();
|
||||
@ -26,9 +35,14 @@ private:
|
||||
game_logic::const_formula_ptr recruit_formula_;
|
||||
game_logic::const_formula_ptr move_formula_;
|
||||
|
||||
std::map<location,paths> possible_moves_;
|
||||
move_map srcdst_, dstsrc_, full_srcdst_, full_dstsrc_, enemy_srcdst_, enemy_dstsrc_;
|
||||
mutable std::map<location,paths> possible_moves_;
|
||||
|
||||
void prepare_move() const;
|
||||
bool move_maps_valid_;
|
||||
mutable move_map srcdst_, dstsrc_, full_srcdst_, full_dstsrc_, enemy_srcdst_, enemy_dstsrc_;
|
||||
mutable variant attacks_cache_;
|
||||
|
||||
game_logic::map_formula_callable vars_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -22,8 +22,11 @@ struct formula_input {
|
||||
//interface for objects that can have formulae run on them
|
||||
class formula_callable : public reference_counted_object {
|
||||
public:
|
||||
explicit formula_callable(bool has_self=true) : has_self_(has_self)
|
||||
{}
|
||||
|
||||
variant query_value(const std::string& key) const {
|
||||
if(key == "self") {
|
||||
if(has_self_ && key == "self") {
|
||||
return variant(this);
|
||||
}
|
||||
return get_value(key);
|
||||
@ -57,6 +60,7 @@ protected:
|
||||
}
|
||||
private:
|
||||
virtual variant get_value(const std::string& key) const = 0;
|
||||
bool has_self_;
|
||||
};
|
||||
|
||||
class formula_callable_no_ref_count : public formula_callable {
|
||||
@ -84,7 +88,7 @@ class formula_callable_with_backup : public formula_callable {
|
||||
backup_.get_inputs(inputs);
|
||||
}
|
||||
public:
|
||||
formula_callable_with_backup(const formula_callable& main, const formula_callable& backup) : main_(main), backup_(backup)
|
||||
formula_callable_with_backup(const formula_callable& main, const formula_callable& backup) : formula_callable(false), main_(main), backup_(backup)
|
||||
{}
|
||||
};
|
||||
|
||||
@ -95,6 +99,7 @@ public:
|
||||
private:
|
||||
variant get_value(const std::string& key) const;
|
||||
void get_inputs(std::vector<formula_input>* inputs) const;
|
||||
void set_value(const std::string& key, const variant& value);
|
||||
std::map<std::string,variant> values_;
|
||||
const formula_callable* fallback_;
|
||||
};
|
||||
|
@ -332,15 +332,26 @@ private:
|
||||
class map_function : public function_expression {
|
||||
public:
|
||||
explicit map_function(const args_list& args)
|
||||
: function_expression(args, 2, 2)
|
||||
: function_expression(args, 2, 3)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
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(formula_callable_with_backup(*items[n].as_callable(), variables));
|
||||
vars.push_back(val);
|
||||
|
||||
if(args().size() == 2) {
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
const variant val = args().back()->evaluate(formula_callable_with_backup(*items[n].as_callable(), variables));
|
||||
vars.push_back(val);
|
||||
}
|
||||
} else {
|
||||
map_formula_callable self_callable;
|
||||
const std::string self = args()[1]->evaluate(variables).as_string();
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
self_callable.add(self, items[n]);
|
||||
const variant val = args().back()->evaluate(formula_callable_with_backup(self_callable, formula_callable_with_backup(*items[n].as_callable(), variables)));
|
||||
vars.push_back(val);
|
||||
}
|
||||
}
|
||||
|
||||
return variant(&vars);
|
||||
@ -350,12 +361,15 @@ private:
|
||||
class sum_function : public function_expression {
|
||||
public:
|
||||
explicit sum_function(const args_list& args)
|
||||
: function_expression(args, 1, 1)
|
||||
: function_expression(args, 1, 2)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
variant res(0);
|
||||
const variant items = args()[0]->evaluate(variables);
|
||||
if(args().size() >= 2) {
|
||||
res = args()[1]->evaluate(variables);
|
||||
}
|
||||
for(int n = 0; n != items.num_elements(); ++n) {
|
||||
res = res + items[n];
|
||||
}
|
||||
@ -399,6 +413,17 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class refcount_function : public function_expression {
|
||||
public:
|
||||
explicit refcount_function(const args_list& args)
|
||||
: function_expression(args, 1, 1)
|
||||
{}
|
||||
private:
|
||||
variant execute(const formula_callable& variables) const {
|
||||
return variant(args()[0]->evaluate(variables).refcount());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
variant formula_function_expression::execute(const formula_callable& variables) const
|
||||
@ -477,6 +502,8 @@ expression_ptr create_function(const std::string& fn,
|
||||
return expression_ptr(new size_function(args));
|
||||
} else if(fn == "null") {
|
||||
return expression_ptr(new null_function(args));
|
||||
} else if(fn == "refcount") {
|
||||
return expression_ptr(new refcount_function(args));
|
||||
} else {
|
||||
std::cerr << "no function '" << fn << "'\n";
|
||||
throw formula_error();
|
||||
|
@ -1510,7 +1510,7 @@ private:
|
||||
}
|
||||
|
||||
//Ask for confirmation if the player hasn't made any moves (other than gotos).
|
||||
if(preferences::confirm_no_moves() && units_alive && !some_units_have_moved) {
|
||||
if(false && preferences::confirm_no_moves() && units_alive && !some_units_have_moved) {
|
||||
const int res = gui::dialog(*gui_,"",_("You have not started your turn yet. Do you really want to end your turn?"), gui::YES_NO).show();
|
||||
if(res != 0) {
|
||||
return false;
|
||||
@ -2329,7 +2329,6 @@ private:
|
||||
turn_info turn_data(gameinfo_, gamestate_, status_, *gui_, const_cast<gamemap&>(map_), teams_, team_num, units_, dummy_sender, dummy_undo);
|
||||
ai_interface::info info(*gui_, map_, gameinfo_, units_, teams_, team_num, status_, turn_data, gamestate_);
|
||||
formula_ai eval(info);
|
||||
eval.prepare_move();
|
||||
try {
|
||||
add_chat_message(time(NULL), _("ai"), 0, eval.evaluate(str));
|
||||
} catch(...) {
|
||||
|
@ -16,6 +16,8 @@ public:
|
||||
void add_ref() const { ++count_; }
|
||||
void dec_ref() const { if(--count_ == 0) { delete const_cast<reference_counted_object*>(this); } }
|
||||
|
||||
int refcount() const { return count_; }
|
||||
|
||||
protected:
|
||||
void turn_reference_counting_off() { count_ = 1000000; }
|
||||
private:
|
||||
|
@ -28,14 +28,14 @@ std::string variant_type_to_string(variant::TYPE type) {
|
||||
}
|
||||
|
||||
struct variant_list {
|
||||
variant_list() : refcount(0)
|
||||
variant_list() : refcount(1)
|
||||
{}
|
||||
std::vector<variant> elements;
|
||||
int refcount;
|
||||
};
|
||||
|
||||
struct variant_string {
|
||||
variant_string() : refcount(0)
|
||||
variant_string() : refcount(1)
|
||||
{}
|
||||
std::string str;
|
||||
int refcount;
|
||||
@ -384,6 +384,23 @@ void variant::serialize_from_string(const std::string& str)
|
||||
*this = game_logic::formula(str).execute();
|
||||
}
|
||||
|
||||
int variant::refcount() const
|
||||
{
|
||||
switch(type_) {
|
||||
case TYPE_LIST:
|
||||
return list_->refcount;
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
return string_->refcount;
|
||||
break;
|
||||
case TYPE_CALLABLE:
|
||||
return callable_->refcount();
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string variant::string_cast() const
|
||||
{
|
||||
switch(type_) {
|
||||
|
@ -85,6 +85,8 @@ public:
|
||||
void serialize_to_string(std::string& str) const;
|
||||
void serialize_from_string(const std::string& str);
|
||||
|
||||
int refcount() const;
|
||||
|
||||
std::string string_cast() const;
|
||||
|
||||
std::string to_debug_string(std::vector<const game_logic::formula_callable*>* seen=NULL) const;
|
||||
|
Loading…
x
Reference in New Issue
Block a user