formula_ai.cpp

Go to the documentation of this file.
00001 /* $Id: formula_ai.cpp 26789 2008-05-23 11:24:07Z dragonking $ */
00002 /*
00003    Copyright (C) 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 #include <boost/regex.hpp>
00016 
00017 #include "actions.hpp"
00018 #include "callable_objects.hpp"
00019 #include "formula.hpp"
00020 #include "formula_ai.hpp"
00021 #include "formula_callable.hpp"
00022 #include "formula_function.hpp"
00023 #include "pathutils.hpp"
00024 #include "log.hpp"
00025 #include "attack_prediction.hpp"
00026 
00027 #define LOG_AI LOG_STREAM(info, ai)
00028 #define WRN_AI LOG_STREAM(warn, ai)
00029 #define ERR_AI LOG_STREAM(err, ai)
00030 
00031 namespace {
00032 using namespace game_logic;
00033 
00034 class position_callable : public formula_callable {
00035     unit_map units_;
00036     int chance_;
00037     variant get_value(const std::string& key) const {
00038         if(key == "chance") {
00039             return variant(chance_);
00040         } else {
00041             return variant();
00042         }
00043     }
00044 
00045     void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
00046         inputs->push_back(game_logic::formula_input("chance", game_logic::FORMULA_READ_ONLY));
00047     }
00048 public:
00049     position_callable(unit_map* units, int chance) : chance_(chance)
00050     {
00051         units->swap(units_);
00052     }
00053 
00054     void swap_position(formula_ai& ai) {
00055         ai.get_info().units.swap(units_);
00056     }
00057 
00058     class swapper {
00059     public:
00060         formula_ai& ai;
00061         unit_map& a;
00062         unit_map& b;
00063         formula_ai::move_map_backup backup;
00064         void swap() {
00065             a.swap(b);
00066             ai.swap_move_map(backup);
00067         }
00068         swapper(formula_ai& ai, position_callable& pos)
00069           : ai(ai), a(ai.get_info().units), b(pos.units_) {
00070           swap();
00071         }
00072 
00073         ~swapper() {
00074             swap();
00075         }
00076     };
00077     friend class swapper;
00078 };
00079 
00080 class distance_between_function : public function_expression {
00081 public:
00082     explicit distance_between_function(const args_list& args)
00083       : function_expression("distance_between", args, 2, 2)
00084     {}
00085 
00086 private:
00087     variant execute(const formula_callable& variables) const {
00088         const args_list& arguments = args();
00089         const expression_ptr& exp_p = arguments[0];
00090         variant my_variant = exp_p->evaluate(variables);
00091         const gamemap::location loc1 = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
00092         const gamemap::location loc2 = convert_variant<location_callable>(args()[1]->evaluate(variables))->loc();
00093         return variant(distance_between(loc1, loc2));
00094     }
00095 };
00096 
00097 class distance_to_nearest_unowned_village_function : public function_expression {
00098 public:
00099     distance_to_nearest_unowned_village_function(const args_list& args, const formula_ai& ai)
00100       : function_expression("distance_to_nearest_unowned_village", args, 1, 1), ai_(ai) {
00101     }
00102 
00103 private:
00104     variant execute(const formula_callable& variables) const {
00105         const gamemap::location loc = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
00106         int best = 1000000;
00107         const std::vector<gamemap::location>& villages = ai_.get_info().map.villages();
00108         const std::set<gamemap::location>& my_villages = ai_.current_team().villages();
00109         for(std::vector<gamemap::location>::const_iterator i = villages.begin(); i != villages.end(); ++i) {
00110             int distance = distance_between(loc, *i);
00111             if(distance < best) {
00112                 if(my_villages.count(*i) == 0) {
00113                     best = distance;
00114                 }
00115             }
00116         }
00117 
00118         return variant(best);
00119     }
00120 
00121     const formula_ai& ai_;
00122 };
00123 
00124 class nearest_loc_function : public function_expression {
00125 public:
00126     nearest_loc_function(const args_list& args, const formula_ai& ai)
00127       : function_expression("nearest_loc", args, 2, 2), ai_(ai) {
00128     }
00129 
00130 private:
00131     variant execute(const formula_callable& variables) const {
00132         const gamemap::location loc = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
00133         variant items = args()[1]->evaluate(variables); 
00134         int best = 1000000;
00135         int best_i = -1;
00136 
00137         for(size_t i = 0; i < items.num_elements(); ++i) {
00138 
00139             const gamemap::location move_loc = convert_variant<location_callable>(items[i])->loc();
00140             int distance = distance_between(loc, move_loc);
00141 
00142             if(distance < best) {
00143                     best = distance;
00144                     best_i = i;
00145             }
00146         }
00147 
00148         if( best_i != -1)
00149             return variant(new location_callable(convert_variant<location_callable>(items[best_i])->loc()));
00150         else
00151             return variant();
00152     }
00153 
00154     const formula_ai& ai_;
00155 };
00156 
00157 
00158 class nearest_keep_function : public function_expression {
00159 public:
00160     nearest_keep_function(const args_list& args, const formula_ai& ai)
00161       : function_expression("nearest_keep", args, 1, 1), ai_(ai) {
00162     }
00163 
00164 private:
00165     variant execute(const formula_callable& variables) const {
00166         const gamemap::location loc = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
00167         int best = 1000000;
00168         int best_i = -1;
00169 
00170         ai_.get_keeps();
00171         int size = ai_.get_keeps_cache().num_elements();
00172 
00173         for( int i = 0 ; i < size; ++i) {
00174             int distance = distance_between(loc, convert_variant<location_callable>(ai_.get_keeps_cache()[i])->loc() );
00175             if(distance < best)
00176             {
00177                     best = distance;
00178                     best_i = i;
00179             }
00180         }
00181 
00182         if( best_i != -1)
00183             return variant(new location_callable(convert_variant<location_callable>(ai_.get_keeps_cache()[best_i])->loc()));
00184         else
00185             return variant();
00186     }
00187 
00188     const formula_ai& ai_;
00189 };
00190 
00191 class close_enemies_function : public function_expression {
00192 public:
00193     close_enemies_function(const args_list& args, const formula_ai& ai)
00194       : function_expression("close_enemies", args, 2, 2), ai_(ai) {
00195     }
00196 
00197 private:
00198     variant execute(const formula_callable& variables) const {
00199         std::vector<variant> vars;
00200         const gamemap::location loc = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
00201         int range_s = args()[1]->evaluate(variables).as_int();
00202         if (range_s < 0) {
00203             WRN_AI << "close_enemies_function: range is negative (" << range_s << ")\n";
00204             range_s = 0;
00205         }
00206         size_t range = static_cast<size_t>(range_s);
00207         unit_map::const_iterator un = ai_.get_info().units.begin();
00208         unit_map::const_iterator end = ai_.get_info().units.end();
00209         while (un != end) {
00210             if (distance_between(loc, un->first) <= range) {
00211                 if (un->second.side() != ai_.get_info().team_num) {
00212                     vars.push_back(variant(new unit_callable(*un)));
00213                 }
00214             }
00215             ++un;           
00216         }
00217         return variant(&vars);
00218     }
00219 
00220     const formula_ai& ai_;
00221 };
00222 
00223 
00224 class outcome_callable : public formula_callable {
00225     std::vector<variant> hitLeft_, prob_, status_;
00226     variant get_value(const std::string& key) const {
00227         if(key == "hitpoints_left") {
00228             return variant(new std::vector<variant>(hitLeft_));
00229         } else if(key == "probability") {
00230             return variant(new std::vector<variant>(prob_));
00231         } else if(key == "possible_status") {
00232             return variant(new std::vector<variant>(status_));
00233         } else {
00234             return variant();
00235         }
00236     }
00237 
00238     void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
00239         inputs->push_back(game_logic::formula_input("hitpoints_left", game_logic::FORMULA_READ_ONLY));
00240         inputs->push_back(game_logic::formula_input("probability", game_logic::FORMULA_READ_ONLY));
00241         inputs->push_back(game_logic::formula_input("possible_status", game_logic::FORMULA_READ_ONLY));
00242     }
00243 public:
00244     outcome_callable(       const std::vector<variant>& hitLeft,
00245                     const std::vector<variant>& prob,
00246                     const std::vector<variant>& status)
00247       : hitLeft_(hitLeft), prob_(prob), status_(status)
00248     {
00249     }
00250 
00251     const std::vector<variant>& hitLeft() const { return hitLeft_; }
00252     const std::vector<variant>& prob() const { return prob_; }
00253     const std::vector<variant>& status() const { return status_; }
00254 };
00255 
00256 class calculate_outcome_function : public function_expression {
00257 public:
00258     calculate_outcome_function(const args_list& args, const formula_ai& ai)
00259       : function_expression("calculate_outcome", args, 3, 4), ai_(ai) {
00260     }
00261 
00262 private:
00263     variant execute(const formula_callable& variables) const {
00264         std::vector<variant> vars;
00265         int weapon;
00266         if (args().size() > 3) weapon = args()[3]->evaluate(variables).as_int();
00267         else weapon = -1;
00268         battle_context bc(ai_.get_info().map, ai_.get_info().teams, ai_.get_info().units,
00269             ai_.get_info().state, convert_variant<location_callable>(args()[1]->evaluate(variables))->loc(), 
00270             convert_variant<location_callable>(args()[2]->evaluate(variables))->loc(), weapon, -1, 1.0, NULL,
00271             &ai_.get_info().units.find(convert_variant<location_callable>(args()[0]->evaluate(variables))->loc())->second);
00272         std::vector<double> hp_dist = bc.get_attacker_combatant().hp_dist;
00273         std::vector<double>::iterator it = hp_dist.begin();
00274         int i = 0;
00275         std::vector<variant> hitLeft;
00276         std::vector<variant> prob;
00277         while (it != hp_dist.end()) {
00278             if (*it != 0) {
00279                 hitLeft.push_back(variant(i));
00280                 prob.push_back(variant(int(*it*10000)));
00281             }
00282             ++it;
00283             ++i;
00284         }
00285         std::vector<variant> status;
00286         if (bc.get_attacker_combatant().poisoned != 0)
00287             status.push_back(variant("Poisoned"));
00288         if (bc.get_attacker_combatant().slowed != 0)
00289             status.push_back(variant("Slowed"));
00290         if (bc.get_defender_stats().stones && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_attacker_stats().hp)
00291             status.push_back(variant("Stoned"));
00292         if (bc.get_defender_stats().plagues && hitLeft[0].as_int() == 0)
00293             status.push_back(variant("Zombiefied"));
00294         vars.push_back(variant(new outcome_callable(hitLeft, prob, status)));
00295         hitLeft.clear();
00296         prob.clear();       
00297         status.clear();
00298         hp_dist = bc.get_defender_combatant().hp_dist;
00299         it = hp_dist.begin();
00300         i = 0;
00301         while (it != hp_dist.end()) {
00302             if (*it != 0) {
00303                 hitLeft.push_back(variant(i));
00304                 prob.push_back(variant(int(*it*10000)));
00305             }
00306             ++it;
00307             ++i;
00308         }
00309         if (bc.get_defender_combatant().poisoned != 0)
00310             status.push_back(variant("Poisoned"));
00311         if (bc.get_defender_combatant().slowed != 0)
00312             status.push_back(variant("Slowed"));
00313         if (bc.get_attacker_stats().stones && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_attacker_stats().hp)
00314             status.push_back(variant("Stoned"));
00315         if (bc.get_attacker_stats().plagues && hitLeft[0].as_int() == 0)
00316             status.push_back(variant("Zombiefied"));
00317         vars.push_back(variant(new outcome_callable(hitLeft, prob, status)));
00318         return variant(&vars);
00319     }
00320 
00321     const formula_ai& ai_;
00322 };
00323 
00324 
00325 class outcomes_function : public function_expression {
00326 public:
00327     outcomes_function(const args_list& args, const formula_ai& ai)
00328       : function_expression("outcomes", args, 1, 1), ai_(ai) {
00329     }
00330 
00331 private:
00332     variant execute(const formula_callable& variables) const {
00333         variant attack = args()[0]->evaluate(variables);
00334         ai::attack_analysis* analysis = convert_variant<ai::attack_analysis>(attack);
00335         unit_map units_with_moves(ai_.get_info().units);
00336         for(size_t n = 0; n != analysis->movements.size(); ++n) {
00337             std::pair<gamemap::location,unit>* pair = units_with_moves.extract(analysis->movements[n].first);
00338             pair->first = analysis->movements[n].second;
00339             units_with_moves.add(pair);
00340         }
00341 
00342         std::vector<variant> vars;
00343         if(analysis->chance_to_kill > 0.0) {
00344             unit_map units(units_with_moves);
00345             units.erase(analysis->target);
00346             vars.push_back(variant(new position_callable(&units, static_cast<int>(analysis->chance_to_kill*100))));
00347 
00348         }
00349 
00350         if(analysis->chance_to_kill < 1.0) {
00351             unit_map units(units_with_moves);
00352             vars.push_back(variant(new position_callable(&units, static_cast<int>(100 - analysis->chance_to_kill*100))));
00353         }
00354 
00355         return variant(&vars);
00356     }
00357 
00358     const formula_ai& ai_;
00359 };
00360 
00361 class evaluate_for_position_function : public function_expression {
00362 public:
00363     evaluate_for_position_function(const args_list& args, formula_ai& ai)
00364       : function_expression("evaluate_for_position", args, 2, 2), ai_(ai) {
00365     }
00366 
00367 private:
00368     variant execute(const formula_callable& variables) const {
00369         variant position = args()[0]->evaluate(variables);
00370         position_callable* pos = convert_variant<position_callable>(position);
00371         position_callable::swapper swapper(ai_, *pos);
00372         return args()[1]->evaluate(variables);
00373     }
00374 
00375     formula_ai& ai_;
00376 };
00377 
00378 class recruit_callable : public formula_callable {
00379     gamemap::location loc_;
00380     std::string type_;
00381     variant get_value(const std::string& /*key*/) const { return variant(); }
00382 public:
00383     recruit_callable(const gamemap::location& loc, const std::string& type)
00384       : loc_(loc), type_(type)
00385     {}
00386 
00387     const gamemap::location& loc() const { return loc_; }
00388     const std::string& type() const { return type_; }
00389 };
00390 
00391 class evaluate_village_possession_function : public function_expression { //TODO
00392 public:
00393     evaluate_village_possession_function(const args_list& args, const formula_ai& ai)
00394       : function_expression("evaluate_village_possession", args, 0, 0), ai_(ai) {
00395     }
00396 
00397 private:
00398     variant execute(const formula_callable& variables) const {
00399 
00400         int villages_number = ai_.get_info().map.villages().size();
00401 
00402         //in case there are few villages, ignore this evaluation
00403         if (villages_number < 6)
00404             return variant(50);
00405 
00406         int allied_villages_number = 0;
00407         int opponents_villages_number = 0;
00408         int allies = 0;
00409 
00410         int teams_number = ai_.get_info().teams.size();
00411         for( int i = 0; i < teams_number; ++i)
00412         {
00413             if ( ai_.current_team().is_enemy(i+1) )
00414             {
00415                 opponents_villages_number += ai_.get_info().teams[i].villages().size();
00416             } else
00417             {
00418                 ++allies;
00419                 allied_villages_number += ai_.get_info().teams[i].villages().size();
00420             }
00421         }
00422 
00423         int controlled_villages = allied_villages_number + opponents_villages_number;
00424 
00425         //how important is each village, but multipled by 50
00426         int single_village_ratio = 0;
00427 
00428         if (villages_number != 0)
00429             single_village_ratio = 5000/villages_number;
00430 
00431         int village_state = allied_villages_number - opponents_villages_number;
00432 
00433         //up to this point, vilage_state contains state of the villages in AI's team
00434         //now check how the AI is doing compared to its allies
00435 
00436         int ai_villages = ai_.current_team().villages().size();
00437 
00438         int ai_desired_villages = allied_villages_number / allies;
00439 
00440         village_state += ai_villages - ai_desired_villages;
00441 
00442         //calculate evaluation
00443         int evaluation = 50 + (village_state * single_village_ratio)/100;
00444 
00445         if (evaluation > 100)
00446             evaluation = 100;
00447         if (evaluation < 0)
00448             evaluation = 0;
00449 
00450         return variant(evaluation);
00451     }
00452 
00453     const formula_ai& ai_;
00454 };
00455 
00456 class recruit_function : public function_expression {
00457 public:
00458     explicit recruit_function(const args_list& args)
00459       : function_expression("recruit", args, 1, 2)
00460     {}
00461 private:
00462     variant execute(const formula_callable& variables) const {
00463         const std::string type = args()[0]->evaluate(variables).as_string();
00464         gamemap::location loc;
00465         if(args().size() >= 2) {
00466             loc = convert_variant<location_callable>(args()[1]->evaluate(variables))->loc();
00467         }
00468 
00469         return variant(new recruit_callable(loc, type));
00470     }
00471 };
00472 
00473 class unit_chooser_function : public function_expression {
00474 public:
00475     explicit unit_chooser_function(const args_list& args, const formula_ai& ai)
00476       : function_expression("unit_chooser", args, 1, 1), ai_(ai)
00477     {}
00478 private:
00479     variant execute(const formula_callable& variables) const {
00480         const variant input = args()[0]->evaluate(variables);
00481 
00482         gamemap::location loc;
00483 
00484         if ( !input.is_map() || (input.num_elements() == 0))
00485         {
00486             return variant();
00487         }
00488 
00489         if (input.num_elements() == 1)
00490         {
00491             return variant(new recruit_callable(loc, input.get_keys()[0].as_string() )); 
00492         } else 
00493         { 
00494             std::map<std::string, int> current_units;
00495             std::map<std::string, int>::iterator unit_it;
00496             int unit_count = 0;
00497             
00498             for(unit_map::unit_iterator i = ai_.get_info().units.begin() ; i != ai_.get_info().units.end() ; ++i)
00499             {
00500                 if (i->second.side() == ai_.get_info().team_num)
00501                 {
00502                     std::string unit = i->second.type_id();
00503                     unit_it = current_units.find(unit);
00504                     if ( unit_it != current_units.end() )
00505                     {
00506                         unit_it->second++;
00507                     } else
00508                     {
00509                         current_units[ unit ] = 1;
00510                     }
00511                     unit_count++;
00512                 }
00513             }
00514 
00515             std::map<variant, variant> input_map = input.get_map();
00516 
00517             for(unit_it = current_units.begin() ; unit_it != current_units.end() ; ++unit_it)
00518             {           
00519                 unit_it->second = (unit_it->second * 100) / unit_count;
00520                 //std::cerr << unit_it->first << " " << unit_it->second << std::endl;
00521             }
00522 
00523             for(unit_it = current_units.begin(); unit_it != current_units.end(); ++unit_it)
00524             {
00525                 std::map<variant, variant>::iterator i = input_map.find( variant(unit_it->first) );
00526                 if ( i != input_map.end() )
00527                 {
00528                     if( unit_it->second >= i->second.as_int() )
00529                     {
00530                         input_map.erase(i);
00531                         if (input_map.size() == 0)
00532                             break;
00533                     } else
00534                     {
00535                         int number = i->second.as_int() - unit_it->second;
00536                         i->second = variant(number);
00537                     }
00538                 }
00539             }
00540 
00541             if (input_map.size() == 0)
00542             {
00543                 //std::cerr << "input_map became empty, calculating best posible recruit option using base input\n";
00544                 input_map = input.get_map();
00545             }
00546 
00547 
00548             if (input_map.size() == 1)
00549             {
00550                 return variant(new recruit_callable(loc, input_map.begin()->first.as_string() )); 
00551             } else
00552             {
00553                 int max = 0;
00554                 std::map<variant, variant>::iterator max_it = input_map.begin(); 
00555     
00556                 for(std::map<variant, variant>::iterator i = input_map.begin(); i != input_map.end(); ++i)
00557                 {
00558                     if (i->second.as_int() > max)
00559                     {
00560                         max = i->second.as_int();
00561                         max_it = i;
00562                     }
00563                 }
00564                 
00565                 return variant(new recruit_callable(loc, max_it->first.as_string() )); 
00566             }
00567 
00568         }
00569     }
00570 
00571     const formula_ai& ai_;
00572 };
00573 
00574 class move_function : public function_expression {
00575 public:
00576     explicit move_function(const args_list& args)
00577       : function_expression("move", args, 2, 2)
00578     {}
00579 private:
00580     variant execute(const formula_callable& variables) const {
00581         const gamemap::location src = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
00582         const gamemap::location dst = convert_variant<location_callable>(args()[1]->evaluate(variables))->loc();
00583         std::cerr << "move(): " << src << ", " << dst << ")\n";
00584         return variant(new move_callable(src, dst));
00585     }
00586 };
00587 
00588 class set_var_callable : public formula_callable {
00589     std::string key_;
00590     variant value_;
00591     variant get_value(const std::string& /*key*/) const { return variant(); }
00592 public:
00593     set_var_callable(const std::string& key, const variant& value)
00594       : key_(key), value_(value)
00595     {}
00596 
00597     const std::string& key() const { return key_; }
00598     variant value() const { return value_; }
00599 };
00600 
00601 class set_var_function : public function_expression {
00602 public:
00603     explicit set_var_function(const args_list& args)
00604       : function_expression("set_var", args, 2, 2)
00605     {}
00606 private:
00607     variant execute(const formula_callable& variables) const {
00608         return variant(new set_var_callable(args()[0]->evaluate(variables).as_string(), args()[1]->evaluate(variables)));
00609     }
00610 };
00611 
00612 class fallback_callable : public formula_callable {
00613     std::string key_;
00614     variant get_value(const std::string& /*key*/) const { return variant(); }
00615 public:
00616     explicit fallback_callable(const std::string& key) : key_(key) {
00617     }
00618 
00619     const std::string& key() const { return key_; }
00620 };
00621 
00622 class fallback_function : public function_expression {
00623 public:
00624     explicit fallback_function(const args_list& args)
00625       : function_expression("fallback", args, 1, 1)
00626     {}
00627 private:
00628     variant execute(const formula_callable& variables) const {
00629         return variant(new fallback_callable(args()[0]->evaluate(variables).as_string()));
00630     }
00631 };
00632 
00633 class attack_callable : public formula_callable {
00634     gamemap::location move_from_, src_, dst_;
00635     battle_context bc_;
00636     variant get_value(const std::string& key) const {
00637         if(key == "attacker") {
00638             return variant(new location_callable(src_));
00639         } else if(key == "defender") {
00640             return variant(new location_callable(dst_));
00641         } else if(key == "move_from") {
00642             return variant(new location_callable(move_from_));
00643         } else {
00644             return variant();
00645         }
00646     }
00647 
00648     void get_inputs(std::vector<game_logic::formula_input>* inputs) const {
00649         inputs->push_back(game_logic::formula_input("attacker", game_logic::FORMULA_READ_ONLY));
00650         inputs->push_back(game_logic::formula_input("defender", game_logic::FORMULA_READ_ONLY));
00651         inputs->push_back(game_logic::formula_input("move_from", game_logic::FORMULA_READ_ONLY));
00652     }
00653 public:
00654     attack_callable(const formula_ai& ai,
00655                     const gamemap::location& move_from,
00656                     const gamemap::location& src, const gamemap::location& dst,
00657                     int weapon)
00658       : move_from_(move_from), src_(src), dst_(dst),
00659         bc_(ai.get_info().map, ai.get_info().teams, ai.get_info().units,
00660             ai.get_info().state, src, dst, weapon, -1, 1.0, NULL,
00661             &ai.get_info().units.find(move_from)->second)
00662     {
00663     }
00664 
00665     const gamemap::location& move_from() const { return move_from_; }
00666     const gamemap::location& src() const { return src_; }
00667     const gamemap::location& dst() const { return dst_; }
00668     int weapon() const { return bc_.get_attacker_stats().attack_num; }
00669     int defender_weapon() const { return bc_.get_defender_stats().attack_num; }
00670 };
00671 
00672 class attack_function : public function_expression {
00673 public:
00674     explicit attack_function(const args_list& args, const formula_ai& ai)
00675       : function_expression("attack", args, 3, 4),
00676         ai_(ai)
00677     {}
00678 private:
00679     variant execute(const formula_callable& variables) const {
00680         const gamemap::location move_from = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
00681         const gamemap::location src = convert_variant<location_callable>(args()[1]->evaluate(variables))->loc();
00682         const gamemap::location dst = convert_variant<location_callable>(args()[2]->evaluate(variables))->loc();
00683         const int weapon = args().size() == 4 ? args()[3]->evaluate(variables).as_int() : -1;
00684         if(ai_.get_info().units.count(move_from) == 0 || ai_.get_info().units.count(dst) == 0) {
00685             std::cerr << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst << "\n";
00686             return variant();
00687         }
00688         return variant(new attack_callable(ai_, move_from, src, dst, weapon));
00689     }
00690 
00691     const formula_ai& ai_;
00692 };
00693 
00694 class is_village_function : public function_expression {
00695 public:
00696     explicit is_village_function(const args_list& args)
00697       : function_expression("is_village", args, 2, 3)
00698     {}
00699 private:
00700     variant execute(const formula_callable& variables) const {
00701         const gamemap& m = convert_variant<gamemap_callable>(args()[0]->evaluate(variables))->get_gamemap();
00702 
00703         gamemap::location loc;
00704         if(args().size() == 2) {
00705             loc = convert_variant<location_callable>(args()[1]->evaluate(variables))->loc();
00706         } else {
00707             loc = gamemap::location( args()[1]->evaluate(variables).as_int() - 1,
00708                                     args()[2]->evaluate(variables).as_int() - 1 );
00709         }
00710         return variant(m.is_village(loc));
00711     }
00712 };
00713 
00714 class unit_at_function : public function_expression {
00715 public:
00716     unit_at_function(const args_list& args, const formula_ai& ai_object)
00717       : function_expression("unit_at", args, 1, 1), ai_(ai_object)
00718     {}
00719 private:
00720     variant execute(const formula_callable& variables) const {
00721         const location_callable* loc = convert_variant<location_callable>(args()[0]->evaluate(variables));
00722         const unit_map::const_iterator i = ai_.get_info().units.find(loc->loc());
00723         if(i != ai_.get_info().units.end()) {
00724             return variant(new unit_callable(*i));
00725         } else {
00726             return variant();
00727         }
00728     }
00729 
00730     const formula_ai& ai_;
00731 };
00732 
00733 class unit_moves_function : public function_expression {
00734 public:
00735     unit_moves_function(const args_list& args, const formula_ai& ai_object)
00736       : function_expression("unit_moves", args, 1, 1), ai_(ai_object)
00737     {}
00738 private:
00739     variant execute(const formula_callable& variables) const {
00740         variant res = args()[0]->evaluate(variables);
00741         std::vector<variant> vars;
00742         if(res.is_null()) {
00743             return variant(&vars);
00744         }
00745 
00746         const gamemap::location& loc = convert_variant<location_callable>(res)->loc();
00747         const formula_ai::move_map& srcdst = ai_.srcdst();
00748         typedef formula_ai::move_map::const_iterator Itor;
00749         std::pair<Itor,Itor> range = srcdst.equal_range(loc);
00750 
00751         for(Itor i = range.first; i != range.second; ++i) {
00752             vars.push_back(variant(new location_callable(i->second)));
00753         }
00754 
00755         return variant(&vars);
00756     }
00757 
00758     const formula_ai& ai_;
00759 };
00760 
00761 class units_can_reach_function : public function_expression {
00762 public:
00763     units_can_reach_function(const args_list& args, const formula_ai& ai_object)
00764       : function_expression("units_can_reach", args, 2, 2), ai_(ai_object)
00765     {}
00766 private:
00767     variant execute(const formula_callable& variables) const {
00768         std::vector<variant> vars;
00769         variant dstsrc_var = args()[0]->evaluate(variables);
00770         const ai::move_map& dstsrc = convert_variant<move_map_callable>(dstsrc_var)->dstsrc();
00771         std::pair<ai::move_map::const_iterator,ai::move_map::const_iterator> range =
00772             dstsrc.equal_range(convert_variant<location_callable>(args()[1]->evaluate(variables))->loc());
00773         while(range.first != range.second) {
00774             unit_map::const_iterator un = ai_.get_info().units.find(range.first->second);
00775             assert(un != ai_.get_info().units.end());
00776             vars.push_back(variant(new unit_callable(*un)));
00777             ++range.first;
00778         }
00779 
00780         return variant(&vars);
00781     }
00782 
00783     const formula_ai& ai_;
00784 };
00785 
00786 class defense_on_function : public function_expression {
00787 public:
00788     defense_on_function(const args_list& args, const formula_ai& ai_object)
00789       : function_expression("defense_on", args, 2, 2), ai_(ai_object)
00790     {}
00791 private:
00792     variant execute(const formula_callable& variables) const {
00793         variant u = args()[0]->evaluate(variables);
00794         variant loc_var = args()[1]->evaluate(variables);
00795         if(u.is_null() || loc_var.is_null()) {
00796             return variant();
00797         }
00798 
00799         const unit& un = convert_variant<unit_callable>(u)->get_unit();
00800         const gamemap::location& loc = convert_variant<location_callable>(loc_var)->loc();
00801         if(!ai_.get_info().map.on_board(loc)) {
00802             return variant();
00803         }
00804 
00805         return variant(100 - un.defense_modifier(ai_.get_info().map[loc]));
00806     }
00807 
00808     const formula_ai& ai_;
00809 };
00810 
00811 class chance_to_hit_function : public function_expression {
00812 public:
00813     chance_to_hit_function(const args_list& args, const formula_ai& ai_object)
00814       : function_expression("chance_to_hit", args, 2, 2), ai_(ai_object)
00815     {}
00816 private:
00817     variant execute(const formula_callable& variables) const {
00818         variant u = args()[0]->evaluate(variables);
00819         variant loc_var = args()[1]->evaluate(variables);
00820         if(u.is_null() || loc_var.is_null()) {
00821             return variant();
00822         }
00823 
00824         const unit& un = convert_variant<unit_callable>(u)->get_unit();
00825         const gamemap::location& loc = convert_variant<location_callable>(loc_var)->loc();
00826         if(!ai_.get_info().map.on_board(loc)) {
00827             return variant();
00828         }
00829 
00830         return variant(un.defense_modifier(ai_.get_info().map[loc]));
00831     }
00832 
00833     const formula_ai& ai_;
00834 };
00835 
00836 class max_possible_damage_function : public function_expression {
00837 public:
00838     max_possible_damage_function(const args_list& args, const formula_ai& ai_object)
00839       : function_expression("max_possible_damage", args, 2, 2), ai_(ai_object)
00840     {}
00841 private:
00842     variant execute(const formula_callable& variables) const {
00843         variant u1 = args()[0]->evaluate(variables);
00844         variant u2 = args()[1]->evaluate(variables);
00845         if(u1.is_null() || u2.is_null()) {
00846             return variant();
00847         }
00848 
00849         const unit& attacker = convert_variant<unit_callable>(u1)->get_unit();
00850         const unit& defender = convert_variant<unit_callable>(u2)->get_unit();
00851         const std::vector<attack_type>& attacks = attacker.attacks();
00852 
00853         int best = 0;
00854         for(std::vector<attack_type>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
00855             const int dmg = ((defender.damage_from(*i, false, gamemap::location()) * i->damage())/100) * i->num_attacks();
00856             if(dmg > best) {
00857                 best = dmg;
00858             }
00859         }
00860 
00861         return variant(best);
00862     }
00863 
00864     const formula_ai& ai_;
00865 };
00866 
00867 class ai_function_symbol_table : public function_symbol_table {
00868     formula_ai& ai_;
00869     std::set<std::string> move_functions;
00870 
00871     expression_ptr create_function(const std::string& fn,
00872                                    const std::vector<expression_ptr>& args) const {
00873         if(fn == "outcomes") {
00874             return expression_ptr(new outcomes_function(args, ai_));
00875         } else if(fn == "evaluate_for_position") {
00876             return expression_ptr(new evaluate_for_position_function(args, ai_));
00877         } else if(fn == "evaluate_village_possession") {
00878             return expression_ptr(new evaluate_village_possession_function(args, ai_));
00879         } else if(fn == "move") {
00880             return expression_ptr(new move_function(args));
00881         } else if(fn == "attack") {
00882             return expression_ptr(new attack_function(args, ai_));
00883         } else if(fn == "recruit") {
00884             return expression_ptr(new recruit_function(args));
00885         } else if(fn == "unit_chooser") {
00886             return expression_ptr(new unit_chooser_function(args, ai_));
00887         } else if(fn == "is_village") {
00888             return expression_ptr(new is_village_function(args));
00889         } else if(fn == "unit_at") {
00890             return expression_ptr(new unit_at_function(args, ai_));
00891         } else if(fn == "unit_moves") {
00892             return expression_ptr(new unit_moves_function(args, ai_));
00893         } else if(fn == "set_var") {
00894             return expression_ptr(new set_var_function(args));
00895         } else if(fn == "fallback") {
00896             return expression_ptr(new fallback_function(args));
00897         } else if(fn == "units_can_reach") {
00898             return expression_ptr(new units_can_reach_function(args, ai_));
00899         } else if(fn == "defense_on") {
00900             return expression_ptr(new defense_on_function(args, ai_));
00901         } else if(fn == "chance_to_hit") {
00902             return expression_ptr(new chance_to_hit_function(args, ai_));
00903         } else if(fn == "max_possible_damage") {
00904             return expression_ptr(new max_possible_damage_function(args, ai_));
00905         } else if(fn == "distance_to_nearest_unowned_village") {
00906             return expression_ptr(new distance_to_nearest_unowned_village_function(args, ai_));
00907         } else if(fn == "nearest_keep") {
00908             return expression_ptr(new nearest_keep_function(args, ai_));
00909         } else if(fn == "nearest_loc") {
00910             return expression_ptr(new nearest_loc_function(args, ai_));
00911         } else if(fn == "close_enemies") {
00912             return expression_ptr(new close_enemies_function(args, ai_));
00913         } else if(fn == "calculate_outcome") {
00914             return expression_ptr(new calculate_outcome_function(args, ai_));
00915         } else if(fn == "distance_between") {
00916             return expression_ptr(new distance_between_function(args));
00917         } else {
00918             return function_symbol_table::create_function(fn, args);
00919         }
00920     }
00921     
00922 public:
00923 
00924     void add_formula_function(const std::string& name, const_formula_ptr formula, 
00925                               const_formula_ptr precondition, const std::vector<std::string>& args)
00926     {
00927         if(boost::regex_search(name,boost::regex("^ai_move"))) {
00928             move_functions.insert(name);
00929         }
00930         function_symbol_table::add_formula_function(name, formula, precondition, args);
00931     }
00932 
00933 
00934     explicit ai_function_symbol_table(formula_ai& ai) : ai_(ai)
00935     {}
00936 };
00937 }
00938 
00939 formula_ai::formula_ai(info& i) : ai(i), move_maps_valid_(false)
00940 {
00941     //make sure we don't run out of refcount
00942     add_ref();
00943     vars_.add_ref();
00944 }
00945 
00946 void formula_ai::play_turn()
00947 {
00948     ai_function_symbol_table function_table(*this);
00949     game_logic::candidate_move_map candidate_moves;
00950 
00951     const config& ai_param = current_team().ai_parameters();
00952     config::const_child_itors team_formula = ai_param.child_range("team_formula");
00953     if(team_formula.first != team_formula.second) {
00954         std::string formula_string = (**team_formula.first)["rulebase"];
00955         move_formula_ = game_logic::formula::create_optional_formula(formula_string, &function_table);
00956     } else {
00957     config::const_child_itors functions = ai_param.child_range("function");
00958     for(config::const_child_iterator i = functions.first; i != functions.second; ++i) {
00959         const t_string& name = (**i)["name"];
00960         const t_string& inputs = (**i)["inputs"];
00961         const t_string& formula_str = (**i)["formula"];
00962 
00963         std::vector<std::string> args = utils::split(inputs);
00964         function_table.add_formula_function(name, game_logic::const_formula_ptr(new game_logic::formula(formula_str, &function_table)), game_logic::formula::create_optional_formula((**i)["precondition"], &function_table), args);
00965     }
00966 
00967     recruit_formula_ = game_logic::formula::create_optional_formula(current_team().ai_parameters()["recruit"], &function_table);
00968     move_formula_ = game_logic::formula::create_optional_formula(current_team().ai_parameters()["move"], &function_table);
00969     }
00970     //execute units formulas first
00971 
00972     for(unit_map::unit_iterator i = units_.begin() ; i != units_.end() ; ++i)
00973     {
00974         std::vector<game_logic::const_formula_ptr> unit_candidate_moves; 
00975         unit_candidate_moves.push_back(move_formula_);
00976         if ( (i->second.side() == get_info().team_num) && i->second.has_formula() )
00977         {
00978             game_logic::const_formula_ptr formula(new game_logic::formula(i->second.get_formula(), &function_table));
00979             game_logic::map_formula_callable callable(this);
00980             unit_candidate_moves.push_back(formula);
00981             callable.add_ref();
00982             callable.add("me", variant(new unit_callable(*i)));
00983             make_move(formula, callable);
00984         }
00985         candidate_moves.insert(std::pair<const std::string, std::vector<game_logic::const_formula_ptr> >
00986                     (i->second.underlying_id(), unit_candidate_moves));
00987     }
00988 
00989     game_logic::map_formula_callable callable(this);
00990     callable.add_ref();
00991     while(make_move(move_formula_,callable)) {
00992     }
00993 }
00994 
00995 std::string formula_ai::evaluate(const std::string& formula_str)
00996 {
00997     ai_function_symbol_table function_table(*this);
00998     const config& ai_param = current_team().ai_parameters();
00999     config::const_child_itors functions = ai_param.child_range("function");
01000     for(config::const_child_iterator i = functions.first; i != functions.second; ++i) {
01001         const t_string& name = (**i)["name"];
01002         const t_string& inputs = (**i)["inputs"];
01003         const t_string& formula_str = (**i)["formula"];
01004         std::vector<std::string> args = utils::split(inputs);
01005         function_table.add_formula_function(name, game_logic::const_formula_ptr(new game_logic::formula(formula_str, &function_table)), game_logic::formula::create_optional_formula((**i)["precondition"], &function_table), args);
01006     }
01007 
01008     game_logic::formula f(formula_str, &function_table);
01009 
01010     game_logic::map_formula_callable callable(this);
01011     callable.add_ref();
01012 
01013     const variant v = f.execute(callable);
01014 
01015     if ( execute_variant(v, true ) )
01016         return "Made move: " + v.to_debug_string();
01017 
01018     return v.to_debug_string();
01019 }
01020 
01021 void formula_ai::swap_move_map(move_map_backup& backup)
01022 {
01023     std::swap(move_maps_valid_, backup.move_maps_valid);
01024     std::swap(backup.attacks_cache, attacks_cache_);
01025     backup.move_maps_valid = move_maps_valid_;
01026     backup.srcdst.swap(srcdst_);
01027     backup.dstsrc.swap(dstsrc_);
01028     backup.full_srcdst.swap(full_srcdst_);
01029     backup.full_dstsrc.swap(full_dstsrc_);
01030     backup.enemy_srcdst.swap(enemy_srcdst_);
01031     backup.enemy_dstsrc.swap(enemy_dstsrc_);
01032 }
01033 
01034 void formula_ai::prepare_move() const
01035 {
01036     if(move_maps_valid_) {
01037         return;
01038     }
01039 
01040     possible_moves_.clear();
01041     srcdst_.clear();
01042     dstsrc_.clear();
01043 
01044     calculate_possible_moves(possible_moves_, srcdst_, dstsrc_, false);
01045 
01046     full_srcdst_.clear();
01047     full_dstsrc_.clear();
01048 
01049     std::map<location,paths> possible_moves_dummy;
01050     calculate_possible_moves(possible_moves_dummy, full_srcdst_, full_dstsrc_, false, true);
01051 
01052     enemy_srcdst_.clear();
01053     enemy_dstsrc_.clear();
01054     possible_moves_dummy.clear();
01055     calculate_possible_moves(possible_moves_dummy, enemy_srcdst_, enemy_dstsrc_, true);
01056 
01057     attacks_cache_ = variant();
01058     move_maps_valid_ = true;
01059 }
01060 
01061 bool formula_ai::make_move(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables)
01062 {
01063     if(!formula_) {
01064         ai_interface* fallback = create_ai("", get_info());
01065         fallback->play_turn();
01066         return false;
01067     }
01068 
01069     move_maps_valid_ = false;
01070 
01071     std::cerr << "do move...\n";
01072     const variant var = formula_->execute(variables);
01073 
01074     return execute_variant(var);
01075 }
01076 
01077 //commandline=true when we evaluate formula from commandline, false otherwise (default)
01078 bool formula_ai::execute_variant(const variant& var, bool commandline)
01079 {
01080     std::vector<variant> vars;
01081     if(var.is_list()) {
01082         for(size_t n = 0; n != var.num_elements(); ++n) {
01083             vars.push_back(var[n]);
01084         }
01085     } else {
01086         vars.push_back(var);
01087     }
01088 
01089     bool made_move = false;
01090 
01091     for(std::vector<variant>::const_iterator i = vars.begin(); i != vars.end(); ++i) {
01092         if(i->is_null()) {
01093             continue;
01094         }
01095 
01096         const move_callable* move = try_convert_variant<move_callable>(*i);
01097         const attack_callable* attack = try_convert_variant<attack_callable>(*i);
01098         const ai::attack_analysis* attack_analysis = try_convert_variant<ai::attack_analysis>(*i);
01099         const recruit_callable* recruit_command = try_convert_variant<recruit_callable>(*i);
01100         const set_var_callable* set_var_command = try_convert_variant<set_var_callable>(*i);
01101         const fallback_callable* fallback_command = try_convert_variant<fallback_callable>(*i);
01102 
01103         prepare_move();
01104         if(move) {
01105             std::cerr << "MOVE: " << move->src().x << "," << move->src().y << " -> " << move->dst().x << "," << move->dst().y << "\n";
01106             unit_map::iterator i = units_.find(move->src());
01107             if( (possible_moves_.count(move->src()) > 0) && (i->second.movement_left() != 0) ) {
01108                 move_unit(move->src(), move->dst(), possible_moves_);
01109                 made_move = true;
01110             }
01111         } else if(attack) {
01112             if(get_info().units.count(attack->dst()) == 0) {
01113                 //this is a legitimate situation; someone might send a series of units in
01114                 //to attack, but if the defender dies in the middle, we'll save the unit
01115                 //ordered to move so it can get a different command.
01116                 continue;
01117             }
01118 
01119             if(attack->move_from() != attack->src()) {
01120                 move_unit(attack->move_from(), attack->src(), possible_moves_);
01121             }
01122             std::cerr << "ATTACK: " << attack->src() << " -> " << attack->dst() << " " << attack->weapon() << "\n";
01123             attack_enemy(attack->src(), attack->dst(), attack->weapon(), attack->defender_weapon());
01124             made_move = true;
01125         } else if(attack_analysis) {
01126             //If we get an attack analysis back we will do the first attack.
01127             //Then the AI can get run again and re-choose.
01128             assert(attack_analysis->movements.empty() == false);
01129 
01130             //make sure that unit which has to attack is at given position and is able to attack
01131             unit_map::const_iterator unit = units_.find(attack_analysis->movements.front().first);
01132             if ( ( unit == units_.end() ) || (unit->second.attacks_left() == 0) )
01133                 continue;
01134 
01135             const gamemap::location& src = attack_analysis->movements.front().second;
01136             const gamemap::location& dst = attack_analysis->target;
01137 
01138             //now check if location to which we want to move is still unoccupied
01139             unit = units_.find(src);
01140             if ( unit != units_.end() )
01141                 continue;
01142 
01143             //now check if target is still valid
01144             unit = units_.find(dst);
01145             if ( unit == units_.end() )
01146                 continue;
01147 
01148             move_unit(attack_analysis->movements.front().first,
01149                       attack_analysis->movements.front().second,
01150                       possible_moves_);
01151 
01152             if(get_info().units.count(src)) {
01153                 battle_context bc(get_info().map, get_info().teams,
01154                                   get_info().units, get_info().state,
01155                                   src, dst, -1, -1, 1.0, NULL,
01156                                   &get_info().units.find(src)->second);
01157                 attack_enemy(attack_analysis->movements.front().second,
01158                              attack_analysis->target,
01159                              bc.get_attacker_stats().attack_num,
01160                              bc.get_defender_stats().attack_num);
01161             }
01162             made_move = true;
01163         } else if(recruit_command) {
01164             std::cerr << "RECRUIT: '" << recruit_command->type() << "'\n";
01165             if(recruit(recruit_command->type(), recruit_command->loc())) {
01166                 made_move = true;
01167             }
01168         } else if(set_var_command) {
01169             std::cerr << "setting var: " << set_var_command->key() << " -> " << set_var_command->value().to_debug_string() << "\n";
01170             vars_.add(set_var_command->key(), set_var_command->value());
01171             made_move = true;
01172         } else if(i->is_string() && i->as_string() == "recruit") {
01173             do_recruitment();
01174             made_move = true;
01175         } else if(i->is_string() && i->as_string() == "end_turn") {
01176             return false;
01177         } else if(fallback_command) {
01178             ai_interface* fallback = create_ai(fallback_command->key(), get_info());
01179             if(fallback) {
01180                 fallback->play_turn();
01181             }
01182             return false;
01183         } else {
01184             //this information is unneded when evaluating formulas form commandline
01185             if (!commandline)
01186                 std::cerr << "UNRECOGNIZED MOVE: " << i->to_debug_string() << "\n";
01187         }
01188     }
01189 
01190     return made_move;
01191 }
01192 
01193 
01194 void formula_ai::do_recruitment()
01195 {
01196     if(!recruit_formula_) {
01197         return;
01198     }
01199 
01200     variant var = recruit_formula_->execute(*this);
01201     std::vector<variant> vars;
01202     if(var.is_list()) {
01203         for(size_t n = 0; n != var.num_elements(); ++n) {
01204             vars.push_back(var[n]);
01205         }
01206     } else {
01207         vars.push_back(var);
01208     }
01209 
01210     for(std::vector<variant>::const_iterator i = vars.begin(); i != vars.end(); ++i) {
01211         if(!i->is_string()) {
01212             return;
01213         }
01214 
01215         if(!recruit(i->as_string())) {
01216             return;
01217         }
01218     }
01219 
01220     do_recruitment();
01221 }
01222 
01223 variant formula_ai::get_value(const std::string& key) const
01224 {
01225     if(key == "attacks") {
01226         prepare_move();
01227         if(attacks_cache_.is_null() == false) {
01228             return attacks_cache_;
01229         }
01230 
01231         std::vector<attack_analysis> attacks = const_cast<formula_ai*>(this)->analyze_targets(srcdst_, dstsrc_, enemy_srcdst_, enemy_dstsrc_);
01232         std::vector<variant> vars;
01233         for(std::vector<attack_analysis>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
01234             vars.push_back(variant(new attack_analysis(*i)));
01235         }
01236 
01237         attacks_cache_ = variant(&vars);
01238         return attacks_cache_;
01239     } else if(key == "my_moves") {
01240         prepare_move();
01241         return variant(new move_map_callable(srcdst_, dstsrc_));
01242     } else if(key == "enemy_moves") {
01243         prepare_move();
01244         return variant(new move_map_callable(enemy_srcdst_, enemy_dstsrc_));
01245     } else if(key == "my_leader") {
01246         unit_map::const_iterator i = team_leader(get_info().team_num, get_info().units);
01247         if(i == get_info().units.end()) {
01248             return variant();
01249         }
01250 
01251         return variant(new unit_callable(*i));
01252     } else if(key == "vars") {
01253         return variant(&vars_);
01254     } else if(key == "keeps") {
01255         return get_keeps();
01256     }
01257 
01258     return ai_interface::get_value(key);
01259 }
01260 
01261 void formula_ai::get_inputs(std::vector<formula_input>* inputs) const
01262 {
01263     using game_logic::FORMULA_READ_ONLY;
01264     inputs->push_back(game_logic::formula_input("attacks", FORMULA_READ_ONLY));
01265     inputs->push_back(game_logic::formula_input("my_moves", FORMULA_READ_ONLY));
01266     inputs->push_back(game_logic::formula_input("enemy_moves", FORMULA_READ_ONLY));
01267     inputs->push_back(game_logic::formula_input("my_leader", FORMULA_READ_ONLY));
01268     inputs->push_back(game_logic::formula_input("vars", FORMULA_READ_ONLY));
01269     inputs->push_back(game_logic::formula_input("keeps", FORMULA_READ_ONLY));
01270 
01271     ai_interface::get_inputs(inputs);
01272 }
01273 
01274 variant formula_ai::get_keeps() const
01275 {
01276     if(keeps_cache_.is_null()) {
01277         std::vector<variant> vars;
01278         for(size_t x = 0; x != size_t(get_info().map.w()); ++x) {
01279             for(size_t y = 0; y != size_t(get_info().map.h()); ++y) {
01280                 const gamemap::location loc(x,y);
01281                 if(get_info().map.is_keep(loc)) {
01282                     gamemap::location adj[6];
01283                     get_adjacent_tiles(loc,adj);
01284                     for(size_t n = 0; n != 6; ++n) {
01285                         if(get_info().map.is_castle(adj[n])) {
01286                             vars.push_back(variant(new location_callable(loc)));
01287                             break;
01288                         }
01289                     }
01290                 }
01291             }
01292         }
01293         keeps_cache_ = variant(&vars);
01294     }
01295 
01296     return keeps_cache_;
01297 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs