ugg_types/
arena_overview.rs

1use std::fmt;
2
3use serde::Serialize;
4use serde::de::{Deserialize, Deserializer, IgnoredAny, SeqAccess, Visitor};
5
6use crate::default_overview::{Abilities, Items, LateItem};
7use crate::overview::handle_unknown;
8
9#[derive(Debug, Clone, Serialize)]
10pub struct ArenaOverviewData {
11    pub starting_items: Items,
12    pub core_items: Items,
13    pub abilities: Abilities,
14    pub item_4_options: Vec<LateItem>,
15    pub item_5_options: Vec<LateItem>,
16    pub item_6_options: Vec<LateItem>,
17    pub consumables: Vec<LateItem>,
18    pub prismatic_items: Vec<PrismaticItem>,
19    pub wins: i64,
20    pub matches: i64,
21    pub low_sample_size: bool,
22    pub champion_synergies: Vec<ChampionSynergy>,
23    pub augments: Vec<Augment>,
24}
25
26#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
27pub struct PrismaticItem {
28    pub id: i64,
29    pub matches: i64,
30    pub wins: i64,
31}
32
33impl PrismaticItem {
34    #[must_use]
35    #[allow(clippy::cast_precision_loss)]
36    pub fn winrate(&self) -> f64 {
37        if self.matches <= 0 {
38            return 0f64;
39        }
40        (self.wins as f64) / (self.matches as f64)
41    }
42}
43
44impl PartialOrd for PrismaticItem {
45    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
46        Some(self.cmp(other))
47    }
48}
49
50impl Ord for PrismaticItem {
51    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
52        self.winrate().total_cmp(&other.winrate())
53    }
54}
55
56impl<'de> Deserialize<'de> for PrismaticItem {
57    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58    where
59        D: Deserializer<'de>,
60    {
61        struct PrismaticItemVisitor;
62
63        impl<'de> Visitor<'de> for PrismaticItemVisitor {
64            type Value = PrismaticItem;
65
66            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
67                formatter.write_str("prismatic items")
68            }
69
70            fn visit_seq<V>(self, mut visitor: V) -> Result<PrismaticItem, V::Error>
71            where
72                V: SeqAccess<'de>,
73            {
74                let id = handle_unknown(visitor.next_element::<i64>());
75
76                // no clue what these two are
77                let _ = visitor.next_element::<IgnoredAny>().is_ok();
78                let _ = visitor.next_element::<IgnoredAny>().is_ok();
79
80                let wins = handle_unknown(visitor.next_element::<i64>());
81                let matches = handle_unknown(visitor.next_element::<i64>());
82                Ok(PrismaticItem { id, matches, wins })
83            }
84        }
85
86        deserializer.deserialize_seq(PrismaticItemVisitor)
87    }
88}
89
90#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
91pub struct ChampionSynergy {
92    pub id: i64,
93    top_four: i64,
94    picked: i64,
95    first: i64,
96    sum_of_placements: i64,
97}
98
99impl ChampionSynergy {
100    #[must_use]
101    #[allow(clippy::cast_precision_loss)]
102    pub fn top_four_rate(&self) -> f64 {
103        if self.picked <= 0 {
104            return 0f64;
105        }
106        (self.top_four as f64) / (self.picked as f64)
107    }
108}
109
110impl PartialOrd for ChampionSynergy {
111    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
112        Some(self.cmp(other))
113    }
114}
115
116impl Ord for ChampionSynergy {
117    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
118        self.top_four_rate().total_cmp(&other.top_four_rate())
119    }
120}
121
122impl<'de> Deserialize<'de> for ChampionSynergy {
123    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
124    where
125        D: Deserializer<'de>,
126    {
127        struct ChampionSynergiesVisitor;
128
129        impl<'de> Visitor<'de> for ChampionSynergiesVisitor {
130            type Value = ChampionSynergy;
131
132            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
133                formatter.write_str("champion synergy")
134            }
135
136            fn visit_seq<V>(self, mut visitor: V) -> Result<ChampionSynergy, V::Error>
137            where
138                V: SeqAccess<'de>,
139            {
140                let id = handle_unknown(visitor.next_element::<i64>());
141                let top_four = handle_unknown(visitor.next_element::<i64>());
142                let picked = handle_unknown(visitor.next_element::<i64>());
143                let first = handle_unknown(visitor.next_element::<i64>());
144                let sum_of_placements = handle_unknown(visitor.next_element::<i64>());
145
146                Ok(ChampionSynergy {
147                    id,
148                    top_four,
149                    picked,
150                    first,
151                    sum_of_placements,
152                })
153            }
154        }
155
156        deserializer.deserialize_seq(ChampionSynergiesVisitor)
157    }
158}
159
160#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
161pub struct Augment {
162    pub id: i64,
163    wins: i64,
164    matches: i64,
165}
166
167impl Augment {
168    #[must_use]
169    #[allow(clippy::cast_precision_loss)]
170    pub fn winrate(&self) -> f64 {
171        if self.matches <= 0 {
172            return 0f64;
173        }
174        (self.wins as f64) / (self.matches as f64)
175    }
176}
177
178impl PartialOrd for Augment {
179    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
180        Some(self.cmp(other))
181    }
182}
183
184impl Ord for Augment {
185    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
186        self.winrate().total_cmp(&other.winrate())
187    }
188}
189
190impl<'de> Deserialize<'de> for Augment {
191    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
192    where
193        D: Deserializer<'de>,
194    {
195        struct AugmentVisitor;
196
197        impl<'de> Visitor<'de> for AugmentVisitor {
198            type Value = Augment;
199
200            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
201                formatter.write_str("augment")
202            }
203
204            fn visit_seq<V>(self, mut visitor: V) -> Result<Augment, V::Error>
205            where
206                V: SeqAccess<'de>,
207            {
208                let id = handle_unknown(visitor.next_element::<i64>());
209                let wins = handle_unknown(visitor.next_element::<i64>());
210                let matches = handle_unknown(visitor.next_element::<i64>());
211
212                Ok(Augment { id, wins, matches })
213            }
214        }
215
216        deserializer.deserialize_seq(AugmentVisitor)
217    }
218}
219
220impl<'de> Deserialize<'de> for ArenaOverviewData {
221    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222    where
223        D: Deserializer<'de>,
224    {
225        struct ArenaOverviewDataVisitor;
226
227        impl<'de> Visitor<'de> for ArenaOverviewDataVisitor {
228            type Value = ArenaOverviewData;
229
230            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
231                formatter.write_str("overview data")
232            }
233
234            fn visit_seq<V>(self, mut visitor: V) -> Result<ArenaOverviewData, V::Error>
235            where
236                V: SeqAccess<'de>,
237            {
238                // winrate + extra stuff?
239                let _ = visitor.next_element::<IgnoredAny>().is_ok();
240
241                // winrate + extra stuff?
242                let _ = visitor.next_element::<IgnoredAny>().is_ok();
243
244                let starting_items = visitor
245                    .next_element::<Items>()?
246                    .ok_or(serde::de::Error::custom("Could not parse starting items."))?;
247                let core_items = visitor
248                    .next_element::<Items>()?
249                    .ok_or(serde::de::Error::custom("Could not parse core items."))?;
250                let abilities = visitor
251                    .next_element::<Abilities>()?
252                    .ok_or(serde::de::Error::custom("Could not parse abilities."))?;
253
254                let late_items = visitor
255                    .next_element::<(
256                        Vec<LateItem>,
257                        Vec<LateItem>,
258                        Vec<LateItem>,
259                        Vec<LateItem>,
260                        Vec<PrismaticItem>,
261                        // just a random array?
262                        IgnoredAny,
263                    )>()?
264                    .ok_or(serde::de::Error::custom(
265                        "Could not parse late / prismatic items.",
266                    ))?;
267
268                // Prismatic items are ordered by pickrate, not winrate
269                // reorder them
270                let mut prismatic_items = late_items.4;
271                prismatic_items.sort_by(|a, b| b.cmp(a));
272
273                let match_info = visitor.next_element::<(i64, i64)>()?.unwrap_or_default();
274                let low_sample_size = match_info.1 < 1000;
275
276                // this is the original low sample size value, it's always false though, so ignore.
277                let _ = visitor.next_element::<IgnoredAny>().is_ok();
278
279                // I think this is where shards would end up? It's junk data.
280                let _ = visitor.next_element::<IgnoredAny>().is_ok();
281
282                // empty array
283                let _ = visitor.next_element::<IgnoredAny>().is_ok();
284
285                // These are ordered by pick rate which seems like a bad default
286                // reorder them by their top 4 rate instead.
287                let mut champion_synergies = visitor
288                    .next_element::<Vec<ChampionSynergy>>()?
289                    .unwrap_or_default();
290                champion_synergies.sort_by(|a, b| b.cmp(a));
291
292                // Augments are ordered by pickrate in the UI, but winrate
293                // in the data. Pick rate is presumably influenced by the tier
294                // of the augment. Sort by winrate, but consider bucketing these
295                // by tier if the information is available.
296                let mut augments = visitor.next_element::<Vec<Augment>>()?.unwrap_or_default();
297                augments.sort_by(|a, b| b.cmp(a));
298
299                // Don't know what this is yet
300                while let Some(IgnoredAny) = visitor.next_element()? {}
301
302                let arena_overview_data = ArenaOverviewData {
303                    starting_items,
304                    core_items,
305                    abilities,
306                    item_4_options: late_items.0,
307                    item_5_options: late_items.1,
308                    item_6_options: late_items.2,
309                    consumables: late_items.3,
310                    prismatic_items,
311                    wins: match_info.0,
312                    matches: match_info.1,
313                    low_sample_size,
314                    champion_synergies,
315                    augments,
316                };
317                Ok(arena_overview_data)
318            }
319        }
320
321        deserializer.deserialize_seq(ArenaOverviewDataVisitor)
322    }
323}