The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
stage_rca.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 2025
3 by Yurii Chernyi <terraninfo@terraninfo.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * Candidate actions evaluator
18 * @file
19 */
20
22
23#include "ai/manager.hpp"
26#include "ai/composite/rca.hpp"
28#include "log.hpp"
29
30#include <functional>
31
32namespace ai {
33
34namespace ai_default_rca {
35
37#define DBG_AI_TESTING_RCA_DEFAULT LOG_STREAM(debug, log_ai_testing_rca_default)
38#define LOG_AI_TESTING_RCA_DEFAULT LOG_STREAM(info, log_ai_testing_rca_default)
39#define ERR_AI_TESTING_RCA_DEFAULT LOG_STREAM(err, log_ai_testing_rca_default)
40
42 : stage(context,cfg)
43 , candidate_actions_()
44 , cfg_(cfg)
45{
46}
47
49{
50 //init the candidate actions
51 for (const config &cfg_element : cfg_.child_range("candidate_action")) {
52 engine::parse_candidate_action_from_config(*this,cfg_element,back_inserter(candidate_actions_));
53 }
54
56 [this](auto& candidate_actions, const config& cfg) {
57 engine::parse_candidate_action_from_config(*this, cfg, std::back_inserter(candidate_actions));
58 });
59}
60
62{
65 cfg.add_child("candidate_action",ca->to_config());
66 }
67 return cfg;
68}
69
70
72public:
74 {
75 return a->get_max_score() > b->get_max_score();
76 }
77};
78
80{
81 LOG_AI_TESTING_RCA_DEFAULT << "Starting candidate action evaluation loop for side "<< get_side();
82
84 ca->enable();
85 }
86
87 //sort candidate actions by max_score DESC
89
90 bool executed = false;
91 bool gamestate_changed = false;
92 do {
93 executed = false;
94 double best_score = candidate_action::BAD_SCORE;
95 candidate_action_ptr best_ptr;
96
97 //Evaluation
99 if (!ca_ptr->is_enabled()){
100 DBG_AI_TESTING_RCA_DEFAULT << "Skipping disabled candidate action: "<< *ca_ptr;
101 continue;
102 }
103
104 if (ca_ptr->get_max_score()<=best_score) {
105 DBG_AI_TESTING_RCA_DEFAULT << "Ending candidate action evaluation loop because current score "<<best_score<<" is greater than the upper bound of score for remaining candidate actions "<< ca_ptr->get_max_score();
106 break;
107 }
108
109 DBG_AI_TESTING_RCA_DEFAULT << "Evaluating candidate action: "<< *ca_ptr;
110 double score = ca_ptr->evaluate();
111 DBG_AI_TESTING_RCA_DEFAULT << "Evaluated candidate action to score "<< score << " : " << *ca_ptr;
112
113 if (score>best_score) {
114 best_score = score;
115 best_ptr = ca_ptr;
116 }
117 }
118
119 //Execution
120 if (best_score>candidate_action::BAD_SCORE) {
121 DBG_AI_TESTING_RCA_DEFAULT << "Executing best candidate action: "<< *best_ptr;
123 best_ptr->execute();
124 executed = true;
125 if (!gs_o.is_gamestate_changed()) {
126 //this means that this CA has lied to us in evaluate()
127 //we punish it by disabling it
128 DBG_AI_TESTING_RCA_DEFAULT << "Disabling candidate action because it failed to change the game state: "<< *best_ptr;
129 best_ptr->disable();
130 //since we don't re-enable at this play_stage, if we disable this CA, other may get the chance to go.
131 } else {
132 gamestate_changed = true;
133 }
134 } else {
135 LOG_AI_TESTING_RCA_DEFAULT << "Ending candidate action evaluation loop due to best score "<< best_score<<"<="<< candidate_action::BAD_SCORE;
136 }
137 } while (executed);
138 LOG_AI_TESTING_RCA_DEFAULT << "Ended candidate action evaluation loop for side "<< get_side();
140 return gamestate_changed;
141}
142
144{
145 std::vector<std::size_t> tbr; // indexes of elements to be removed
146
147 for (std::size_t i = 0; i != candidate_actions_.size(); ++i)
148 {
149 if (candidate_actions_[i]->to_be_removed())
150 {
151 tbr.push_back(i); // so we fill the array with the indexes
152 }
153 }
154
155 for (std::size_t i = 0; i != tbr.size(); ++i)
156 {
157 // we should go downwards, so that index shifts don't affect us
158 std::size_t index = tbr.size() - i - 1; // downcounting for is not possible using unsigned counters, so we hack around
159 std::string path = "stage[" + this->get_id() + "].candidate_action[" + candidate_actions_[tbr[index]]->get_name() + "]";
160
161 config cfg = config();
162 cfg["path"] = path;
163 cfg["action"] = "delete";
164
165 ai::manager::get_singleton().modify_active_ai_for_side(this->get_side(), cfg); // we remove the CA
166 }
167
168
169// @note: this code might be more convenient, but is obviously faulty and incomplete, because of iterator invalidation rules
170// If you see a way to complete it, please contact me(Nephro).
171// for (std::vector<candidate_action_ptr>::iterator it = candidate_actions_.begin(); it != candidate_actions_.end(); )
172// {
173// if ((*it)->to_be_removed())
174// {
175// // code to remove a CA
176// std::string path = "stage[" + this->get_id() + "].candidate_action[" + (*it)->get_name() + "]";
177//
178// config cfg = config();
179// cfg["path"] = path;
180// cfg["action"] = "delete";
181//
182// ai::manager::get_singleton().modify_active_ai_for_side(this->get_side(), cfg);
183// }
184// else
185// {
186// ++it; // @note: should I modify this to a while loop?
187// }
188// }
189}
190
195
199
200} // end of namespace testing_ai_default
201
202} // end of namespace ai
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
std::vector< candidate_action_ptr > candidate_actions_
Definition stage_rca.hpp:47
candidate_action_evaluation_loop(ai_context &context, const config &cfg)
Definition stage_rca.cpp:41
bool do_play_stage()
Play the turn - implementation.
Definition stage_rca.cpp:79
bool operator()(const candidate_action_ptr &a, const candidate_action_ptr &b) const
Definition stage_rca.cpp:73
static const double BAD_SCORE
Definition rca.hpp:33
property_handler_map & property_handlers()
static void parse_candidate_action_from_config(rca_context &context, const config &cfg, std::back_insert_iterator< std::vector< candidate_action_ptr > > b)
Definition engine.cpp:61
bool is_gamestate_changed()
Check if the gamestate has changed since last reset reset is done once on construction,...
static manager & get_singleton()
Definition manager.hpp:140
void modify_active_ai_for_side(ai::side_number side, const config &cfg)
Modifies AI parameters for active AI of the given side.
Definition manager.cpp:522
virtual side_number get_side() const =0
Get the side number.
virtual std::string get_id() const
Definition stage.cpp:74
virtual config to_config() const
serialize
Definition stage.cpp:65
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
child_itors child_range(std::string_view key)
Definition config.cpp:268
AI Support engine - creating specific ai components from config.
const config * cfg
A helper class to observe the game state.
Standard logging facilities (interface).
static lg::log_domain log_ai_testing_rca_default("ai/stage/rca")
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition actions.cpp:59
static void register_vector_property(property_handler_map &property_handlers, const std::string &property, std::vector< std::shared_ptr< X > > &values, Factory &&construction_factory)
std::shared_ptr< candidate_action > candidate_action_ptr
Definition rca.hpp:145
Composite AI component.
candidate action framework
#define LOG_AI_TESTING_RCA_DEFAULT
Definition stage_rca.cpp:38
#define DBG_AI_TESTING_RCA_DEFAULT
Definition stage_rca.cpp:37
candidate action evaluator
#define i
#define b