File Coverage

blib/lib/Project/Euler/Problem/Base.pm
Criterion Covered Total %
statement 14 16 87.5
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 20 22 90.9


line stmt bran cond sub pod time code
1 1     1   1563 use strict;
  1         3  
  1         42  
2 1     1   8 use warnings;
  1         3  
  1         58  
3             package Project::Euler::Problem::Base;
4             BEGIN {
5 1     1   16 $Project::Euler::Problem::Base::VERSION = '0.20';
6             }
7              
8 1     1   5 use Modern::Perl;
  1         2  
  1         5  
9 1     1   937 use namespace::autoclean;
  1         48004  
  1         8  
10              
11 1     1   576 use Moose::Role;
  0            
  0            
12             use Project::Euler::Lib::Types qw/ ProblemLink ProblemName PosInt MyDateTime /;
13              
14             use Carp;
15             use Readonly;
16              
17             Readonly::Scalar my $BASE_URL => q{http://projecteuler.net/index.php?section=problems&id=};
18              
19              
20             #ABSTRACT: Abstract class that the problems will extend from
21              
22              
23              
24             has 'problem_number' => (
25             is => 'ro',
26             isa => PosInt,
27             required => 1,
28             lazy_build => 1,
29             init_arg => undef,
30             );
31             requires '_build_problem_number';
32              
33              
34              
35             has 'problem_name' => (
36             is => 'ro',
37             isa => ProblemName,
38             required => 1,
39             lazy_build => 1,
40             init_arg => undef,
41             );
42             requires '_build_problem_name';
43              
44              
45              
46             has 'problem_date' => (
47             is => 'ro',
48             isa => MyDateTime,
49             coerce => 1,
50             required => 1,
51             lazy_build => 1,
52             init_arg => undef,
53             );
54             requires '_build_problem_date';
55              
56              
57              
58             has 'problem_desc' => (
59             is => 'ro',
60             isa => 'Str',
61             required => 1,
62             lazy_build => 1,
63             init_arg => undef,
64             );
65             requires '_build_problem_desc';
66              
67              
68              
69             has 'problem_link_base' => (
70             is => 'ro',
71             isa => 'Str',
72             required => 1,
73             lazy => 1,
74             init_arg => undef,
75             default => $BASE_URL,
76             );
77              
78              
79              
80             has 'problem_link' => (
81             is => 'ro',
82             isa => ProblemLink,
83             required => 1,
84             lazy_build => 1,
85             init_arg => undef,
86             );
87             sub _build_problem_link {
88             my ($self) = @_;
89             return $BASE_URL . $self->problem_number;
90             }
91              
92              
93              
94             has 'default_input' => (
95             is => 'ro',
96             isa => 'Str',
97             required => 1,
98             lazy_build => 1,
99             init_arg => undef,
100             );
101             requires '_build_default_input';
102              
103              
104              
105             has 'default_answer' => (
106             is => 'ro',
107             isa => 'Str',
108             required => 1,
109             lazy_build => 1,
110             init_arg => undef,
111             );
112             requires '_build_default_answer';
113              
114              
115              
116             has 'has_input' => (
117             is => 'ro',
118             isa => 'Bool',
119             required => 1,
120             default => 1,
121             init_arg => undef,
122             );
123              
124              
125              
126             has 'use_defaults' => (
127             is => 'rw',
128             isa => 'Bool',
129             required => 1,
130             default => 1,
131             );
132              
133              
134              
135              
136             has 'help_message' => (
137             is => 'ro',
138             isa => 'Str',
139             required => 1,
140             lazy_build => 1,
141             init_arg => undef,
142             );
143             requires '_build_help_message';
144              
145              
146              
147             has 'custom_input' => (
148             is => 'rw',
149             isa => 'Str',
150             required => 0,
151             trigger => \&_check_input_stub,
152             );
153             sub _check_input_stub {
154             $_[0]->_check_input(@_);
155             }
156              
157              
158              
159             has 'custom_answer' => (
160             is => 'rw',
161             isa => 'Str',
162             required => 0,
163             );
164              
165              
166              
167              
168             has 'solved_status' => (
169             is => 'ro',
170             isa => 'Maybe[Bool]',
171             writer => '_set_solved_status',
172             required => 0,
173             init_arg => undef,
174             );
175              
176              
177              
178             has 'solved_answer' => (
179             is => 'ro',
180             isa => 'Maybe[Str]',
181             writer => '_set_solved_answer',
182             required => 0,
183             init_arg => undef,
184             );
185              
186              
187              
188             has 'solved_wanted' => (
189             is => 'ro',
190             isa => 'Maybe[Str]',
191             writer => '_set_solved_wanted',
192             required => 0,
193             init_arg => undef,
194             );
195              
196              
197              
198              
199             has 'more_info' => (
200             is => 'ro',
201             isa => 'Maybe[Str]',
202             writer => '_set_more_info',
203             lazy => 1,
204             default => q{},
205             init_arg => undef,
206             );
207              
208              
209              
210              
211              
212             requires '_check_input';
213             requires '_solve_problem';
214              
215              
216              
217              
218             sub solve {
219             my ($self, $cust_input, $cust_answer) = @_;
220             my $answer;
221              
222             # If the user provided some input, then we'll won't use the defaults
223             my $defaults = defined $cust_input ? 0 : $self->use_defaults;
224              
225             # If no input was given as an arg, try to get it from the current object.
226             # This may still return an undef but that's alright
227             $cust_input //= $self->custom_input;
228             $cust_answer //= $self->custom_answer;
229              
230              
231             # If the problem takes input, determine the appropriate course of action
232             if ( $self->has_input ) {
233             # The user wants to use the defaults so don't pass anything
234             if ( $defaults ) {
235             $answer = $self->_solve_problem;
236             }
237             # Pass the user input to the subroutine (if it's defined!)
238             elsif (defined $cust_input) {
239             $answer = $self->_solve_problem( $cust_input );
240             }
241             # The user tried to use a cutsom input string to
242             # solve the problem but hasn't defined it yet!
243             else {
244             confess q{You tried to use custom inputs to solve the problem, but it has not been set yet}
245             }
246             }
247              
248             # There are no paramaters to pass!
249             else {
250             $answer = $self->_solve_problem;
251             }
252              
253              
254             # Determine what the expected answer should be, depending on whether the
255             # defaults were used or not.
256             my $wanted = $defaults ? $self->default_answer : $cust_answer;
257              
258             # Determine if the given answer was correct.
259             # Use a blank string rather than undef for the given and expected answer
260             $answer //= q{}; $wanted //= q{};
261              
262             # See if the answer was correct
263             my $status = $answer eq $wanted;
264              
265             # Save the answer, wanted, and status
266             $self->_set_solved_answer($answer);
267             $self->_set_solved_wanted($wanted);
268             $self->_set_solved_status($status);
269              
270              
271             # Return either the status, answer, and wanted or if the user just
272             # expects a scalar, the found answer
273             return wantarray ? ($status, $answer, $wanted) : $answer;
274             }
275              
276              
277              
278              
279             sub status {
280             my ($self) = @_;
281             my $out;
282              
283             # Extract the status and solved and expected answer
284             my ($answer, $wanted, $status) =
285             @{$self}{qw/ solved_answer solved_wanted solved_status /};
286              
287             # If the status isn't even defined then the problem wasn't ever run
288             if (!defined $status) {
289             $out = q{It appears that the problem has yet to be solved once.};
290             }
291              
292             # Otherwise print a message if it failed or not
293             else {
294             $out = sprintf(q{The last run was%s successful! The answer expected was '%s' %s the answer returned was '%s'},
295             $status ? q{} : ' not', $wanted, $status ? 'and' : 'but', $answer
296             );
297             }
298              
299             if ($self->has_more_info) {
300             $out .= sprintf(qq{\n%s}, $self->more_info);
301             }
302              
303              
304             return $out;
305             }
306              
307              
308              
309             1; # End of Project::Euler::Problem::Base
310              
311             __END__
312             =pod
313              
314             =head1 NAME
315              
316             Project::Euler::Problem::Base - Abstract class that the problems will extend from
317              
318             =head1 VERSION
319              
320             version 0.20
321              
322             =head1 SYNOPSIS
323              
324             package Project::Euler::Problem::P999;
325             use Moose;
326             with 'Project::Euler::Problem::Base';
327              
328             =head1 DESCRIPTION
329              
330             To ensure that each problem class performs a minimum set of functions, this
331             class will define the basic subroutines and variables that every object must
332             implement.
333              
334             =head1 ATTRIBUTES
335              
336             =head2 problem_number
337              
338             Problem number on the problem's website
339              
340             =over 4
341              
342             =item Isa
343              
344             PosInt
345              
346             =item Requires
347              
348             _build_problem_number
349              
350             =back
351              
352             =head2 problem_name
353              
354             Short name for the problem designated by the module author
355              
356             =over 4
357              
358             =item Isa
359              
360             ProblemName
361              
362             =item Requires
363              
364             _build_problem_name
365              
366             =back
367              
368             =head2 problem_date
369              
370             Date the problem was posted on the website
371              
372             =over 4
373              
374             =item Isa
375              
376             MyDateTime
377              
378             =item Requires
379              
380             _build_problem_date
381              
382             =back
383              
384             =head2 problem_desc
385              
386             Description posted on the problem's website
387              
388             =over 4
389              
390             =item Isa
391              
392             Str
393              
394             =item Requires
395              
396             _build_problem_desc
397              
398             =back
399              
400             =head2 problem_link_base
401              
402             The base URL for the problems on L<< http://projecteuler.net >>
403              
404             =over 4
405              
406             =item Isa
407              
408             Str
409              
410             =item Default
411              
412             http://projecteuler.net/index.php?section=problems&id=
413              
414             =back
415              
416             =head2 problem_link
417              
418             URL to the problem's homepage
419              
420             =over 4
421              
422             =item Isa
423              
424             ProblemLink
425              
426             =item Is
427              
428             $self->problem_link_base . $self->problem_number
429              
430             =back
431              
432             =head2 default_input
433              
434             Default input posted on the problem's website
435              
436             =over 4
437              
438             =item Isa
439              
440             Str
441              
442             =item Requires
443              
444             _build_default_input
445              
446             =back
447              
448             =head2 default_answer
449              
450             Answer for the default input
451              
452             =over 4
453              
454             =item Isa
455              
456             Str
457              
458             =item Requires
459              
460             _build_default_answer
461              
462             =back
463              
464             =head2 has_input
465              
466             Indicates if the problem takes any input from the user
467              
468             =over 4
469              
470             =item Isa
471              
472             Bool
473              
474             =back
475              
476             =head2 use_defaults
477              
478             Whether the problem should use the default input/answer strings
479              
480             =over 4
481              
482             =item Isa
483              
484             Bool
485              
486             =back
487              
488             =head2 help_message
489              
490             A message to assist the user in using the specific problem
491              
492             =over 4
493              
494             =item Isa
495              
496             Str
497              
498             =item Requires
499              
500             _build_help_message
501              
502             =back
503              
504             =head2 custom_input
505              
506             The user-provided input to the problem
507              
508             =over 4
509              
510             =item Isa
511              
512             Str
513              
514             =back
515              
516             =head2 custom_answer
517              
518             The user-provided answer to the problem
519              
520             =over 4
521              
522             =item Isa
523              
524             Str
525              
526             =back
527              
528             =head2 solved_status
529              
530             Flag to indicate if the last run was successful
531              
532             =over 4
533              
534             =item Isa
535              
536             Maybe[Bool
537              
538             =back
539              
540             =head2 solved_answer
541              
542             The solved answer from the previous run
543              
544             =over 4
545              
546             =item Isa
547              
548             Maybe[Str]
549              
550             =back
551              
552             =head2 solved_wanted
553              
554             The wanted answer from the previous run
555              
556             =over 4
557              
558             =item Isa
559              
560             Maybe[Str]
561              
562             =back
563              
564             =head2 more_info
565              
566             Any additional information the last run provided
567              
568             =over 4
569              
570             =item Isa
571              
572             Maybe[Str]
573              
574             =back
575              
576             =head1 ABSTRACT FUNCTIONS
577              
578             These two functions must be overridden by the extending class
579              
580             =head2 _check_input
581              
582             Ensure the input provided by the user is compliant
583              
584             =head2 _solve_problem
585              
586             This is the main function which will return the status/answer for a problem
587              
588             =head1 PROVIDED FUNCTIONS
589              
590             =head2 solve
591              
592             This function will point to the internal function that actually solves the
593             problem. Depending on the object attributes that are set, it uses either the
594             default or provided inputs (if they are required). It returns the answer as a
595             string in scalar context, or an array containing the status, calculated answer,
596             and expected answer. If values are passed to the function, then they are taken
597             as the custom_input and custom_answer respectively. This also turns off
598             use_defaults temporarily.
599              
600             =head3 Example
601              
602             my $problem_1 = Project::Euler::Problem::P001->new();
603             my $p1_def_answer = $problem_1->solve;
604              
605             $problem_1->custom_input => (42);
606             $problem_1->custom_answer => (24);
607             $problem_1->use_defaults => (0);
608              
609             my $p1_custom_answer = $problem_1->solve;
610              
611             my ($p1_status, $p1_answer, $p1_expected) = $problem_1->solve;
612              
613              
614             # OR #
615              
616              
617             my $problem_2 = Project::Euler::Problem::P002->new();
618             my $p2_def_answer = $problem_2->solve;
619              
620             # Providing input automatically stops using the defaults
621             my $p2_custom_answer = $problem_2->solve( 1, 4 ); # Provide custom input & answer
622              
623             my ($p2_status, $p2_answer, $p2_expected) = $problem_2->solve;
624              
625             =head2 status
626              
627             This function simply returns a nice, readable status message that tells you the
628             outcome of the last run of the module.
629              
630             =head3 Example
631              
632             my $problem_1 = Project::Euler::Problem::P001->new();
633             $problem_1->solve;
634             my $message = $problem_1->last_run_message;
635              
636             =head1 AUTHOR
637              
638             Adam Lesperance <lespea@gmail.com>
639              
640             =head1 COPYRIGHT AND LICENSE
641              
642             This software is copyright (c) 2010 by Adam Lesperance.
643              
644             This is free software; you can redistribute it and/or modify it under
645             the same terms as the Perl 5 programming language system itself.
646              
647             =cut
648