File Coverage

blib/lib/Karel/Robot.pm
Criterion Covered Total %
statement 92 94 97.8
branch 17 24 70.8
condition 6 8 75.0
subroutine 21 21 100.0
pod 5 5 100.0
total 141 152 92.7


line stmt bran cond sub pod time code
1             package Karel::Robot;
2              
3             =head1 NAME
4              
5             Karel::Robot
6              
7             =head1 DESCRIPTION
8              
9             Basic robot class. It represents a robot wihtout a grid.
10              
11             =head1 METHODS
12              
13             =over 4
14              
15             =cut
16              
17 7     7   347215 use warnings;
  7         8  
  7         171  
18 7     7   25 use strict;
  7         10  
  7         105  
19              
20 7     7   2065 use Karel::Grid;
  7         15  
  7         193  
21 7     7   2253 use Karel::Parser;
  7         17  
  7         172  
22 7     7   31 use Carp;
  7         8  
  7         369  
23 7     7   2908 use Module::Load qw{ load };
  7         5227  
  7         37  
24 7     7   360 use Moo;
  7         7  
  7         37  
25 7     7   4217 use Syntax::Construct qw{ // };
  7         4967  
  7         35  
26 7     7   332 use namespace::clean;
  7         9  
  7         98  
27              
28             =item my $robot = 'Karel::Robot'->new
29              
30             The constructor. It can take one parameter: C. Its value
31             should be a parser object, by default an instance of C.
32              
33             =item $robot->set_grid($grid, $x, $y, $direction)
34              
35             Applies the L role to the $robot. C<$grid>
36             must be a C instance, $x and $y denote the position of
37             the robot in the grid. Optional $direction is one of C (for
38             North, East, South, and West), defaults to C.
39              
40             =cut
41              
42             sub set_grid {
43 42     42 1 215 my ($self, $grid, $x, $y, $direction) = @_;
44 42   100     95 $direction //= 'N';
45 42         107 my $with_grid_class = $self->class_with_grid;
46 42 50       172 if (! $self->does($with_grid_class)) {
47 42         35879 load($with_grid_class);
48 42         2297 'Moo::Role'->apply_roles_to_object($self, $with_grid_class);
49 42         957 $self->set_grid($grid, $x, $y, $direction);
50             }
51             }
52              
53             =item class_with_grid
54              
55             The class to which the robot is reblessed after obraining the grid. By
56             default, it's the robot's class plus C<::WithGrid>.
57              
58             =cut
59              
60 42     42 1 122 sub class_with_grid { ref(shift) . '::WithGrid' }
61              
62              
63             =item $robot->load_grid( [ file | handle ] => '...' )
64              
65             Loads grid from the given source. You can specify a scalar reference
66             as C, too. The format of the input is as follows:
67              
68             # karel 4 2
69             WWWWWW
70             W v W
71             W1w W
72             WWWWWW
73              
74             The first line specifies width and height of the grid. An ASCII map of
75             the grid follows with the following symbols:
76              
77             W outer wall
78             w inner wall
79             space blank
80             1 .. 9 marks
81              
82             The robot's position and direction is denoted by either of C<< ^ > v <
83             >> B the cell in which the robot should start. In the
84             example above, the robots starts at coordinates 4, 1 and faces South.
85              
86             =cut
87              
88             my %faces = ( '^' => 'N',
89             '>' => 'E',
90             'v' => 'S',
91             '<' => 'W' );
92              
93             sub load_grid {
94 18     18 1 2628 my ($self, $type, $that) = @_;
95              
96 18         20 my %backup;
97 18 100       79 if ($self->can('grid')) {
98 1         8 @backup{qw{ grid x y direction }} = map $self->$_,
99             qw( grid x y direction );
100             }
101              
102 18         12 my $IN;
103 3 50   3   85 my $open = { file => sub { open $IN, '<', $that or croak "$that: $!" },
104 13 50   13   141 string => sub { open $IN, '<', \$that or croak "'$that': $!" },
105 1     1   1 handle => sub { $IN = $that },
106 18         123 }->{$type};
107 18 100       92 croak "Unknown type $type" unless $open;
108 17         24 $open->();
109              
110 17         51 local $/ = "\n";
111 17         67 my $header = <$IN>;
112 17 50       92 croak 'Invalid format'
113             unless $header =~ /^\# \s* karel \s+ (v[0-9]+\.[0-9]{2}) \s+ ([0-9]+) \s+ ([0-9]+)/x;
114 17         44 my ($version, $x, $y) = ($1, $2, $3);
115 17         284 my $grid = 'Karel::Grid'->new( x => $x,
116             y => $y,
117             );
118              
119 17         65 my $r = 0;
120 17         16 my (@pos, $direction);
121 17         56 while (<$IN>) {
122 73         66 chomp;
123 73         138 my @chars = split //;
124 73         53 my $c = 0;
125 73         97 while ($c != $#chars) {
126 303 100 66     837 next if 'W' eq $chars[$c]
      66        
127             && ( $r == 0 || $r == $y + 1
128             || $c == 0 || $c == $x + 1);
129             my $build = { w => 'build_wall',
130             ' ' => 'clear',
131             # marks
132             ( map {
133 1260         704 my $x = $_;
134             $x => sub {
135 14     14   49 $_[0]->drop_mark(@_[1, 2]) for 1 .. $x
136             }
137 1260         1998 } 1 .. 9 ),
138             # robot
139             ( map {
140 560         390 my $f = $_;
141             $f => sub {
142 17 50   17   27 croak 'Two robots in a grid' if $direction;
143 17         21 $direction = $faces{$f};
144 17         21 @pos = ($c, $r);
145 17         19 splice @chars, $c, 1;
146 7     7   4235 no warnings 'exiting';
  7         9  
  7         2378  
147             redo
148 17         38 }
149 560         1571 } keys %faces )
150 140         155 }->{ $chars[$c] };
151 140 100       1220 croak "Unknown or invalid grid character '$chars[$c]' at $c, $.."
152             unless $build;
153 139         276 $grid->$build($c, $r);
154             } continue {
155 285         889 ++$c;
156             }
157             } continue {
158 72         202 ++$r;
159             }
160              
161 16 100       35 croak 'Wall at starting position' if 'w' eq lc $grid->at(@pos);
162              
163             eval {
164 14         29 $_[0]->set_grid($grid, @pos, $direction);
165 14 50       78 1 } or do {
  14         216  
166 0         0 $_[0]->set_grid(@backup{qw{ grid x y direction }});
167 0         0 croak $@
168             };
169             }
170              
171             has parser => ( is => 'ro',
172             default => sub { 'Karel::Parser'->new },
173             );
174              
175              
176             =item $commands = $robot->knows($command_name)
177              
178             If the robot knows the command, returns its definition; dies
179             otherwise.
180              
181             =cut
182              
183             sub knows {
184 55     55 1 5900 my ($self, $command) = @_;
185 55         195 $self->knowledge->{$command}
186             }
187              
188             sub _learn {
189 40     40   7976 my ($self, $command, $prog) = @_;
190 40         98 my $knowledge = $self->knowledge;
191 40         57 $knowledge->{$command} = $prog;
192 40         129 $self->_set_knowledge($knowledge);
193             }
194              
195             =item $robot->learn($program)
196              
197             Teaches the robot new commands. Dies if the definitions contain
198             unknown commands.
199              
200             =cut
201              
202             sub learn {
203 11     11 1 2792 my ($self, $prog) = @_;
204 11         52 my ($commands, $unknown) = $self->parser->parse($prog);
205 11         91 $self->_learn($_, $commands->{$_}) for keys %$commands;
206 11         41 for my $command (keys %$unknown) {
207 18 50       37 croak "Dont' know $command" unless $self->knows($command);
208             }
209             }
210              
211             has knowledge => (
212             is => 'rwp'
213             );
214              
215              
216              
217             =back
218              
219             =cut
220              
221              
222             __PACKAGE__