deepwoken_reqparse/model/
req.rs1use core::fmt;
2use std::{borrow::Borrow, collections::{BTreeSet, HashSet}};
3
4use crate::{Stat, util::statmap::StatMap};
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
7pub enum Reducability {
8 Reducible,
9 Strict,
10}
11
12impl fmt::Display for Reducability {
13 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
14 match self {
15 Reducability::Reducible => write!(f, "r"),
16 Reducability::Strict => write!(f, "s"),
17 }
18 }
19}
20
21pub type StatSet = BTreeSet<Stat>;
22
23#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
24pub struct Atom {
25 pub(crate) reducability: Reducability,
26 pub(crate) value: i64,
27 pub(crate) stats: StatSet,
29}
30
31impl Atom {
32 pub fn new(r: Reducability) -> Self {
33 Self {
34 reducability: r,
35 value: 0,
36 stats: BTreeSet::new(),
37 }
38 }
39
40 pub fn strict() -> Self {
41 Self {
42 reducability: Reducability::Strict,
43 value: 0,
44 stats: BTreeSet::new(),
45 }
46 }
47
48 pub fn reducible() -> Self {
49 Self {
50 reducability: Reducability::Reducible,
51 value: 0,
52 stats: BTreeSet::new(),
53 }
54 }
55
56 pub fn value(mut self, v: i64) -> Self {
57 self.value = v;
58 self
59 }
60
61 pub fn reducability(mut self, r: Reducability) -> Self {
62 self.reducability = r;
63 self
64 }
65
66 pub fn stat(mut self, stat: Stat) -> Self {
68 self.stats.insert(stat);
69 self
70 }
71
72 pub fn add_stat(&mut self, stat: Stat) {
73 self.stats.insert(stat);
74 }
75
76 pub fn satisfied_by(&self, stats: &StatMap) -> bool {
77 let sum: i64 = self
78 .stats
79 .iter()
80 .map(|s| {
81 if s == &Stat::Total {
82 stats.cost()
83 } else {
84 stats.get(s)
85 }
86 })
87 .sum();
88
89 sum >= self.value
90 }
91
92 pub fn is_empty(&self) -> bool {
94 self.stats.is_empty() && self.value == 0
95 }
96}
97
98impl fmt::Display for Atom {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 if self.stats.len() == 1 {
101 write!(
102 f, "{}{} {}",
103 self.value,
104 self.reducability,
105 self.stats.first().unwrap().short_name()
106 )
107 } else {
108 let sum_expr = self
110 .stats
111 .iter()
112 .map(|s| s.short_name().to_string())
113 .collect::<Vec<String>>()
114 .join(" + ");
115
116 write!(f, "{} = {}{}", sum_expr, self.value, self.reducability)
117 }
118 }
119}
120
121#[derive(Clone, Debug, PartialEq, Eq, Hash)]
122pub enum ClauseType {
123 And,
124 Or,
125}
126
127#[derive(Clone, Debug, Hash, PartialEq, Eq)]
128pub struct Clause {
129 pub(crate) clause_type: ClauseType,
130 pub(crate) atoms: BTreeSet<Atom>,
131}
132
133impl Clause {
134 pub fn new(clause_type: ClauseType) -> Self {
135 Self {
136 clause_type,
137 atoms: BTreeSet::new(),
138 }
139 }
140
141 pub fn and() -> Self {
142 Self {
143 clause_type: ClauseType::And,
144 atoms: BTreeSet::new(),
145 }
146 }
147
148 pub fn or() -> Self {
149 Self {
150 clause_type: ClauseType::Or,
151 atoms: BTreeSet::new(),
152 }
153 }
154
155 pub fn clause_type(mut self, ct: ClauseType) -> Self {
156 self.clause_type = ct;
157 self
158 }
159
160 pub fn atoms(&self) -> &BTreeSet<Atom> {
161 &self.atoms
162 }
163
164 pub fn atoms_mut(&mut self) -> &mut BTreeSet<Atom> {
165 &mut self.atoms
166 }
167
168 pub fn insert(mut self, stats: StatSet, mut atom: Atom) -> Self {
169 atom.stats = stats;
170 self.atoms.insert(atom);
171 self
172 }
173
174 pub fn atom(mut self, atom: Atom) -> Self {
175 self.atoms.insert(atom);
176 self
177 }
178
179 pub fn add_atom(&mut self, atom: Atom) {
180 self.atoms.insert(atom);
181 }
182
183 pub fn satisfied_by(&self, stats: &StatMap) -> bool {
184 match self.clause_type {
185 ClauseType::And => self.atoms.iter().all(|atom| atom.satisfied_by(stats)),
186 ClauseType::Or => self.atoms.iter().any(|atom| atom.satisfied_by(stats)),
187 }
188 }
189
190 pub fn is_empty(&self) -> bool {
191 !self.atoms().iter().any(|a| !a.is_empty())
192 }
193}
194
195impl fmt::Display for Clause {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 let joiner = match self.clause_type {
198 ClauseType::And => ", ",
199 ClauseType::Or => " OR ",
200 };
201
202 let atom_strs: Vec<String> = self
203 .atoms
204 .iter()
205 .filter(|a| !a.is_empty())
206 .map(|atom| format!("{}", atom))
207 .collect();
208
209 write!(f, "{}", atom_strs.join(joiner))
210 }
211}
212
213#[derive(Clone, Debug, Hash)]
214pub struct Requirement {
215 pub(crate) name: Option<String>,
217 pub(crate) prereqs: Vec<String>,
219
220 pub(crate) clauses: Vec<Clause>,
221}
222
223impl PartialEq for Requirement {
224 fn eq(&self, other: &Self) -> bool {
225 if self.clauses.len() != other.clauses.len() {
226 return false;
227 }
228
229 self.clauses.iter().all(|c| other.clauses.contains(c))
231 && other.clauses.iter().all(|c| self.clauses.contains(c))
232 && self.name_or_default() == other.name_or_default()
233 }
235}
236
237impl Eq for Requirement {}
238
239impl Requirement {
240 pub fn new() -> Self {
241 Self {
242 name: None,
243 prereqs: Vec::new(),
244 clauses: Vec::new(),
245 }
246 }
247
248 pub fn add_clause(&mut self, clause: Clause) -> &mut Self {
249 self.clauses.push(clause);
250 self
251 }
252
253 pub fn add_prereq(&mut self, prereq: &str) -> &mut Self {
254 self.prereqs.push(prereq.to_string());
255 self
256 }
257
258 pub fn name(&mut self, name: &str) -> &mut Self {
259 self.name = Some(name.to_string());
260 self
261 }
262
263 pub fn name_or_default(&self) -> String {
264 match &self.name {
265 Some(n) => n.clone(),
266 None => self.to_string(),
267 }
268 }
269
270 pub fn iter(&self) -> impl Iterator<Item = &Clause> {
271 self.clauses.iter()
272 }
273
274 pub fn and_iter(&self) -> impl Iterator<Item = &Clause> {
275 self.clauses
276 .iter()
277 .filter(|c| c.clause_type == ClauseType::And)
278 }
279
280 pub fn or_iter(&self) -> impl Iterator<Item = &Clause> {
281 self.clauses
282 .iter()
283 .filter(|c| c.clause_type == ClauseType::Or)
284 }
285
286 pub fn atoms(&self) -> impl Iterator<Item = &Atom> {
287 self.clauses.iter().flat_map(|clause| clause.atoms.iter())
288 }
289
290 pub fn add_to_all(&mut self, val: i64) -> &mut Self {
291 for clause in &mut self.clauses {
293 clause.atoms = clause
294 .atoms
295 .iter()
296 .map(|atom| {
297 let mut new_atom = atom.clone();
298 new_atom.value += val;
299 new_atom.value = new_atom.value.clamp(0, 100);
300 new_atom
301 })
302 .collect();
303 }
304 self
305 }
306
307 pub fn strict_atoms(&self) -> impl Iterator<Item = &Atom> {
308 self.clauses.iter().flat_map(|clause| {
309 clause
310 .atoms
311 .iter()
312 .filter(|atom| atom.reducability == Reducability::Strict)
313 })
314 }
315
316 pub fn used_stats(&self) -> HashSet<Stat> {
318 self.atoms().fold(HashSet::new(), |mut acc, atom| {
319 for stat in &atom.stats {
320 if stat == &Stat::Total {
321 continue;
322 }
323
324 acc.insert(stat.clone());
325 }
326 acc
327 })
328 }
329
330 pub fn satisfied_by(&self, stats: &StatMap) -> bool {
331 self.clauses.iter().all(|clause| clause.satisfied_by(stats))
332 }
333
334 pub fn is_empty(&self) -> bool {
336 !self.clauses.iter().any(|c| !c.is_empty())
337 }
338}
339
340impl From<Clause> for Requirement {
341 fn from(clause: Clause) -> Self {
342 Self {
343 name: None,
344 prereqs: Vec::new(),
345 clauses: vec![clause],
346 }
347 }
348}
349
350impl fmt::Display for Requirement {
351 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
352 if !self.prereqs.is_empty() {
353 write!(f, "{} => ", self.prereqs.join(", "))?;
354 }
355 if let Some(name) = &self.name {
356 write!(f, "{} := ", name)?;
357 }
358 if self.is_empty() {
359 write!(f, "()")
360 } else {
361 let clause_strs: Vec<String> = self
362 .clauses
363 .iter()
364 .filter(|clause| !clause.is_empty())
365 .map(|clause| clause.to_string())
366 .collect();
367
368 write!(f, "{}", clause_strs.join(", "))
369 }
370 }
371}
372
373pub trait ReqVecExt {
375 fn map_names<F>(&mut self, f: F)
376 where
377 F: Fn(&str) -> String;
378}
379
380impl ReqVecExt for Vec<Requirement> {
381 fn map_names<F>(&mut self, f: F)
382 where
383 F: Fn(&str) -> String,
384 {
385 for req in self.iter_mut() {
386 req.name = req.name.as_ref().map(|name| f(&name));
387
388 req.prereqs = req.prereqs.iter().map(|name| f(&name)).collect();
389 }
390 }
391}
392
393pub trait ReqIterExt {
394 fn max_map(self) -> StatMap;
395
396 fn max_total_req(self) -> i64;
397}
398
399impl<I> ReqIterExt for I
400where
401 I: Iterator,
402 I::Item: Borrow<Requirement>,
403{
404 fn max_map(self) -> StatMap {
405 let mut maxes: StatMap = StatMap::new();
406
407 for req in self {
408 let req = req.borrow();
409
410 for atom in req.atoms() {
411 for &stat in &atom.stats {
412 if stat == Stat::Total {
413 continue;
414 }
415
416 maxes
419 .entry(stat)
420 .and_modify(|cur| *cur = (*cur).max(atom.value))
421 .or_insert(atom.value);
422 }
423 }
424 }
425
426 maxes
427 }
428
429 fn max_total_req(self) -> i64 {
430 let mut max: i64 = 0;
431
432 for req in self {
433 let req = req.borrow();
434
435 for atom in req.atoms() {
436 if atom.stats.contains(&Stat::Total) {
437 max = max.max(atom.value);
438 }
439 }
440 }
441
442 max
443 }
444}
445
446#[derive(Clone, Copy, Debug)]
447pub enum Timing {
448 Free,
449 Post,
450}