File Coverage

blib/lib/Acme/Shotgun.pm
Criterion Covered Total %
statement 84 84 100.0
branch 42 50 84.0
condition 7 9 77.7
subroutine 10 10 100.0
pod 4 4 100.0
total 147 157 93.6


line stmt bran cond sub pod time code
1             package Acme::Shotgun;
2              
3             # ABSTRACT: Shoots holes in files
4              
5 1     1   359654 use strict;
  1         2  
  1         41  
6 1     1   5 use warnings;
  1         2  
  1         1556  
7              
8             our $VERSION = '0.03';
9              
10             sub new {
11 23     23 1 141492 my ($class, %args) = @_;
12              
13 23         352 my $self = {
14             type => 'double',
15             load => 'bird',
16             shots => undef,
17             quiet => 0,
18             debug => 0,
19             verbose => 1,
20             num_rounds => 0,
21             %args,
22             };
23              
24             die "Invalid shotgun type '$self->{type}'! Must be 'double' or 'pump'.\n"
25 23 100       271 unless $self->{type} =~ /^(?:double|pump)$/;
26              
27             die "Invalid ammo type '$self->{load}'! Must be 'bird', 'buck', or 'slug'.\n"
28 22 100       151 unless $self->{load} =~ /^(?:bird|buck|slug)$/;
29              
30 21 100       78 $self->{verbose}++ if $self->{debug};
31 21 50       85 $self->{verbose} = 0 if $self->{quiet};
32              
33 21         56 bless $self, $class;
34 21         84 $self->reload();
35              
36 21         106 return $self;
37             }
38              
39             sub reload {
40 28     28 1 1675 my $self = shift;
41              
42 28 100       101 my $num_rounds = $self->{type} eq 'pump' ? 5 : 2;
43             $num_rounds = $self->{shots}
44 28 100 100     118 if $self->{shots} && $self->{shots} < $num_rounds;
45              
46 28         56 $self->{num_rounds} = $num_rounds;
47              
48 28 50       78 print "Loading $num_rounds round(s)...\n" if $self->{verbose};
49 28         1761 print "Shotgun reloaded!\n";
50 28 50       99 $self->check() if $self->{verbose};
51              
52 28         67 return $self;
53             }
54              
55             sub check {
56 1     1 1 6 my $self = shift;
57             printf "type: %s load: %s rounds: %d\n",
58 1         12 $self->{type}, $self->{load}, $self->{num_rounds};
59 1         4 return $self;
60             }
61              
62             sub fire {
63 16     16 1 23141 my ($self, %args) = @_;
64              
65             my $target = $args{target}
66 16 100       105 or die "No target specified!\n";
67              
68 15 100       489 die "Target file does not exist: $target\n" unless -e $target;
69 14 50       128 die "Target file must be a plain file: $target\n" unless -f $target;
70 14 50       474 die "Target file must be under 1 GB: $target\n"
71             if -s $target > (1024 * 1024);
72              
73 14 100       93 if ($self->{num_rounds} == 0) {
74 1         9 print "Mag empty, you'll need to reload!\n";
75 1         8 return $self;
76             }
77              
78 13         77 while ($self->{num_rounds} > 0) {
79 44         157 $self->_shoot($target);
80 44         182 $self->{num_rounds}--;
81             }
82              
83 13         123 return $self;
84             }
85              
86             ## Private methods
87              
88             sub _shoot {
89 44     44   137 my ($self, $target) = @_;
90              
91 44 100       141 if ($self->{debug}) {
92 2         16 print "POW! (debug - no file modified)\n";
93 2         5 return;
94             }
95              
96 42 50       1748 open my $in, '<', $target or die "Unable to open target file: $target\n";
97 42         1302 my @lines = <$in>;
98 42         480 close $in;
99              
100 42         118 my $height = scalar @lines;
101 42         92 my $width = 80;
102 42         185 my $v_buffer = int rand($height);
103 42         79 my $h_buffer = int rand($width);
104 42         82 my $v_spread = 7;
105 42         64 my $h_spread = 13;
106 42         81 my $r = int rand(3);
107              
108 42         189 for my $v (0 .. $v_spread - 1) {
109 245         369 my $v_offset = $v_buffer + $v;
110 245 100       617 last if $v_offset >= $height;
111              
112 222         1659 my @line = split '', $lines[$v_offset];
113              
114 222         509 for my $h (0 .. $h_spread - 1) {
115 1286         2240 my $h_offset = $h_buffer + $h;
116 1286 100       2940 last if $h_offset >= @line;
117 1175 100       2599 last if $line[$h_offset] eq "\n";
118              
119 1131 100       2732 if ($self->{load} eq 'buck') {
    100          
120             $line[$h_offset] = ' '
121 117 100 50     596 if grep { $_ == $h } @{ _buck_pattern($r)->{$v} // [] };
  416         2071  
  117         243  
122             } elsif ($self->{load} eq 'slug') {
123             $line[$h_offset] = ' '
124 29 100 100     42 if grep { $_ == $h } @{ _slug_pattern($r)->{$v} // [] };
  33         78  
  29         56  
125             } else {
126             $line[$h_offset] = ' '
127 985 100 50     1281 if grep { $_ == $h } @{ _bird_pattern($r)->{$v} // [] };
  1813         3922  
  985         1695  
128             }
129              
130 1131         5309 $lines[$v_offset] = join('', @line);
131             }
132             }
133              
134 42 50       5100 open my $fh, '>', $target or die "Unable to open target file: $target\n";
135 42         683 print $fh $_ for @lines;
136 42         8986 close $fh;
137              
138 42 50       788 print "POW!\n" unless $self->{quiet};
139             }
140              
141             ## Shot pattern data
142              
143             sub _buck_pattern {
144 117     117   195 my $r = shift;
145 117         5516 my @patterns = (
146             { 0=>[6,7], 1=>[1,2,6,7], 2=>[1,2,11,12], 3=>[6,7,11,12],
147             4=>[1,2,6,7], 5=>[1,2,9,10], 6=>[9,10] },
148             { 0=>[1,2,9,10], 1=>[1,2,9,10], 2=>[5,6], 3=>[1,5,6,10,11],
149             4=>[1,2,10,11], 5=>[6,7], 6=>[6,7] },
150             { 0=>[5,6,7], 1=>[1,2,6,10,11], 2=>[1,2,10,11], 3=>[5,6,7],
151             4=>[1,2,6], 5=>[1,2,10], 6=>[9,10] },
152             );
153 117         1312 return $patterns[$r];
154             }
155              
156             sub _slug_pattern {
157 29     29   71 my $r = shift;
158 29         148 my @patterns = (
159             { 0=>[5,6,7], 1=>[5,6] },
160             { 0=>[5,6], 1=>[5,6,7] },
161             { 0=>[5,6], 1=>[4,5,6] },
162             );
163 29         175 return $patterns[$r];
164             }
165              
166             sub _bird_pattern {
167 985     985   1461 my $r = shift;
168 985         12586 my @patterns = (
169             { 0=>[6], 1=>[3,9], 2=>[6], 3=>[3], 4=>[1,6,10], 5=>[4], 6=>[0,7] },
170             { 0=>[6], 1=>[3,9], 2=>[6,11], 3=>[3,7,9], 4=>[6,10],
171             5=>[4,9], 6=>[7,11] },
172             { 0=>[6,9], 1=>[2,4,7], 2=>[5,9], 3=>[1,7], 4=>[6],
173             5=>[3,6,9], 6=>[5] },
174             );
175 985         7821 return $patterns[$r];
176             }
177              
178             1;
179              
180             __END__