The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
simulated_actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2017 by Guorui Xi <kevin.xgr@gmail.com>
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 /**
16  * Implement simulated actions
17  * @file
18  */
19 
20 #include "ai/simulated_actions.hpp"
21 
22 #include "game_board.hpp"
23 #include "game_config.hpp"
24 #include "log.hpp"
25 #include "map/map.hpp"
26 #include "recall_list_manager.hpp"
27 #include "resources.hpp"
28 #include "team.hpp"
29 #include "units/unit.hpp"
30 #include "units/helper.hpp"
31 #include "units/ptr.hpp"
32 #include "units/types.hpp"
33 
34 namespace ai {
35 
36 static lg::log_domain log_ai_sim_actions("ai/sim_actions");
37 #define DBG_AI_SIM_ACTIONS LOG_STREAM(debug, log_ai_sim_actions)
38 #define LOG_AI_SIM_ACTIONS LOG_STREAM(info, log_ai_sim_actions)
39 #define WRN_AI_SIM_ACTIONS LOG_STREAM(warn, log_ai_sim_actions)
40 #define ERR_AI_SIM_ACTIONS LOG_STREAM(err, log_ai_sim_actions)
41 
42 void helper_check_village(const map_location& loc, int side);
43 void helper_place_unit(const unit& u, const map_location& loc);
44 void helper_advance_unit(const map_location& loc);
45 
46 bool simulated_attack(const map_location& attacker_loc, const map_location& defender_loc, double attacker_hp, double defender_hp){
47  LOG_AI_SIM_ACTIONS << "Simulated attack" << std::endl;
48 
50  unit_map::iterator defend_unit = resources::gameboard->units().find(defender_loc);
51 
52  LOG_AI_SIM_ACTIONS << attack_unit->type_name() << " at " << attacker_loc << " attack "
53  << defend_unit->type_name() << " at " << defender_loc << std::endl;
54  LOG_AI_SIM_ACTIONS << "attacker's hp before attack: " << attack_unit->hitpoints() << std::endl;
55  LOG_AI_SIM_ACTIONS << "defender's hp before attack: " << defend_unit->hitpoints() << std::endl;
56 
57  attack_unit->set_hitpoints(static_cast<int>(attacker_hp));
58  defend_unit->set_hitpoints(static_cast<int>(defender_hp));
59 
60  LOG_AI_SIM_ACTIONS << "attacker's hp after attack: " << attack_unit->hitpoints() << std::endl;
61  LOG_AI_SIM_ACTIONS << "defender's hp after attack: " << defend_unit->hitpoints() << std::endl;
62 
63  int attacker_xp = defend_unit->level();
64  int defender_xp = attack_unit->level();
65  bool attacker_died = false;
66  bool defender_died = false;
67  if(attack_unit->hitpoints() <= 0){
68  attacker_xp = 0;
69  defender_xp = game_config::kill_xp(attack_unit->level());
70  (resources::gameboard->units()).erase(attacker_loc);
71  attacker_died = true;
72  }
73 
74  if(defend_unit->hitpoints() <= 0){
75  defender_xp = 0;
76  attacker_xp = game_config::kill_xp(defend_unit->level());
77  (resources::gameboard->units()).erase(defender_loc);
78  defender_died = true;
79  }
80 
81  if(!attacker_died){
82  attack_unit->set_experience(attack_unit->experience()+attacker_xp);
83  helper_advance_unit(attacker_loc);
84  simulated_stopunit(attacker_loc, true, true);
85  }
86 
87  if(!defender_died){
88  defend_unit->set_experience(defend_unit->experience()+defender_xp);
89  helper_advance_unit(defender_loc);
90  simulated_stopunit(defender_loc, true, true);
91  }
92 
93  return true;
94 }
95 
96 bool simulated_move(int side, const map_location& from, const map_location& to, int steps, map_location& unit_location){
97  LOG_AI_SIM_ACTIONS << "Simulated move" << std::endl;
98 
99  // In simulation, AI should not know if there is a enemy's ambusher.
100  std::pair<unit_map::unit_iterator, bool> unit_move = resources::gameboard->units().move(from, to);
101  bool is_ok = unit_move.second;
102  if(!is_ok){
103  unit_location = to; // This happened because in some CAs like get_village_phase and move_leader_to_keep phase,
104  // if the destination is already occupied will not be checked before execute. Just silent
105  // errors in ai/actions and tell rca the game state isn't changed.
106  return false;
107  }
108  unit_map::unit_iterator move_unit = unit_move.first;
109  move_unit->set_movement(move_unit->movement_left()-steps); // Following original logic, remove_movement_ will be considered outside.
110 
111  unit_location = move_unit->get_location(); // For check_after.
112 
113  LOG_AI_SIM_ACTIONS << move_unit->type_name() << " move from " << from << " to " << to << std::endl;
114 
115  if(resources::gameboard->map().is_village(to)){
116  helper_check_village(to, side);
117  }
118 
119  return true;
120 }
121 
122 bool simulated_recall(int side, const std::string& unit_id, const map_location& recall_location){
123  LOG_AI_SIM_ACTIONS << "Simulated recall" << std::endl;
124 
125  team own_team = resources::gameboard->get_team(side);
127 
128  helper_place_unit(*recall_unit, recall_location);
129 
130  own_team.spend_gold(recall_unit->recall_cost()<0 ? own_team.recall_cost() : recall_unit->recall_cost());
131 
132  LOG_AI_SIM_ACTIONS << "recall " << recall_unit->type_name() << " at "
133  << recall_location << " spend " << own_team.recall_cost() << " gold" << std::endl;
134 
135  return true;
136 }
137 
138 bool simulated_recruit(int side, const unit_type* u, const map_location& recruit_location){
139  LOG_AI_SIM_ACTIONS << "Simulated recruit" << std::endl;
140 
141  const unit recruit_unit(*u, side, false); // Random traits, name and gender are not needed. This will cause "duplicate id conflicts" inside unit_map::insert(), but engine will manage this issue correctly.
142  helper_place_unit(recruit_unit, recruit_location);
143 
145 
146  LOG_AI_SIM_ACTIONS << "recruit " << u->type_name() << " at "
147  << recruit_location << " spend " << u->cost() << " gold" << std::endl;
148 
149  return true;
150 }
151 
152 bool simulated_stopunit(const map_location& unit_location, bool remove_movement, bool remove_attacks){
153  LOG_AI_SIM_ACTIONS << "Simulated stopunit" << std::endl;
154 
155  unit_map::iterator stop_unit = resources::gameboard->units().find(unit_location);
156  bool changed = false;
157  if(remove_movement){
158  stop_unit->set_movement(0, true);
159  LOG_AI_SIM_ACTIONS << "remove (" << stop_unit->get_location() << ") " << stop_unit->type_name() << "'s movement" << std::endl;
160  changed = true;
161  }
162  if(remove_attacks){
163  stop_unit->set_attacks(0);
164  LOG_AI_SIM_ACTIONS << "remove (" << stop_unit->get_location() << ") " << stop_unit->type_name() << "'s attacks" << std::endl;
165  changed = true;
166  }
167 
168  return changed;
169 }
170 
172  LOG_AI_SIM_ACTIONS << "Simulated synced_command" << std::endl;
173 
174  DBG_AI_SIM_ACTIONS << "Trigger dummy synced_command_result::do_execute()" << std::endl;
175 
176  return false;
177 }
178 
179 // Helper functions.
180 void helper_check_village(const map_location& loc, int side){
181  std::vector<team> &teams = resources::gameboard->teams();
182  team *t = unsigned(side - 1) < teams.size() ? &teams[side - 1] : nullptr;
183  if(t && t->owns_village(loc)){
184  return;
185  }
186 
187  bool has_leader = resources::gameboard->units().find_leader(side).valid();
188 
189  // Strip the village off all other sides.
190  int old_owner_side = 0;
191  for(std::vector<team>::iterator i = teams.begin(); i != teams.end(); ++i){
192  int i_side = i - teams.begin() + 1;
193  if(!t || has_leader || t->is_enemy(i_side)){
194  if(i->owns_village(loc)){
195  old_owner_side = i_side;
196  i->lose_village(loc);
197  DBG_AI_SIM_ACTIONS << "side " << i_side << " losts village at " << loc << std::endl;
198  }
199  }
200  }
201 
202  // Get the village if have leader.
203  if (!t) return;
204 
205  if(has_leader){
206  t->get_village(loc, old_owner_side, nullptr);
207  DBG_AI_SIM_ACTIONS << "side " << side << " gets village at " << loc << std::endl;
208  }
209 }
210 
211 void helper_place_unit(const unit& u, const map_location& loc){
212  unit_ptr new_unit(new unit(u));
213  new_unit->set_movement(0, true);
214  new_unit->set_attacks(0);
215  new_unit->heal_fully();
216  new_unit->set_location(loc);
217 
218  std::pair<unit_map::iterator, bool> add_result = resources::gameboard->units().insert(new_unit);
219  assert(add_result.second);
220  unit_map::iterator& new_unit_itor = add_result.first;
221 
222  if(resources::gameboard->map().is_village(loc)){
223  helper_check_village(loc, new_unit_itor->side());
224  }
225 }
226 
228  // Choose advanced unit type randomly.
229  // First check if the unit has enough experience and can advance.
230  // Then get all possible options, include modification advancements, like {AMLA DEFAULT} in cfg.
231  // And then randomly choose one to advanced to.
232 
234 
235  if(!unit_helper::will_certainly_advance(advance_unit))
236  return;
237 
238  const std::vector<std::string>& options = advance_unit->advances_to();
239  std::vector<config> mod_options = advance_unit->get_modification_advances();
240  int options_num = unit_helper::number_of_possible_advances(*advance_unit);
241 
242  size_t advance_choice = rand() % options_num;
243  unit_ptr advanced_unit(new unit(*advance_unit));
244 
245  if(advance_choice < options.size()){
246  std::string advance_unit_typename = options[advance_choice];
247  const unit_type *advanced_type = unit_types.find(advance_unit_typename);
248  if(!advanced_type) {
249  ERR_AI_SIM_ACTIONS << "Simulating advancing to unknown unit type: " << advance_unit_typename;
250  assert(false && "simulating to unknown unit type");
251  }
252  advanced_unit->set_experience(advanced_unit->experience_overflow());
253  advanced_unit->advance_to(*advanced_type);
254  advanced_unit->heal_fully();
255  advanced_unit->set_state(unit::STATE_POISONED, false);
256  advanced_unit->set_state(unit::STATE_SLOWED, false);
257  advanced_unit->set_state(unit::STATE_PETRIFIED, false);
258  }else{
259  const config &mod_option = mod_options[advance_choice-options.size()];
260  advanced_unit->set_experience(advanced_unit->experience_overflow());
261  advanced_unit->add_modification("advancement", mod_option);
262  }
263 
264  resources::gameboard->units().replace(loc, advanced_unit);
265  LOG_AI_SIM_ACTIONS << advance_unit->type_name() << " at " << loc << " advanced to " << advanced_unit->type_name() << std::endl;
266 }
267 
268 }// End namespace
int kill_xp(int level)
Definition: game_config.hpp:48
bool recall_unit(const std::string &id, team &current_team, const map_location &loc, const map_location &from, map_location::DIRECTION facing, bool show, bool use_undo)
Recalls the unit with the indicated ID for the provided team.
Definition: create.cpp:735
bool simulated_synced_command()
virtual const unit_map & units() const
Definition: game_board.hpp:97
std::vector< char_t > string
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:137
This class represents a single unit of a specific type.
Definition: unit.hpp:101
void helper_place_unit(const unit &u, const map_location &loc)
umap_retval_pair_t insert(unit_ptr p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:135
unit_iterator find_leader(int side)
Definition: map.cpp:329
bool owns_village(const map_location &loc) const
Definition: team.hpp:184
bool will_certainly_advance(const unit_map::iterator &u)
Encapsulates the logic for deciding whether an iterator u points to a unit that can advance...
Definition: helper.cpp:30
bool is_enemy(int n) const
Definition: team.hpp:241
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:722
unit_type_data unit_types
Definition: types.cpp:1455
The unit is petrified - it cannot move or be attacked.
Definition: unit.hpp:723
virtual const std::vector< team > & teams() const
Definition: game_board.hpp:92
#define DBG_AI_SIM_ACTIONS
bool simulated_attack(const map_location &attacker_loc, const map_location &defender_loc, double attacker_hp, double defender_hp)
-file sdl_utils.hpp
unit_ptr extract_if_matches_id(const std::string &unit_id)
Find a unit by id, and extract from this object if found. Null if not found.
A single unit type that the player may recruit.
Definition: types.hpp:43
umap_retval_pair_t replace(const map_location &l, unit_ptr p)
Works like unit_map::add; but l is emptied first, if needed.
Definition: map.cpp:225
const config & options()
Definition: game.cpp:570
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:44
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
void recruit_unit(const unit_type &u_type, int side_num, const map_location &loc, const map_location &from, bool show, bool use_undo)
Recruits a unit of the given type for the given side.
Definition: create.cpp:703
team & get_team(int i)
Definition: game_board.hpp:94
int recall_cost() const
Definition: team.hpp:192
bool simulated_recall(int side, const std::string &unit_id, const map_location &recall_location)
static lg::log_domain log_ai_sim_actions("ai/sim_actions")
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:721
game_board * gameboard
Definition: resources.cpp:20
void erase(const std::string &key)
Definition: general.cpp:220
umap_retval_pair_t move(const map_location &src, const map_location &dst)
Moves a unit from location src to location dst.
Definition: map.cpp:94
void spend_gold(const int amount)
Definition: team.hpp:207
int cost() const
Definition: types.hpp:159
Encapsulates the map of the game.
Definition: location.hpp:40
void advance_unit(map_location loc, const advancement_option &advance_to, bool fire_event)
Function which will advance the unit at loc to 'advance_to'.
void helper_check_village(const map_location &loc, int side)
#define ERR_AI_SIM_ACTIONS
void helper_advance_unit(const map_location &loc)
bool simulated_stopunit(const map_location &unit_location, bool remove_movement, bool remove_attacks)
#define i
int number_of_possible_advances(const unit &u)
Determines the total number of available advancements (of any kind) for a given unit.
Definition: helper.cpp:25
game_events::pump_result_t get_village(const map_location &, const int owner_side, game_data *fire_event)
Acquires a village from owner_side. Pointer fire_event should be the game_data for the game if it is ...
Definition: team.cpp:425
boost::intrusive_ptr< unit > unit_ptr
Definition: ptr.hpp:29
double t
Definition: astarsearch.cpp:64
bool simulated_move(int side, const map_location &from, const map_location &to, int steps, map_location &unit_location)
void attack_unit(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display)
Performs an attack.
Definition: attack.cpp:1539
bool simulated_recruit(int side, const unit_type *u, const map_location &recruit_location)
Standard logging facilities (interface).
#define LOG_AI_SIM_ACTIONS
recall_list_manager & recall_list()
Definition: team.hpp:214
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1273
unit_iterator find(size_t id)
Definition: map.cpp:311
bool valid() const
Definition: map.hpp:276
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
void move_unit(const std::vector< map_location > &path, unit_ptr u, bool animate, map_location::DIRECTION dir, bool force_scroll)
Display a unit moving along a given path.
Definition: udisplay.cpp:495
Implement simulated actions.