The Battle for Wesnoth  1.15.9+dev
simulated_actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Guorui Xi <kevin.xgr@gmail.com>
3  Part of the Battle for Wesnoth Project https://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 "random.hpp"
27 #include "recall_list_manager.hpp"
28 #include "resources.hpp"
29 #include "team.hpp"
30 #include "units/unit.hpp"
31 #include "units/helper.hpp"
32 #include "units/ptr.hpp"
33 #include "units/types.hpp"
34 
35 namespace ai {
36 
37 static lg::log_domain log_ai_sim_actions("ai/sim_actions");
38 #define DBG_AI_SIM_ACTIONS LOG_STREAM(debug, log_ai_sim_actions)
39 #define LOG_AI_SIM_ACTIONS LOG_STREAM(info, log_ai_sim_actions)
40 #define WRN_AI_SIM_ACTIONS LOG_STREAM(warn, log_ai_sim_actions)
41 #define ERR_AI_SIM_ACTIONS LOG_STREAM(err, log_ai_sim_actions)
42 
43 void helper_check_village(const map_location& loc, int side);
44 void helper_place_unit(const unit& u, const map_location& loc);
45 void helper_advance_unit(const map_location& loc);
46 
47 bool simulated_attack(const map_location& attacker_loc, const map_location& defender_loc, double attacker_hp, double defender_hp){
48  LOG_AI_SIM_ACTIONS << "Simulated attack" << std::endl;
49 
51  unit_map::iterator defend_unit = resources::gameboard->units().find(defender_loc);
52 
53  LOG_AI_SIM_ACTIONS << attack_unit->type_name() << " at " << attacker_loc << " attack "
54  << defend_unit->type_name() << " at " << defender_loc << std::endl;
55  LOG_AI_SIM_ACTIONS << "attacker's hp before attack: " << attack_unit->hitpoints() << std::endl;
56  LOG_AI_SIM_ACTIONS << "defender's hp before attack: " << defend_unit->hitpoints() << std::endl;
57 
58  attack_unit->set_hitpoints(static_cast<int>(attacker_hp));
59  defend_unit->set_hitpoints(static_cast<int>(defender_hp));
60 
61  LOG_AI_SIM_ACTIONS << "attacker's hp after attack: " << attack_unit->hitpoints() << std::endl;
62  LOG_AI_SIM_ACTIONS << "defender's hp after attack: " << defend_unit->hitpoints() << std::endl;
63 
64  int attacker_xp = game_config::combat_xp(defend_unit->level());
65  int defender_xp = game_config::combat_xp(attack_unit->level());
66  bool attacker_died = false;
67  bool defender_died = false;
68  if(attack_unit->hitpoints() <= 0){
69  attacker_xp = 0;
70  defender_xp = game_config::kill_xp(attack_unit->level());
71  (resources::gameboard->units()).erase(attacker_loc);
72  attacker_died = true;
73  }
74 
75  if(defend_unit->hitpoints() <= 0){
76  defender_xp = 0;
77  attacker_xp = game_config::kill_xp(defend_unit->level());
78  (resources::gameboard->units()).erase(defender_loc);
79  defender_died = true;
80  }
81 
82  if(!attacker_died){
83  attack_unit->set_experience(attack_unit->experience()+attacker_xp);
84  helper_advance_unit(attacker_loc);
85  simulated_stopunit(attacker_loc, true, true);
86  }
87 
88  if(!defender_died){
89  defend_unit->set_experience(defend_unit->experience()+defender_xp);
90  helper_advance_unit(defender_loc);
91  simulated_stopunit(defender_loc, true, true);
92  }
93 
94  return true;
95 }
96 
97 bool simulated_move(int side, const map_location& from, const map_location& to, int steps, map_location& unit_location)
98 {
99  LOG_AI_SIM_ACTIONS << "Simulated move" << std::endl;
100 
101  // In simulation, AI should not know if there is a enemy's ambusher.
102  auto [move_unit, success] = resources::gameboard->units().move(from, to);
103 
104  if(!success) {
105  // This happened because in some CAs like get_village_phase and move_leader_to_keep phase,
106  // if the destination is already occupied will not be checked before execute. Just silent
107  // errors in ai/actions and tell rca the game state isn't changed.
108  unit_location = to;
109  return false;
110  }
111 
112  move_unit->set_movement(move_unit->movement_left()-steps); // Following original logic, remove_movement_ will be considered outside.
113 
114  unit_location = move_unit->get_location(); // For check_after.
115 
116  LOG_AI_SIM_ACTIONS << move_unit->type_name() << " move from " << from << " to " << to << std::endl;
117 
118  if(resources::gameboard->map().is_village(to)){
119  helper_check_village(to, side);
120  }
121 
122  return true;
123 }
124 
125 bool simulated_recall(int side, const std::string& unit_id, const map_location& recall_location){
126  LOG_AI_SIM_ACTIONS << "Simulated recall" << std::endl;
127 
128  team own_team = resources::gameboard->get_team(side);
130 
131  helper_place_unit(*recall_unit, recall_location);
132 
133  own_team.spend_gold(recall_unit->recall_cost()<0 ? own_team.recall_cost() : recall_unit->recall_cost());
134 
135  LOG_AI_SIM_ACTIONS << "recall " << recall_unit->type_name() << " at "
136  << recall_location << " spend " << own_team.recall_cost() << " gold" << std::endl;
137 
138  return true;
139 }
140 
141 bool simulated_recruit(int side, const unit_type* u, const map_location& recruit_location){
142  LOG_AI_SIM_ACTIONS << "Simulated recruit" << std::endl;
143 
144  unit_ptr recruit_unit = unit::create(*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.
145  helper_place_unit(*recruit_unit, recruit_location);
146 
148 
149  LOG_AI_SIM_ACTIONS << "recruit " << u->type_name() << " at "
150  << recruit_location << " spend " << u->cost() << " gold" << std::endl;
151 
152  return true;
153 }
154 
155 bool simulated_stopunit(const map_location& unit_location, bool remove_movement, bool remove_attacks){
156  LOG_AI_SIM_ACTIONS << "Simulated stopunit" << std::endl;
157 
158  unit_map::iterator stop_unit = resources::gameboard->units().find(unit_location);
159  bool changed = false;
160  if(remove_movement){
161  stop_unit->set_movement(0, true);
162  LOG_AI_SIM_ACTIONS << "remove (" << stop_unit->get_location() << ") " << stop_unit->type_name() << "'s movement" << std::endl;
163  changed = true;
164  }
165  if(remove_attacks){
166  stop_unit->set_attacks(0);
167  LOG_AI_SIM_ACTIONS << "remove (" << stop_unit->get_location() << ") " << stop_unit->type_name() << "'s attacks" << std::endl;
168  changed = true;
169  }
170 
171  return changed;
172 }
173 
175  LOG_AI_SIM_ACTIONS << "Simulated synced_command" << std::endl;
176 
177  DBG_AI_SIM_ACTIONS << "Trigger dummy synced_command_result::do_execute()" << std::endl;
178 
179  return false;
180 }
181 
182 // Helper functions.
183 void helper_check_village(const map_location& loc, int side){
184  std::vector<team> &teams = resources::gameboard->teams();
185  team *t = static_cast<unsigned>(side - 1) < teams.size() ? &teams[side - 1] : nullptr;
186  if(t && t->owns_village(loc)){
187  return;
188  }
189 
190  bool has_leader = resources::gameboard->units().find_leader(side).valid();
191 
192  // Strip the village off all other sides.
193  int old_owner_side = 0;
194  for(std::vector<team>::iterator i = teams.begin(); i != teams.end(); ++i){
195  int i_side = std::distance(teams.begin(), i) + 1;
196  if(!t || has_leader || t->is_enemy(i_side)){
197  if(i->owns_village(loc)){
198  old_owner_side = i_side;
199  i->lose_village(loc);
200  DBG_AI_SIM_ACTIONS << "side " << i_side << " losts village at " << loc << std::endl;
201  }
202  }
203  }
204 
205  // Get the village if have leader.
206  if (!t) return;
207 
208  if(has_leader){
209  t->get_village(loc, old_owner_side, nullptr);
210  DBG_AI_SIM_ACTIONS << "side " << side << " gets village at " << loc << std::endl;
211  }
212 }
213 
214 void helper_place_unit(const unit& u, const map_location& loc){
215  unit_ptr new_unit = u.clone();
216  new_unit->set_movement(0, true);
217  new_unit->set_attacks(0);
218  new_unit->heal_fully();
219  new_unit->set_location(loc);
220 
221  auto [new_unit_itor, success] = resources::gameboard->units().insert(new_unit);
222  assert(success);
223 
224  if(resources::gameboard->map().is_village(loc)){
225  helper_check_village(loc, new_unit_itor->side());
226  }
227 }
228 
230  // Choose advanced unit type randomly.
231  // First check if the unit has enough experience and can advance.
232  // Then get all possible options, include modification advancements, like {AMLA DEFAULT} in cfg.
233  // And then randomly choose one to advanced to.
234 
236 
237  if(!unit_helper::will_certainly_advance(advance_unit))
238  return;
239 
240  const std::vector<std::string>& options = advance_unit->advances_to();
241  std::vector<config> mod_options = advance_unit->get_modification_advances();
242  int options_num = unit_helper::number_of_possible_advances(*advance_unit);
243 
244  std::size_t advance_choice = randomness::generator->get_random_int(0, options_num-1);
245  unit_ptr advanced_unit = (*advance_unit).clone();
246 
247  if(advance_choice < options.size()){
248  std::string advance_unit_typename = options[advance_choice];
249  const unit_type *advanced_type = unit_types.find(advance_unit_typename);
250  if(!advanced_type) {
251  ERR_AI_SIM_ACTIONS << "Simulating advancing to unknown unit type: " << advance_unit_typename;
252  assert(false && "simulating to unknown unit type");
253  }
254  advanced_unit->set_experience(advanced_unit->experience_overflow());
255  advanced_unit->advance_to(*advanced_type);
256  advanced_unit->heal_fully();
257  advanced_unit->set_state(unit::STATE_POISONED, false);
258  advanced_unit->set_state(unit::STATE_SLOWED, false);
259  advanced_unit->set_state(unit::STATE_PETRIFIED, false);
260  }else{
261  const config &mod_option = mod_options[advance_choice-options.size()];
262  advanced_unit->set_experience(advanced_unit->experience_overflow());
263  advanced_unit->add_modification("advancement", mod_option);
264  }
265 
266  resources::gameboard->units().replace(loc, advanced_unit);
267  LOG_AI_SIM_ACTIONS << advance_unit->type_name() << " at " << loc << " advanced to " << advanced_unit->type_name() << std::endl;
268 }
269 
270 }// End namespace
int kill_xp(int level)
Definition: game_config.hpp:47
int combat_xp(int level)
Definition: game_config.hpp:52
bool simulated_synced_command()
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:1222
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:82
virtual const unit_map & units() const override
Definition: game_board.hpp:109
This class represents a single unit of a specific type.
Definition: unit.hpp:119
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:133
unit_iterator find_leader(int side)
Definition: map.cpp:327
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:34
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
unit_ptr clone() const
Definition: unit.hpp:208
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:853
unit_type_data unit_types
Definition: types.cpp:1441
The unit is poisoned - it loses health each turn.
Definition: unit.hpp:854
#define DBG_AI_SIM_ACTIONS
bool simulated_attack(const map_location &attacker_loc, const map_location &defender_loc, double attacker_hp, double defender_hp)
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:25
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition: unit.hpp:188
A single unit type that the player may recruit.
Definition: types.hpp:44
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:223
const config & options()
Definition: game.cpp:563
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
team & get_team(int i)
Definition: game_board.hpp:94
bool simulated_recall(int side, const std::string &unit_id, const map_location &recall_location)
int cost() const
Definition: types.hpp:166
static lg::log_domain log_ai_sim_actions("ai/sim_actions")
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:140
game_board * gameboard
Definition: resources.cpp:20
void erase(const std::string &key)
Definition: general.cpp:219
bool is_enemy(int n) const
Definition: team.hpp:251
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:92
void spend_gold(const int amount)
Definition: team.hpp:216
int recall_cost() const
Definition: team.hpp:201
Encapsulates the map of the game.
Definition: location.hpp:37
unit_iterator find(std::size_t id)
Definition: map.cpp:309
void advance_unit(map_location loc, const advancement_option &advance_to, bool fire_event)
Function which will advance the unit at loc to &#39;advance_to&#39;.
void helper_check_village(const map_location &loc, int side)
#define ERR_AI_SIM_ACTIONS
std::size_t i
Definition: function.cpp:933
void helper_advance_unit(const map_location &loc)
bool simulated_stopunit(const map_location &unit_location, bool remove_movement, bool remove_attacks)
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:29
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
Definition: random.hpp:51
unit_ptr extract_if_matches_id(const std::string &unit_id, int *pos=nullptr)
Find a unit by id, and extract from this object if found.
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
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:707
game_events::pump_result_t get_village(const map_location &, const int owner_side, game_data *fire_event)
Acquires a village from owner_side.
Definition: team.cpp:440
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:1559
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:223
bool owns_village(const map_location &loc) const
Definition: team.hpp:193
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
bool valid() const
Definition: map.hpp:273
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:509
Implement simulated actions.