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 "units/unit.hpp"
26 #include "log.hpp"
27 #include "menu_events.hpp" // for fallback_ai_to_human_exception
28 
29 static lg::log_domain log_formula_ai("ai/engine/fai");
30 #define DBG_AI LOG_STREAM(debug, log_formula_ai)
31 #define LOG_AI LOG_STREAM(info, log_formula_ai)
32 #define WRN_AI LOG_STREAM(warn, log_formula_ai)
33 #define ERR_AI LOG_STREAM(err, log_formula_ai)
34 
35 namespace ai {
36 
38  auto fai = std::dynamic_pointer_cast<const formula_ai>(for_fai);
39  assert(fai != nullptr);
40  return *std::const_pointer_cast<formula_ai>(fai)->ai_ptr_;
41 }
42 
43 }
44 
45 namespace wfl {
46  using namespace ai;
47 
49 {
50  if(key == "moves") {
51  std::vector<variant> vars;
52  for(move_map::const_iterator i = srcdst_.begin(); i != srcdst_.end(); ++i) {
53  if( i->first == i->second || units_.count(i->second) == 0) {
54  auto item = std::make_shared<move_callable>(i->first, i->second);
55  vars.emplace_back(item);
56  }
57  }
58 
59  return variant(vars);
60  } else if(key == "has_moves") {
61  return variant(!srcdst_.empty());
62  } else {
63  return variant();
64  }
65 }
66 
68 {
69  add_input(inputs, "moves");
70 }
71 
72 int move_callable::do_compare(const formula_callable* callable) const
73 {
74  const move_callable* mv_callable = dynamic_cast<const move_callable*>(callable);
75  if(mv_callable == nullptr) {
76  return formula_callable::do_compare(callable);
77  }
78 
79  const map_location& other_src = mv_callable->src_;
80  const map_location& other_dst = mv_callable->dst_;
81 
82  if (int cmp = src_.do_compare(other_src)) {
83  return cmp;
84  }
85 
86  return dst_.do_compare(other_dst);
87 }
88 
91  move_result_ptr move_result = ai.execute_move_action(src_, dst_, true);
92 
93  if(!move_result->is_ok()) {
94  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'move' formula function\n" << std::endl;
95  return variant(std::make_shared<safe_call_result>(fake_ptr(), move_result->get_status(), move_result->get_unit_location()));
96  }
97 
98  return variant(move_result->is_gamestate_changed());
99 }
100 
102 {
103  const move_partial_callable* mv_callable = dynamic_cast<const move_partial_callable*>(callable);
104  if(mv_callable == nullptr) {
105  return formula_callable::do_compare(callable);
106  }
107 
108  const map_location& other_src = mv_callable->src_;
109  const map_location& other_dst = mv_callable->dst_;
110 
111  if (int cmp = src_.do_compare(other_src)) {
112  return cmp;
113  }
114 
115  return dst_.do_compare(other_dst);
116 }
117 
120  move_result_ptr move_result = ai.execute_move_action(src_, dst_, false);
121 
122  if(!move_result->is_ok()) {
123  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'move_partial' formula function\n" << std::endl;
124  return variant(std::make_shared<safe_call_result>(fake_ptr(), move_result->get_status(), move_result->get_unit_location()));
125  }
126 
127  return variant(move_result->is_gamestate_changed());
128 }
129 
131  if(key == "chance") {
132  return variant(chance_);
133  } else {
134  return variant();
135  }
136 }
137 
139  add_input(inputs, "chance");
140 }
141 
143  if(key == "hitpoints_left") {
144  return variant(hitLeft_);
145  } else if(key == "probability") {
146  return variant(prob_);
147  } else if(key == "possible_status") {
148  return variant(status_);
149  } else {
150  return variant();
151  }
152 }
153 
155  add_input(inputs, "hitpoints_left");
156  add_input(inputs, "probability");
157  add_input(inputs, "possible_status");
158 }
159 
160 
162  const map_location& src, const map_location& dst, int weapon)
163  : move_from_(move_from), src_(src), dst_(dst),
164  bc_(resources::gameboard->units(), src, dst, weapon, -1, 1.0, nullptr,
165  &*resources::gameboard->units().find(move_from))
166 {
167  type_ = ATTACK_C;
168 }
169 
170 
172  if(key == "attack_from") {
173  return variant(std::make_shared<location_callable>(src_));
174  } else if(key == "defender") {
175  return variant(std::make_shared<location_callable>(dst_));
176  } else if(key == "move_from") {
177  return variant(std::make_shared<location_callable>(move_from_));
178  } else {
179  return variant();
180  }
181 }
182 
184  add_input(inputs, "attack_from");
185  add_input(inputs, "defender");
186  add_input(inputs, "move_from");
187 }
188 
190  const {
191  const attack_callable* a_callable = dynamic_cast<const attack_callable*>(callable);
192  if(a_callable == nullptr) {
193  return formula_callable::do_compare(callable);
194  }
195 
196  const map_location& other_from = a_callable->move_from();
197 
198  if (int cmp = move_from_.do_compare(other_from)) {
199  return cmp;
200  }
201  const map_location& other_src = a_callable->src();
202  if (int cmp = src_.do_compare(other_src)) {
203  return cmp;
204  }
205  const map_location& other_dst = a_callable->dst();
206  if (int cmp = dst_.do_compare(other_dst)) {
207  return cmp;
208  }
209  const int other_weapon = a_callable->weapon();
210  if (int cmp = (this->weapon() - other_weapon)) {
211  return cmp;
212  }
213  const int other_def_weapon = a_callable->defender_weapon();
214  return this->defender_weapon() - other_def_weapon;
215 }
216 
219  bool gamestate_changed = false;
221 
222  if(move_from_ != src_) {
223  move_result = ai.execute_move_action(move_from_, src_, false);
224  gamestate_changed |= move_result->is_gamestate_changed();
225 
226  if(!move_result->is_ok()) {
227  //move part failed
228  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'attack' formula function\n" << std::endl;
229  return variant(std::make_shared<safe_call_result>(fake_ptr(), move_result->get_status(), move_result->get_unit_location()));
230  }
231  }
232 
233  if(!move_result || move_result->is_ok()) {
234  //if move wasn't done at all or was done successfully
236  gamestate_changed |= attack_result->is_gamestate_changed();
237  if(!attack_result->is_ok()) {
238  //attack failed
239  LOG_AI << "ERROR #" << attack_result->get_status() << " while executing 'attack' formula function\n" << std::endl;
240  return variant(std::make_shared<safe_call_result>(fake_ptr(), attack_result->get_status()));
241  }
242  }
243 
244  return variant(gamestate_changed);
245 }
246 
248  if(key == "attacks") {
249  std::vector<variant> vars;
250  for(move_map::const_iterator i = ai_.get_srcdst().begin(); i != ai_.get_srcdst().end(); ++i) {
251  /* for each possible move check all adjacent tiles for enemies */
252  if(units_.count(i->second) == 0) {
253  collect_possible_attacks(vars, i->first, i->second);
254  }
255  }
256  /* special case, when unit moved toward enemy and can only attack */
258  if (i->side() == ai_.get_side() && i->attacks_left() > 0) {
259  collect_possible_attacks(vars, i->get_location(), i->get_location());
260  }
261  }
262  return variant(vars);
263  } else {
264  return variant();
265  }
266 }
267 
269  add_input(inputs, "attacks");
270 }
271 
272 /* 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.*/
273 void attack_map_callable::collect_possible_attacks(std::vector<variant>& vars, map_location attacker_location, map_location attack_position) const {
274  map_location adj[6];
275  get_adjacent_tiles(attack_position, adj);
276 
277  for(int n = 0; n != 6; ++n) {
278  /* if adjacent tile is outside the board */
279  if (! resources::gameboard->map().on_board(adj[n]))
280  continue;
282  /* if tile is empty */
283  if (unit == units_.end())
284  continue;
285  /* if tile is occupied by friendly or petrified/invisible unit */
286  if (!ai_.current_team().is_enemy(unit->side()) ||
287  unit->incapacitated() ||
288  unit->invisible(unit->get_location(), *resources::gameboard))
289  continue;
290  /* add attacks with default weapon */
291  auto item = std::make_shared<attack_callable>(attacker_location, attack_position, adj[n], -1);
292  vars.emplace_back(item);
293  }
294 }
295 
296 
298  if( key == "id")
299  return variant(id_);
300  if( key == "loc")
301  return variant(std::make_shared<location_callable>(loc_));
302  return variant();
303 }
304 
306  add_input(inputs, "id");
307  add_input(inputs, "loc");
308 }
309 
313 
314  if(recall_result->is_ok()) {
315  recall_result->execute();
316  } else {
317  LOG_AI << "ERROR #" << recall_result->get_status() << " while executing 'recall' formula function\n" << std::endl;
318  return variant(std::make_shared<safe_call_result>(fake_ptr(), recall_result->get_status()));
319  }
320 
321  return variant(recall_result->is_gamestate_changed());
322 }
323 
325  if( key == "unit_type")
326  return variant(type_);
327  if( key == "recruit_loc")
328  return variant(std::make_shared<location_callable>(loc_));
329  return variant();
330 }
331 
333  add_input(inputs, "unit_type");
334  add_input(inputs, "recruit_loc");
335 }
336 
340 
341  //is_ok()==true means that the action is successful (eg. no unexpected events)
342  //is_ok() must be checked or the code will complain :)
343  if(recruit_result->is_ok()) {
344  recruit_result->execute();
345  } else {
346  LOG_AI << "ERROR #" << recruit_result->get_status() << " while executing 'recruit' formula function\n" << std::endl;
347  return variant(std::make_shared<safe_call_result>(fake_ptr(), recruit_result->get_status()));
348  }
349 
350  //is_gamestate_changed()==true means that the game state was somehow changed by action.
351  //it is believed that during a turn, a game state can change only a finite number of times
352  return variant(recruit_result->is_gamestate_changed());
353 }
354 
356  if(key == "loc")
357  return variant(std::make_shared<location_callable>(loc_));
358 
359  if(key == "key")
360  return variant(key_);
361 
362  if(key == "value")
363  return value_;
364 
365  return variant();
366 }
367 
369  add_input(inputs, "loc");
370  add_input(inputs, "key");
371  add_input(inputs, "value");
372 }
373 
375  int status = 0;
378 
379 /* if(!infinite_loop_guardian_.set_unit_var_check()) {
380  status = 5001; //exceeded nmber of calls in a row - possible infinite loop
381  } else*/ if((unit = units.find(loc_)) == units.end()) {
382  status = 5002; //unit not found
383  } else if(unit->side() != get_ai_context(ctxt.as_callable()).get_side()) {
384  status = 5003;//unit does not belong to our side
385  }
386 
387  if(status == 0) {
388  LOG_AI << "Setting unit variable: " << key_ << " -> " << value_.to_debug_string() << "\n";
389  unit->formula_manager().add_formula_var(key_, value_);
390  return variant(true);
391  }
392 
393  ERR_AI << "ERROR #" << status << " while executing 'set_unit_var' formula function" << std::endl;
394  return variant(std::make_shared<safe_call_result>(fake_ptr(), status));
395 }
396 
398  // We want give control of the side to human for the rest of this turn
400 }
401 
402 }
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:100
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:94
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:93
variant get_value(const std::string &key) const override
std::shared_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:91
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:92
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
size_t i
Definition: function.cpp:933
variant execute_self(variant ctxt) override
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