diff --git a/data/scenario-test.cfg b/data/scenario-test.cfg index 129f1717b53..f9f00e947ed 100644 --- a/data/scenario-test.cfg +++ b/data/scenario-test.cfg @@ -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 diff --git a/src/ai.cpp b/src/ai.cpp index f5bb375271d..e842b68eb4b 100644 --- a/src/ai.cpp +++ b/src/ai.cpp @@ -689,7 +689,7 @@ void ai::attack_enemy(const location& attacking_unit, const location& target, void ai_interface::calculate_possible_moves(std::map& res, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement, - const std::set* remove_destinations) + const std::set* 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* remove_destinations, bool see_all - ) + ) const { for(unit_map::const_iterator un_it = units.begin(); un_it != units.end(); ++un_it) { diff --git a/src/ai_attack.cpp b/src/ai_attack.cpp index 374f850d82e..e677e473179 100644 --- a/src/ai_attack.cpp +++ b/src/ai_attack.cpp @@ -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; diff --git a/src/ai_interface.hpp b/src/ai_interface.hpp index 960dd73ffb6..af4d8b4513f 100644 --- a/src/ai_interface.hpp +++ b/src/ai_interface.hpp @@ -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& possible_moves, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement=false, - const std::set* remove_destinations=NULL); + const std::set* 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& possible_moves, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement=false, - const std::set* remove_destinations=NULL, bool see_all=false); + const std::set* 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 diff --git a/src/callable_objects.cpp b/src/callable_objects.cpp index b1b8e63c89c..88f0e779dde 100644 --- a/src/callable_objects.cpp +++ b/src/callable_objects.cpp @@ -51,13 +51,13 @@ variant move_map_callable::get_value(const std::string& key) const if(key == "moves") { std::vector 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(); } diff --git a/src/callable_objects.hpp b/src/callable_objects.hpp index 4fa3dd29eb3..33ddf1b4780 100644 --- a/src/callable_objects.hpp +++ b/src/callable_objects.hpp @@ -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* 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 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 { diff --git a/src/formula.cpp b/src/formula.cpp index 3ff27b36aba..787968fab61 100644 --- a/src/formula.cpp +++ b/src/formula.cpp @@ -54,10 +54,15 @@ void map_formula_callable::get_inputs(std::vector* inputs) const fallback_->get_inputs(inputs); } for(std::map::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; diff --git a/src/formula_ai.cpp b/src/formula_ai.cpp index 316e3d2e2b5..a5681971a9b 100644 --- a/src/formula_ai.cpp +++ b/src/formula_ai.cpp @@ -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(); + const location_callable* loc2 = args()[1]->evaluate(variables).convert_to(); + 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* 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()->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 vars; + const ai::move_map& dstsrc = args()[0]->evaluate(variables).convert_to()->dstsrc(); + std::pair range = + dstsrc.equal_range(args()[1]->evaluate(variables).convert_to()->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(); const attack_callable* attack = i->try_convert(); const recruit_callable* recruit_command = i->try_convert(); + const set_var_callable* set_var_command = i->try_convert(); 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); diff --git a/src/formula_ai.hpp b/src/formula_ai.hpp index 695ac3cb524..e0965733165 100644 --- a/src/formula_ai.hpp +++ b/src/formula_ai.hpp @@ -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 possible_moves_; - move_map srcdst_, dstsrc_, full_srcdst_, full_dstsrc_, enemy_srcdst_, enemy_dstsrc_; + mutable std::map 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 diff --git a/src/formula_callable.hpp b/src/formula_callable.hpp index ce4a7fdc388..612141f74a9 100644 --- a/src/formula_callable.hpp +++ b/src/formula_callable.hpp @@ -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* inputs) const; + void set_value(const std::string& key, const variant& value); std::map values_; const formula_callable* fallback_; }; diff --git a/src/formula_function.cpp b/src/formula_function.cpp index 6bb1a51b332..d714eddd17b 100644 --- a/src/formula_function.cpp +++ b/src/formula_function.cpp @@ -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 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(); diff --git a/src/menu_events.cpp b/src/menu_events.cpp index b8be437290a..44430c6b19d 100644 --- a/src/menu_events.cpp +++ b/src/menu_events.cpp @@ -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(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(...) { diff --git a/src/reference_counted_object.hpp b/src/reference_counted_object.hpp index 3e82f10193e..fbfc77ba0a7 100644 --- a/src/reference_counted_object.hpp +++ b/src/reference_counted_object.hpp @@ -16,6 +16,8 @@ public: void add_ref() const { ++count_; } void dec_ref() const { if(--count_ == 0) { delete const_cast(this); } } + int refcount() const { return count_; } + protected: void turn_reference_counting_off() { count_ = 1000000; } private: diff --git a/src/variant.cpp b/src/variant.cpp index aee29f79b81..aa361e92aca 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -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 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_) { diff --git a/src/variant.hpp b/src/variant.hpp index ec8b2d0a53a..b6287a8551f 100644 --- a/src/variant.hpp +++ b/src/variant.hpp @@ -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* seen=NULL) const;