1use crate::koi::{KoiDevelopment, RecentActivity};
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub enum Behavior {
8 DeepRest,
10 Rest,
12 SurfaceFeeding,
14 Dart,
16 Cruise,
18}
19
20const DEEP_REST_QUIET_DAYS: u32 = 60;
21const REST_QUIET_DAYS: u32 = 14;
22const DART_MILESTONES: [u64; 6] = [1_000, 5_000, 10_000, 25_000, 50_000, 100_000];
24const DART_COMPARED_WEEKS: usize = 52;
26const DART_MIN_COMPLETE_WEEKS: usize = 10;
28const DART_WEEK_SHARE_DENOMINATOR: usize = 10;
30const DAYS_PER_WEEK: usize = 7;
31
32pub fn select_behavior(koi_development: &KoiDevelopment) -> Behavior {
51 let recent_activity = &koi_development.recent_activity;
52 if recent_activity.quiet_day_count >= DEEP_REST_QUIET_DAYS {
53 Behavior::DeepRest
54 } else if recent_activity.quiet_day_count >= REST_QUIET_DAYS {
55 Behavior::Rest
56 } else if is_first_contribution_of_week(recent_activity) {
57 Behavior::SurfaceFeeding
58 } else if has_crossed_milestone(koi_development.total_contributions, recent_activity)
59 || is_finished_busy_week(recent_activity)
60 {
61 Behavior::Dart
62 } else {
63 Behavior::Cruise
64 }
65}
66
67fn is_first_contribution_of_week(recent_activity: &RecentActivity) -> bool {
68 matches!(
69 recent_activity.observation_week_contributions.split_last(),
70 Some((&observed_day, earlier_days))
71 if observed_day > 0 && earlier_days.iter().all(|&day_contributions| day_contributions == 0)
72 )
73}
74
75fn has_crossed_milestone(total_contributions: u64, recent_activity: &RecentActivity) -> bool {
76 let earlier_total = total_contributions.saturating_sub(recent_activity.recent_contributions);
77 DART_MILESTONES
78 .iter()
79 .any(|&milestone| earlier_total < milestone && milestone <= total_contributions)
80}
81
82fn is_finished_busy_week(recent_activity: &RecentActivity) -> bool {
83 let finished_week = &recent_activity.observation_week_contributions;
84 let complete_weeks = &recent_activity.complete_week_contributions;
85 let compared_weeks =
86 &complete_weeks[complete_weeks.len().saturating_sub(DART_COMPARED_WEEKS)..];
87 let week_total: u64 = finished_week.iter().sum();
88 let matching_week_count = compared_weeks
89 .iter()
90 .filter(|&&week_contributions| week_contributions >= week_total)
91 .count();
92 finished_week.len() == DAYS_PER_WEEK
93 && compared_weeks.len() >= DART_MIN_COMPLETE_WEEKS
94 && matching_week_count * DART_WEEK_SHARE_DENOMINATOR <= compared_weeks.len()
95}