added very basic AI which uses the formula AI

This commit is contained in:
David White 2008-02-03 18:59:40 +00:00
parent a0e8dffd80
commit d9d5905b69
15 changed files with 351 additions and 74 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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();
}

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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_;
};

View File

@ -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();

View File

@ -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(...) {

View File

@ -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:

View File

@ -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_) {

View File

@ -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;