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
107
108
109
110
111
112
113
114
115
116
117
118
119
use rand::prelude::*;
use rayon::prelude::*;
use crate::character::{Character, RawCaracsValue};
use crate::dofapi::{CaracKind, Element, Equipement};
use crate::rls::rls;
const STEPS: u32 = 100_000;
const ASSIGNABLE_CARACS: &[CaracKind] = &[
CaracKind::Vitality,
CaracKind::Wisdom,
CaracKind::Stats(Element::Air),
CaracKind::Stats(Element::Earth),
CaracKind::Stats(Element::Fire),
CaracKind::Stats(Element::Water),
];
fn walk_character<'i>(
init: &Character<'i>,
rng: &mut impl rand::Rng,
db_slot_pool: &[Vec<&'i Equipement>],
) -> Character<'i> {
let mut new = init.clone();
if rng.gen_bool(0.5) {
let slot_i = rng.gen_range(0, db_slot_pool.len());
let item = db_slot_pool[slot_i]
.choose(rng)
.expect("No available item for slot");
new.item_slots[slot_i].equip(item);
new
} else {
let kind = ASSIGNABLE_CARACS.iter().choose(rng).unwrap();
let from = ASSIGNABLE_CARACS.iter().choose(rng).unwrap();
if new
.carac_spend_or_seek(kind, *[1, 5, 10].choose(rng).unwrap(), from)
.is_err()
{
let _ = new.carac_spend_or_seek(kind, 1, from);
}
new
}
}
pub fn eval_character(
character: &Character<'_>,
target: &[(RawCaracsValue, f64)],
) -> f64 {
let target_min = |target: f64, width: f64, x: f64| -> f64 {
1. / (1. + (-4. * (x - target) / width).exp())
};
let target_zero = |width: f64, x: f64| -> f64 {
let nx = 2. * x / width;
(1. - (nx.exp() - (-nx).exp()) / (nx.exp() + (-nx).exp())).powi(2)
};
let caracs = character.get_caracs();
let targets_weight: f64 = target
.iter()
.map(|(target_type, target_val)| {
if let Ok(smithmage_weight) = target_type.approx_smithmage_weight()
{
let val = caracs.eval(target_type);
let width = 100. / smithmage_weight;
let invert =
if target_type.is_decreasing() { -1. } else { 1. };
target_min(*target_val * invert, width, val * invert)
} else {
1.
}
})
.product();
let count_item_conflicts = character.count_item_conflicts();
let conflicts_weight = 0.05f64.powi(count_item_conflicts.into());
let conditions_weight = target_zero(
200.,
character.condition_overflow(&character.all_conditions()),
);
targets_weight * conflicts_weight * conditions_weight
}
pub fn optimize_character<'i>(
init: Character<'i>,
count: u64,
target: &[(RawCaracsValue, f64)],
db_equipements: &'i [Equipement],
) -> Vec<Character<'i>> {
let slot_pool: Vec<_> = init
.item_slots
.iter()
.map(|slot| {
db_equipements
.iter()
.filter(|item| slot.get_allowed().contains(&item.item_type))
.collect::<Vec<_>>()
})
.collect();
(0..count)
.into_par_iter()
.map_init(rand::thread_rng, |mut rng, _| {
rls(
init.clone(),
STEPS,
&mut rng,
|character| eval_character(character, target),
|character, rng| walk_character(&character, rng, &slot_pool),
)
})
.collect()
}