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   366232 use warnings;
  7         13  
  7         174  
18 7     7   32 use strict;
  7         7  
  7         111  
19              
20 7     7   2140 use Karel::Grid;
  7         18  
  7         208  
21 7     7   2689 use Karel::Parser;
  7         15  
  7         194  
22 7     7   35 use Carp;
  7         10  
  7         471  
23 7     7   3668 use Module::Load qw{ load };
  7         5924  
  7         38  
24 7     7   366 use Moo;
  7         10  
  7         38  
25 7     7   4946 use Syntax::Construct qw{ // };
  7         5258  
  7         35  
26 7     7   365 use namespace::clean;
  7         12  
  7         125  
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 217 my ($self, $grid, $x, $y, $direction) = @_;
44 42   100     93 $direction //= 'N';
45 42         103 my $with_grid_class = $self->class_with_grid;
46 42 50       178 if (! $self->does($with_grid_class)) {
47 42         36348 load($with_grid_class);
48 42         2376 'Moo::Role'->apply_roles_to_object($self, $with_grid_class);
49 42         977 $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 120 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 2722 my ($self, $type, $that) = @_;
95              
96 18         21 my %backup;
97 18 100       87 if ($self->can('grid')) {
98 1         9 @backup{qw{ grid x y direction }} = map $self->$_,
99             qw( grid x y direction );
100             }
101              
102 18         16 my $IN;
103 3 50   3   91 my $open = { file => sub { open $IN, '<', $that or croak "$that: $!" },
104 13 50   13   168 string => sub { open $IN, '<', \$that or croak "'$that': $!" },
105 1     1   1 handle => sub { $IN = $that },
106 18         149 }->{$type};
107 18 100       106 croak "Unknown type $type" unless $open;
108 17         28 $open->();
109              
110 17         57 local $/ = "\n";
111 17         83 my $header = <$IN>;
112 17 50       103 croak 'Invalid format'
113             unless $header =~ /^\# \s* karel \s+ (v[0-9]+\.[0-9]{2}) \s+ ([0-9]+) \s+ ([0-9]+)/x;
114 17         48 my ($version, $x, $y) = ($1, $2, $3);
115 17         313 my $grid = 'Karel::Grid'->new( x => $x,
116             y => $y,
117             );
118              
119 17         62 my $r = 0;
120 17         15 my (@pos, $direction);
121 17         57 while (<$IN>) {
122 73         67 chomp;
123 73         139 my @chars = split //;
124 73         54 my $c = 0;
125 73         113 while ($c != $#chars) {
126 303 100 66     803 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         725 my $x = $_;
134             $x => sub {
135 14     14   50 $_[0]->drop_mark(@_[1, 2]) for 1 .. $x
136             }
137 1260         1938 } 1 .. 9 ),
138             # robot
139             ( map {
140 560         355 my $f = $_;
141             $f => sub {
142 17 50   17   28 croak 'Two robots in a grid' if $direction;
143 17         18 $direction = $faces{$f};
144 17         26 @pos = ($c, $r);
145 17         23 splice @chars, $c, 1;
146 7     7   4550 no warnings 'exiting';
  7         12  
  7         2503  
147             redo
148 17         41 }
149 560         1504 } keys %faces )
150 140         171 }->{ $chars[$c] };
151 140 100       1221 croak "Unknown or invalid grid character '$chars[$c]'" unless $build;
152 139         298 $grid->$build($c, $r);
153             } continue {
154 285         875 ++$c;
155             }
156             } continue {
157 72         176 ++$r;
158             }
159              
160 16 100       41 croak 'Wall at starting position' if 'w' eq lc $grid->at(@pos);
161              
162             eval {
163 14         34 $_[0]->set_grid($grid, @pos, $direction);
164 14 50       82 1 } or do {
  14         250  
165 0         0 $_[0]->set_grid(@backup{qw{ grid x y direction }});
166 0         0 croak $@
167             };
168             }
169              
170             has parser => ( is => 'ro',
171             default => sub { 'Karel::Parser'->new },
172             );
173              
174              
175             =item $commands = $robot->knows($command_name)
176              
177             If the robot knows the command, returns its definition; dies
178             otherwise.
179              
180             =cut
181              
182             sub knows {
183 55     55 1 6151 my ($self, $command) = @_;
184 55         195 $self->knowledge->{$command}
185             }
186              
187             sub _learn {
188 40     40   7894 my ($self, $command, $prog) = @_;
189 40         105 my $knowledge = $self->knowledge;
190 40         63 $knowledge->{$command} = $prog;
191 40         134 $self->_set_knowledge($knowledge);
192             }
193              
194             =item $robot->learn($program)
195              
196             Teaches the robot new commands. Dies if the definitions contain
197             unknown commands.
198              
199             =cut
200              
201             sub learn {
202 11     11 1 3226 my ($self, $prog) = @_;
203 11         60 my ($commands, $unknown) = $self->parser->parse($prog);
204 11         101 $self->_learn($_, $commands->{$_}) for keys %$commands;
205 11         40 for my $command (keys %$unknown) {
206 18 50       38 croak "Dont' know $command" unless $self->knows($command);
207             }
208             }
209              
210             has knowledge => (
211             is => 'rwp'
212             );
213              
214              
215              
216             =back
217              
218             =cut
219              
220              
221             __PACKAGE__