00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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& ) 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 {
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
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
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
00434
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
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
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
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& ) 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& ) 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
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
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
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
01114
01115
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
01127
01128 assert(attack_analysis->movements.empty() == false);
01129
01130
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
01139 unit = units_.find(src);
01140 if ( unit != units_.end() )
01141 continue;
01142
01143
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
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 }