-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDecision.h
106 lines (95 loc) · 3.64 KB
/
Decision.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#pragma once
#include <chrono>
#include <functional>
#include <string>
#include <vector>
#include "Consideration.h"
class Decision;
using Action = std::function<void(Decision&)>;
/**/
enum class UtilityScore : int {
Ignore = 0,
SlightlyUseful = 1,
Useful = 2,
VeryUseful = 3,
MostUseful = 4
};
/** Container for an Action, that can be performed when it seems useful.
*
* A Decision is used in the DecisionEngine when it has the highest total
* utility score of all active Decisions. The utility score indicates how
* useful it is in the current situation to execute a specific Action.
*
* The total utility score is computed as a function of this Decision's
* utility, and the scores of its Considerations with Decision::computeScore().
*/
class Decision {
public:
using Clock = std::chrono::steady_clock;
Decision(const std::string& name,
const std::string& description,
UtilityScore utility,
std::vector<Consideration> considerations,
const Action& action)
: name_(name),
description_(description),
utility_(utility),
considerations_(considerations),
action_(action)
{}
Decision() = default;
Decision(const Decision& other) = default;
Decision& operator=(const Decision& other) = default;
/** Calculate the 'usefulness' of a Decision.
*
* This score is a multiplication of its utility and the scores of all
* its Considerations. Then, the score will be adjusted with a weighing
* factor for the number of Considerations.
*
* Because all Considerations will return a score between 0 and 1, we
* can expect this number to become smaller for Decisions that have more
* Considerations. For example, a Decision A with three Considerations
* scoring each 0.9 and utility of 1 has a score of 1 * 0.9 * 0.9 * 0.9 =
* 0.729. Another Decision B with 1 Consideration scoring 0.75 has a
* total score of 1 * 0.75 = 0.75. Intuitively, A should have a higher
* score; each of its Considerations indicate that it is very important.
* The weighing factor adjusts for this.
*/
float computeScore() const {
const float modification_factor = 1.f - (1.f / float(considerations_.size()));
float total_score = static_cast<float>(utility_);
float score = 0.f;
for (auto& consideration : considerations_) {
score = consideration.computeScore();
total_score *= score + ((1.f - score) * modification_factor * score);
if (total_score < 1e-6f) break;
}
return total_score;
}
const std::string& getName() const { return name_; }
const std::string& getDescription() const { return description_; }
UtilityScore getUtility() const { return utility_; }
const Action& getAction() const { return action_; }
const Clock::time_point getExecutionTimestamp() const { return execution_timestamp_; }
const Clock::duration getTimeSinceExecution() const {
return getTimeSinceExecution(std::chrono::steady_clock::now());
}
const Clock::duration getTimeSinceExecution(const Clock::time_point& timestamp) const {
return timestamp - execution_timestamp_;
}
bool isNeverExecuted() const {
return execution_timestamp_.time_since_epoch().count() == 0;
}
/** Execute the Action associated with this Decision. */
void execute() {
execution_timestamp_ = std::chrono::steady_clock::now();
action_(*this);
}
private:
std::string name_;
std::string description_;
UtilityScore utility_;
std::vector<Consideration> considerations_;
Action action_;
std::chrono::steady_clock::time_point execution_timestamp_;
};