The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
callable_objects.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2017 by Bartosz Waresiak <dragonking@o2.pl>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "ai/formula/ai.hpp"
16 #include "attack_prediction.hpp"
17 #include "game_board.hpp"
20 #include "resources.hpp"
21 #include "map/map.hpp"
22 #include "ai/game_info.hpp"
23 #include "ai/actions.hpp"
25 #include "log.hpp"
26 #include "menu_events.hpp" // for fallback_ai_to_human_exception
27 
28 static lg::log_domain log_formula_ai("ai/engine/fai");
29 #define DBG_AI LOG_STREAM(debug, log_formula_ai)
30 #define LOG_AI LOG_STREAM(info, log_formula_ai)
31 #define WRN_AI LOG_STREAM(warn, log_formula_ai)
32 #define ERR_AI LOG_STREAM(err, log_formula_ai)
33 
34 namespace ai {
35 
37  auto fai = std::dynamic_pointer_cast<const formula_ai>(for_fai);
38  assert(fai != nullptr);
39  return *std::const_pointer_cast<formula_ai>(fai)->ai_ptr_;
40 }
41 
42 }
43 
44 namespace wfl {
45  using namespace ai;
46 
48 {
49  if(key == "moves") {
50  std::vector<variant> vars;
51  for(move_map::const_iterator i = srcdst_.begin(); i != srcdst_.end(); ++i) {
52  if( i->first == i->second || units_.count(i->second) == 0) {
53  auto item = std::make_shared<move_callable>(i->first, i->second);
54  vars.emplace_back(item);
55  }
56  }
57 
58  return variant(vars);
59  } else if(key == "has_moves") {
60  return variant(!srcdst_.empty());
61  } else {
62  return variant();
63  }
64 }
65 
67 {
68  add_input(inputs, "moves");
69 }
70 
71 int move_callable::do_compare(const formula_callable* callable) const
72 {
73  const move_callable* mv_callable = dynamic_cast<const move_callable*>(callable);
74  if(mv_callable == nullptr) {
75  return formula_callable::do_compare(callable);
76  }
77 
78  const map_location& other_src = mv_callable->src_;
79  const map_location& other_dst = mv_callable->dst_;
80 
81  if (int cmp = src_.do_compare(other_src)) {
82  return cmp;
83  }
84 
85  return dst_.do_compare(other_dst);
86 }
87 
90  move_result_ptr move_result = ai.execute_move_action(src_, dst_, true);
91 
92  if(!move_result->is_ok()) {
93  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'move' formula function\n" << std::endl;
94  return variant(std::make_shared<safe_call_result>(fake_ptr(), move_result->get_status(), move_result->get_unit_location()));
95  }
96 
97  return variant(move_result->is_gamestate_changed());
98 }
99 
101 {
102  const move_partial_callable* mv_callable = dynamic_cast<const move_partial_callable*>(callable);
103  if(mv_callable == nullptr) {
104  return formula_callable::do_compare(callable);
105  }
106 
107  const map_location& other_src = mv_callable->src_;
108  const map_location& other_dst = mv_callable->dst_;
109 
110  if (int cmp = src_.do_compare(other_src)) {
111  return cmp;
112  }
113 
114  return dst_.do_compare(other_dst);
115 }
116 
119  move_result_ptr move_result = ai.execute_move_action(src_, dst_, false);
120 
121  if(!move_result->is_ok()) {
122  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'move_partial' formula function\n" << std::endl;
123  return variant(std::make_shared<safe_call_result>(fake_ptr(), move_result->get_status(), move_result->get_unit_location()));
124  }
125 
126  return variant(move_result->is_gamestate_changed());
127 }
128 
130  if(key == "chance") {
131  return variant(chance_);
132  } else {
133  return variant();
134  }
135 }
136 
138  add_input(inputs, "chance");
139 }
140 
142  if(key == "hitpoints_left") {
143  return variant(hitLeft_);
144  } else if(key == "probability") {
145  return variant(prob_);
146  } else if(key == "possible_status") {
147  return variant(status_);
148  } else {
149  return variant();
150  }
151 }
152 
154  add_input(inputs, "hitpoints_left");
155  add_input(inputs, "probability");
156  add_input(inputs, "possible_status");
157 }
158 
159 
161  const map_location& src, const map_location& dst, int weapon)
162  : move_from_(move_from), src_(src), dst_(dst),
163  bc_(resources::gameboard->units(), src, dst, weapon, -1, 1.0, nullptr,
164  &*resources::gameboard->units().find(move_from))
165 {
166  type_ = ATTACK_C;
167 }
168 
169 
171  if(key == "attack_from") {
172  return variant(std::make_shared<location_callable>(src_));
173  } else if(key == "defender") {
174  return variant(std::make_shared<location_callable>(dst_));
175  } else if(key == "move_from") {
176  return variant(std::make_shared<location_callable>(move_from_));
177  } else {
178  return variant();
179  }
180 }
181 
183  add_input(inputs, "attack_from");
184  add_input(inputs, "defender");
185  add_input(inputs, "move_from");
186 }
187 
189  const {
190  const attack_callable* a_callable = dynamic_cast<const attack_callable*>(callable);
191  if(a_callable == nullptr) {
192  return formula_callable::do_compare(callable);
193  }
194 
195  const map_location& other_from = a_callable->move_from();
196 
197  if (int cmp = move_from_.do_compare(other_from)) {
198  return cmp;
199  }
200  const map_location& other_src = a_callable->src();
201  if (int cmp = src_.do_compare(other_src)) {
202  return cmp;
203  }
204  const map_location& other_dst = a_callable->dst();
205  if (int cmp = dst_.do_compare(other_dst)) {
206  return cmp;
207  }
208  const int other_weapon = a_callable->weapon();
209  if (int cmp = (this->weapon() - other_weapon)) {
210  return cmp;
211  }
212  const int other_def_weapon = a_callable->defender_weapon();
213  return this->defender_weapon() - other_def_weapon;
214 }
215 
218  bool gamestate_changed = false;
220 
221  if(move_from_ != src_) {
222  move_result = ai.execute_move_action(move_from_, src_, false);
223  gamestate_changed |= move_result->is_gamestate_changed();
224 
225  if(!move_result->is_ok()) {
226  //move part failed
227  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'attack' formula function\n" << std::endl;
228  return variant(std::make_shared<safe_call_result>(fake_ptr(), move_result->get_status(), move_result->get_unit_location()));
229  }
230  }
231 
232  if(!move_result || move_result->is_ok()) {
233  //if move wasn't done at all or was done successfully
235  gamestate_changed |= attack_result->is_gamestate_changed();
236  if(!attack_result->is_ok()) {
237  //attack failed
238  LOG_AI << "ERROR #" << attack_result->get_status() << " while executing 'attack' formula function\n" << std::endl;
239  return variant(std::make_shared<safe_call_result>(fake_ptr(), attack_result->get_status()));
240  }
241  }
242 
243  return variant(gamestate_changed);
244 }
245 
247  if(key == "attacks") {
248  std::vector<variant> vars;
249  for(move_map::const_iterator i = ai_.get_srcdst().begin(); i != ai_.get_srcdst().end(); ++i) {
250  /* for each possible move check all adjacent tiles for enemies */
251  if(units_.count(i->second) == 0) {
252  collect_possible_attacks(vars, i->first, i->second);
253  }
254  }
255  /* special case, when unit moved toward enemy and can only attack */
257  if (i->side() == ai_.get_side() && i->attacks_left() > 0) {
258  collect_possible_attacks(vars, i->get_location(), i->get_location());
259  }
260  }
261  return variant(vars);
262  } else {
263  return variant();
264  }
265 }
266 
268  add_input(inputs, "attacks");
269 }
270 
271 /* add to vars all attacks on enemy units around <attack_position> tile. attacker_location is tile where unit is currently standing. It's moved to attack_position first and then performs attack.*/
272 void attack_map_callable::collect_possible_attacks(std::vector<variant>& vars, map_location attacker_location, map_location attack_position) const {
273  map_location adj[6];
274  get_adjacent_tiles(attack_position, adj);
275 
276  for(int n = 0; n != 6; ++n) {
277  /* if adjacent tile is outside the board */
278  if (! resources::gameboard->map().on_board(adj[n]))
279  continue;
281  /* if tile is empty */
282  if (unit == units_.end())
283  continue;
284  /* if tile is occupied by friendly or petrified/invisible unit */
285  if (!ai_.current_team().is_enemy(unit->side()) ||
286  unit->incapacitated() ||
287  unit->invisible(unit->get_location(), *resources::gameboard))
288  continue;
289  /* add attacks with default weapon */
290  auto item = std::make_shared<attack_callable>(attacker_location, attack_position, adj[n], -1);
291  vars.emplace_back(item);
292  }
293 }
294 
295 
297  if( key == "id")
298  return variant(id_);
299  if( key == "loc")
300  return variant(std::make_shared<location_callable>(loc_));
301  return variant();
302 }
303 
305  add_input(inputs, "id");
306  add_input(inputs, "loc");
307 }
308 
312 
313  if(recall_result->is_ok()) {
314  recall_result->execute();
315  } else {
316  LOG_AI << "ERROR #" << recall_result->get_status() << " while executing 'recall' formula function\n" << std::endl;
317  return variant(std::make_shared<safe_call_result>(fake_ptr(), recall_result->get_status()));
318  }
319 
320  return variant(recall_result->is_gamestate_changed());
321 }
322 
324  if( key == "unit_type")
325  return variant(type_);
326  if( key == "recruit_loc")
327  return variant(std::make_shared<location_callable>(loc_));
328  return variant();
329 }
330 
332  add_input(inputs, "unit_type");
333  add_input(inputs, "recruit_loc");
334 }
335 
339 
340  //is_ok()==true means that the action is successful (eg. no unexpected events)
341  //is_ok() must be checked or the code will complain :)
342  if(recruit_result->is_ok()) {
343  recruit_result->execute();
344  } else {
345  LOG_AI << "ERROR #" << recruit_result->get_status() << " while executing 'recruit' formula function\n" << std::endl;
346  return variant(std::make_shared<safe_call_result>(fake_ptr(), recruit_result->get_status()));
347  }
348 
349  //is_gamestate_changed()==true means that the game state was somehow changed by action.
350  //it is believed that during a turn, a game state can change only a finite number of times
351  return variant(recruit_result->is_gamestate_changed());
352 }
353 
355  if(key == "loc")
356  return variant(std::make_shared<location_callable>(loc_));
357 
358  if(key == "key")
359  return variant(key_);
360 
361  if(key == "value")
362  return value_;
363 
364  return variant();
365 }
366 
368  add_input(inputs, "loc");
369  add_input(inputs, "key");
370  add_input(inputs, "value");
371 }
372 
374  int status = 0;
377 
378 /* if(!infinite_loop_guardian_.set_unit_var_check()) {
379  status = 5001; //exceeded nmber of calls in a row - possible infinite loop
380  } else*/ if((unit = units.find(loc_)) == units.end()) {
381  status = 5002; //unit not found
382  } else if(unit->side() != get_ai_context(ctxt.as_callable()).get_side()) {
383  status = 5003;//unit does not belong to our side
384  }
385 
386  if(status == 0) {
387  LOG_AI << "Setting unit variable: " << key_ << " -> " << value_.to_debug_string() << "\n";
388  unit->formula_manager().add_formula_var(key_, value_);
389  return variant(true);
390  }
391 
392  ERR_AI << "ERROR #" << status << " while executing 'set_unit_var' formula function" << std::endl;
393  return variant(std::make_shared<safe_call_result>(fake_ptr(), status));
394 }
395 
397  // We want give control of the side to human for the rest of this turn
399 }
400 
401 }
Defines formula ai.
int do_compare(const formula_callable *callable) const override
Compare two attacks in deterministic way or compare pointers (nondeterministic in consequent game run...
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:79
variant execute_self(variant ctxt) override
variant get_value(const std::string &key) const override
virtual const unit_map & units() const
Definition: game_board.hpp:97
unit_iterator end()
Definition: map.hpp:415
std::vector< char_t > string
const map_location & dst() const
This class represents a single unit of a specific type.
Definition: unit.hpp:101
virtual recall_result_ptr check_recall_action(const std::string &id, const map_location &where=map_location::null_location(), const map_location &from=map_location::null_location())=0
size_t count(const map_location &loc) const
Definition: map.hpp:400
const map_location & src() const
attack_callable(const map_location &move_from, const map_location &src, const map_location &dst, int weapon)
Managing the AI-Game interaction - AI actions and their results.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.hpp:299
bool is_enemy(int n) const
Definition: team.hpp:241
#define LOG_AI
void get_inputs(formula_input_vector &inputs) const override
virtual const move_map & get_srcdst() const override
Definition: contexts.hpp:851
std::shared_ptr< move_result > move_result_ptr
Definition: game_info.hpp:100
int defender_weapon() const
std::vector< formula_input > formula_input_vector
variant execute_self(variant ctxt) override
const ai::formula_ai & ai_
std::shared_ptr< recruit_result > recruit_result_ptr
Definition: game_info.hpp:99
variant get_value(const std::string &key) const override
std::shared_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:97
ai_context & get_ai_context(wfl::const_formula_callable_ptr for_fai)
void get_inputs(formula_input_vector &inputs) const override
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
std::shared_ptr< recall_result > recall_result_ptr
Definition: game_info.hpp:98
void get_inputs(formula_input_vector &inputs) const override
void get_inputs(formula_input_vector &inputs) const override
game_board * gameboard
Definition: resources.cpp:20
variant execute_self(variant ctxt) override
virtual int do_compare(const formula_callable *callable) const
Definition: callable.hpp:145
variant execute_self(variant ctxt) override
variant get_value(const std::string &key) const override
virtual attack_result_ptr execute_attack_action(const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon)=0
Encapsulates the map of the game.
Definition: location.hpp:40
void collect_possible_attacks(std::vector< variant > &vars, map_location attacker_location, map_location attack_position) const
static lg::log_domain log_formula_ai("ai/engine/fai")
void get_inputs(formula_input_vector &inputs) const override
variant get_value(const std::string &key) const override
formula_callable_ptr fake_ptr()
Definition: callable.hpp:41
virtual const team & current_team() const override
Definition: contexts.hpp:536
Game information for the AI.
#define ERR_AI
variant get_value(const std::string &key) const override
void get_inputs(formula_input_vector &inputs) const override
variant execute_self(variant ctxt) override
variant execute_self(variant ctxt) override
#define i
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:477
virtual recruit_result_ptr check_recruit_action(const std::string &unit_name, const map_location &where=map_location::null_location(), const map_location &from=map_location::null_location())=0
int do_compare(const formula_callable *callable) const override
std::shared_ptr< const formula_callable > const_formula_callable_ptr
variant get_value(const std::string &key) const override
void get_inputs(formula_input_vector &inputs) const override
Definition: contexts.hpp:42
bool find(E event, F functor)
Tests whether an event handler is available.
Composite AI contexts.
Standard logging facilities (interface).
void get_inputs(formula_input_vector &inputs) const override
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:649
variant execute_self(variant ctxt) override
Container associating units to locations.
Definition: map.hpp:99
int do_compare(const map_location &a) const
three-way comparator
Definition: location.hpp:88
int do_compare(const formula_callable *callable) const override
static void add_input(formula_input_vector &inputs, const std::string &key, FORMULA_ACCESS_TYPE access_type=FORMULA_READ_ONLY)
Definition: callable.hpp:135
unit_iterator find(size_t id)
Definition: map.cpp:311
static map_location::DIRECTION n
unit_map * units
Definition: resources.cpp:34
variant get_value(const std::string &key) const override
const map_location & move_from() const
variant get_value(const std::string &key) const override
virtual move_result_ptr execute_move_action(const map_location &from, const map_location &to, bool remove_movement=true, bool unreach_is_ok=false)=0