From 4fc02e2d9be5e421fd9ae3839aa8e2d8b0a3b6da Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Mon, 26 Aug 2024 00:04:26 +0200 Subject: impl PartialOrd for gamedata::Encounter --- src/gamedata.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/gamedata.rs b/src/gamedata.rs index f880ba6..9f0f9d4 100644 --- a/src/gamedata.rs +++ b/src/gamedata.rs @@ -345,6 +345,53 @@ impl Display for Encounter { } } +macro_rules! ordering_group { + ($self:expr, $other:expr ; $($group:expr;)*) => { + $( + let group = &$group; + if let Some(pos_a) = group.iter().position(|x| *x == $self) { + if let Some(pos_b) = group.iter().position(|x| *x == $other) { + return Some(pos_a.cmp(&pos_b)); + } + } + )* + } +} + +// We could implement (or even derive) PartialOrd for all bosses, but there is no "canonical" order +// (do fractal bosses come before raid bosses or after?). This implements a conservative order, +// such that at least the bosses that appear in a single raid wing/fractal are ordered relative to +// each other, but there is no order between different wings. This still allows some useful cases +// (like sorting bosses within a wing). +// +// There are some edge cases, like the Wing 5 events (Eater/Eyes/Broken King) and Adina/Sabir, +// which technically happen at the same time, but we're just defining a fixed order for those here. +// +// In the future, this implementation might be expanded to a total order, but by keeping it a +// partial order for now we can implement the cases that are clear (bosses within a wing) while +// allowing future extensions in a backwards-compatible way (once we figured out how we want to +// order the different wings/strikes/fractals). +impl PartialOrd for Encounter { + fn partial_cmp(&self, other: &Self) -> Option { + use Encounter::*; + ordering_group!(*self, *other; + // Raids + [ValeGuardian, Gorseval, Sabetha]; + [Slothasor, BanditTrio, Matthias]; + [KeepConstruct, TwistedCastle, Xera]; + [Cairn, MursaatOverseer, Samarog, Deimos]; + [SoullessHorror, RiverOfSouls, BrokenKing, EaterOfSouls, StatueOfDarkness, VoiceInTheVoid]; + [ConjuredAmalgamate, TwinLargos, Qadim]; + [CardinalAdina, CardinalSabir, QadimThePeerless]; + // Shattered Observatory CM + [Skorvald, Artsariiv, Arkk]; + // Nightmare CM + [MAMA, Siax, Ensolyss]; + ); + None + } +} + /// Enum containing all boss IDs. /// /// For a high-level event categorization, take a look at the [`Encounter`] enum. The IDs listed @@ -1141,6 +1188,21 @@ mod tests { } } + #[test] + fn test_encounter_partial_cmp() { + // Just a few example cases + use Encounter::*; + assert!(ValeGuardian < Gorseval); + assert!(ValeGuardian < Sabetha); + assert!(Gorseval < Sabetha); + assert!(ValeGuardian.partial_cmp(&Slothasor).is_none()); + + assert!(VoiceInTheVoid > SoullessHorror); + assert!(Qadim > TwinLargos); + + assert!(Ankka.partial_cmp(&Dragonvoid).is_none()); + } + #[test] fn test_boss_parsing_ok() { use Boss::*; -- cgit v1.2.3