File Coverage

blib/lib/Games/Roguelike/Caves.pm
Criterion Covered Total %
statement 71 71 100.0
branch 34 42 80.9
condition 21 25 84.0
subroutine 6 6 100.0
pod 0 2 0.0
total 132 146 90.4


line stmt bran cond sub pod time code
1             package Games::Roguelike::Caves;
2              
3             #use 5.008000;
4 1     1   24067 use strict;
  1         2  
  1         39  
5 1     1   5 use warnings;
  1         2  
  1         31  
6              
7             #require Exporter;
8              
9 1     1   5 use base 'Exporter';
  1         5  
  1         600  
10              
11             our @EXPORT = qw(
12             generate_cave
13             outline_walls
14             );
15              
16             our $VERSION = '0.01';
17              
18             #use cellular automata to carve out a decent cave
19             #initially contain 45% walls at random
20             #a tile becomes or remains a wall if the 3x3 region centered on it contains at least 5 walls.
21             #use 1 to represent wall, 0 is space
22             sub generate_cave{
23 1     1 0 10 my ($w, $h, $iterations, $percentWalls, $wall, $floor) = @_;
24 1 50 33     9 die 'dimensions?' unless ($w and $h);
25 1   50     6 $iterations ||= 12;
26 1   50     5 $percentWalls ||= .45;
27 1 50       5 $percentWalls /= 100 if $percentWalls>1; # in case it's .45 or something
28 1 50       5 $wall = ' ' unless defined $wall;
29 1 50       8 $floor = '.' unless defined $floor;
30            
31 1         2 my @terrain = ();
32 1         2 my @nextStep = ();
33 1         5 for my $x (0..$w-1){
34 50         65 for my $y (0..$h-1){
35 1000 100       1729 $terrain[$y][$x] = rand()<$percentWalls ? 1 : 0;
36             }
37             }
38 1         3 for (1..$iterations){
39 12         19 for my $x (0..$w-1){
40 600         857 for my $y (0..$h-1){
41 12000 100 100     73157 if ( !$x or !$y or $x==$w-1 or $y==$h-1){
      100        
      100        
42             #we're at edge: be wall.
43 1632         1655 $nextStep[$y][$x] = 1;
44 1632         2447 next;
45             }
46 10368         9355 my $c=0;
47             #count walls in 3x3 square
48 10368         11350 $c += $terrain[$y-1][$x-1];
49 10368         10361 $c += $terrain[$y-1][$x];
50 10368         10521 $c += $terrain[$y-1][$x+1];
51 10368         13369 $c += $terrain[$y] [$x-1];
52 10368         9322 $c += $terrain[$y] [$x];
53 10368         9795 $c += $terrain[$y] [$x+1];
54 10368         11403 $c += $terrain[$y+1][$x-1];
55 10368         10689 $c += $terrain[$y+1][$x];
56 10368         10673 $c += $terrain[$y+1][$x+1];
57 10368 100       18200 $nextStep[$y][$x] = $c>4 ? 1 : 0;
58             }
59             }
60             #swap arrays using typeglobs
61             #(*terrain,*nextStep) = (*nextStep,*terrain)
62 12         56 my @tmp = @terrain;
63 12         41 @terrain = @nextStep;
64 12         49 @nextStep = @tmp;
65             }
66             #translate to cave wall or floor
67 1         6 for my $x (0..$w-1){
68 50         71 for my $y (0..$h-1){
69 1000 100       1779 $terrain[$y][$x] = $terrain[$y][$x] ? $wall : $floor;
70             #print STDOUT $terrain[$y][$x];
71             }
72             #print STDOUT "\n";
73             }
74 1         53 return \@terrain;
75             }
76              
77             sub outline_walls{
78 1     1 0 1117 my ($terrain, $wall, $floor) = @_;
79 1         3 my $h = $#$terrain + 1;
80 1 50       4 die 'empty map' unless $h;
81 1         3 my $w = $#{$terrain->[0]} + 1;
  1         3  
82 1 50       4 die 'empty row' unless $w;
83 1 50       3 $floor = '.' unless defined $floor;
84 1 50       3 $wall = ' ' unless defined $wall;
85            
86 1     1   7 no warnings; #yeah. sometimes this checks tiles outide of $terrain.
  1         1  
  1         281  
87 1         3 for my $x (0..$w-1){
88 50         65 for my $y (0..$h-1){
89 1000 100       1619 next if $terrain->[$y][$x] eq $floor; #is floor
90 494         466 my ($v,$h)=(0,0); #vert/horiz weighting
91 494 100       812 $v++ if $terrain->[$y][$x-1] eq $floor;
92 494 100       866 $v++ if $terrain->[$y][$x+1] eq $floor;
93 494 100       796 $h++ if $terrain->[$y-1][$x] eq $floor;
94 494 100       867 $h++ if $terrain->[$y+1][$x] eq $floor;
95 494 100       943 if ($h>$v){
    100          
    100          
96 57         108 $terrain->[$y][$x] = '-';
97             }
98             elsif ($v>$h){
99 43         76 $terrain->[$y][$x] = '|';
100             }
101             elsif($v){ #maybe a corner. either will do
102 73         106 $terrain->[$y][$x] = '-'
103             }
104             else{ #might border nothing
105 321 100 100     2965 if ($terrain->[$y-1][$x-1] eq $floor or
      100        
      100        
106             $terrain->[$y-1][$x+1] eq $floor or
107             $terrain->[$y+1][$x-1] eq $floor or
108             $terrain->[$y+1][$x+1] eq $floor)
109             {
110 73         109 $terrain->[$y][$x] = '|';
111             }
112             } #else it stays as ' '
113             }
114             }
115 1         8 delete $terrain->[$h]; #this last row autovivified
116             }
117              
118             1;
119             __END__