switch day diff calculation to epoch based so that "N hours short of M days" becomes...
[engit/Iron-Munger.git] / lib / IronMunger / Calculate.pm
1 package IronMunger::Calculate;
2
3 use strict;
4 use warnings;
5 use List::Util qw(min);
6 use autobox;
7 use autobox::DateTime::Duration;
8 use signatures;
9
10 use Sub::Exporter -setup => {
11   exports => [
12     qw(successful_sequential_posts days_remaining_to_post level_for_post_count)
13   ]
14 };
15
16 sub day_diff ($dt1, $dt2) {
17   $dt1 = $dt1->at if $dt1->isa('IronMunger::Post');
18   $dt2 = $dt2->at if $dt2->isa('IronMunger::Post');
19   my $seconds = $dt2->epoch - $dt1->epoch;
20   $seconds = -$seconds if $seconds < 0;
21   int($seconds / 86400);
22   #$dt1->delta_days($dt2)->delta_days;
23 }
24
25 sub check_post_gap ($aperture, $days, @posts) {
26   return scalar @posts if @posts <= $aperture;
27   my @next_post = splice(@posts, 0, $aperture);
28   if (day_diff(DateTime->now, $next_post[-1]) > $days) {
29     while (@next_post && day_diff(DateTime->now, $next_post[-1]) > $days) {
30       pop @next_post;
31     }
32     return scalar(@next_post);
33   }
34   my $success = $aperture;
35   foreach my $post (@posts) {
36     if (day_diff($next_post[0],$post) > $days) {
37       return $success;
38     }
39     $success++;
40     shift(@next_post);
41     push(@next_post, $post);
42   }
43   return $success;
44 }
45
46 sub check_time_remaining ($aperture, $days, @posts) {
47   my @consider = @posts > $aperture ? @posts[0 .. $aperture - 1] : @posts;
48   foreach my $cand (reverse @consider) {
49     my $days_ago = day_diff(DateTime->now, $cand);
50     return $days - $days_ago if $days > $days_ago;
51   }
52   return $days;
53 }
54
55 sub check_both ($check, @posts) {
56   return min(
57     $check->(1, 10, @posts), # 10 days between posts
58     $check->(4, 32, @posts), # 4 posts within any given 32 days
59   );
60 }
61
62 sub successful_sequential_posts (@posts) {
63   return check_both(\&check_post_gap, @posts);
64 }
65
66 sub days_remaining_to_post (@posts) {
67   return check_both(\&check_time_remaining, @posts);
68 }
69
70 sub level_for_post_count($count) {
71   return 'paper' if $count < 4;
72   return 'stone' if $count < 12;
73   return 'bronze' if $count < 36;
74   return 'iron';
75 }
76
77 1;