File Coverage

blib/lib/Games/Solitaire/Verify/App/CmdLine/From_ShirlHartSolver.pm
Criterion Covered Total %
statement 124 140 88.5
branch 43 64 67.1
condition n/a
subroutine 16 16 100.0
pod n/a
total 183 220 83.1


line stmt bran cond sub pod time code
1             package Games::Solitaire::Verify::App::CmdLine::From_ShirlHartSolver;
2             $Games::Solitaire::Verify::App::CmdLine::From_ShirlHartSolver::VERSION = '0.2601';
3 2     2   592509 use strict;
  2         6  
  2         81  
4 2     2   11 use warnings;
  2         3  
  2         123  
5 2     2   610 use autodie;
  2         21806  
  2         12  
6              
7 2     2   14046 use parent 'Games::Solitaire::Verify::FromOtherSolversBase';
  2         7  
  2         16  
8              
9 2     2   167 use List::Util qw(first);
  2         4  
  2         4925  
10              
11             __PACKAGE__->mk_acc_ref(
12             [
13             qw(
14             _move_was_performed
15             _input_move_index
16             _st
17             _filename
18             _sol_filename
19             _variant_params
20             _buffer_ref
21             )
22             ]
23             );
24              
25             my $COL_RE = qr/[1-8]/;
26             my $FREECELL_RE = qr/[abcd]/;
27              
28             sub _perform_and_output_move_wrapper
29             {
30 83     83   112 my $self = shift;
31              
32 83         221 $self->_perform_and_output_move(shift);
33 83         817 return $self->_move_was_performed(1);
34             }
35              
36             sub _analyze_shirl_hart_move
37             {
38 83     83   159 my ( $self, $src_char, $dest_char, $move_line, $src_card, $dest ) = @_;
39 83         295 my %fc = ( a => 0, b => 1, c => 2, d => 3 );
40             my $is_invalid_dest_col = sub {
41 43     43   59 my ($dest_col_idx) = @_;
42 43         101 my $dcol = $self->_st->get_column($dest_col_idx);
43             return (
44 43 100       98 $dest eq 'e'
45             ? ( $dcol->len() > 0 )
46             : ( $dest ne $dcol->top->to_string )
47             );
48 83         322 };
49             my $try_col = sub {
50 141     141   199 my $c = shift;
51 141 100       659 if ( $c =~ /\A$COL_RE\z/ )
52             {
53 118         335 return $c - 1;
54             }
55 23         53 return undef;
56 83         176 };
57             my $try_foundation = sub {
58 23 100   23   46 if ( $dest_char eq 'h' )
59             {
60 18 50       31 if ( $dest ne 'h' )
61             {
62 0         0 die "wrong move foundations - $move_line";
63             }
64 18         33 return 1;
65             }
66 5         11 return;
67 83         182 };
68             my $try_fc = sub {
69 83     83   111 my $c = shift;
70 83         200 return $fc{$c};
71 83         195 };
72 83 100       160 if ( defined( my $src_col_idx = $try_col->($src_char) ) )
    50          
73             {
74 75 100       147 if ( defined( my $dest_fc_idx = $try_fc->($dest_char) ) )
75             {
76 22 50       41 if ( $dest ne 'f' )
77             {
78 0         0 die "wrong move to freecell - $move_line";
79             }
80 22 50       67 if ( $src_card ne
81             $self->_st->get_column($src_col_idx)->top->to_string )
82             {
83 0         0 die "wrong move to freecell - $move_line";
84             }
85              
86 22         117 return $self->_perform_and_output_move_wrapper(
87             sprintf(
88             "Move a card from stack %d to freecell %d",
89             $src_col_idx, $dest_fc_idx,
90             ),
91             );
92             }
93 53 100       86 if ( defined( my $dest_col_idx = $try_col->($dest_char) ) )
94             {
95 38         70 $src_card = substr( $src_card, 0, 2 );
96 38         103 my $col = $self->_st->get_column($src_col_idx);
97 167     167   259 my $idx = first { $col->pos($_)->to_string eq $src_card }
98 38         123 ( 0 .. $col->len - 1 );
99 38 50       127 if ( !defined $idx )
100             {
101 0         0 die "wrong move stack to stack - $move_line";
102             }
103 38 50       62 if ( $is_invalid_dest_col->($dest_col_idx) )
104             {
105 0         0 die "wrong move stack to stack - $move_line";
106             }
107              
108 38         82 return $self->_perform_and_output_move_wrapper(
109             sprintf(
110             "Move %d cards from stack %d to stack %d",
111             $col->len() - $idx,
112             $src_col_idx, $dest_col_idx,
113             ),
114             );
115             }
116              
117 15 50       23 if ( $try_foundation->() )
118             {
119 15 50       46 if ( $src_card ne
120             $self->_st->get_column($src_col_idx)->top->to_string )
121             {
122 0         0 die "wrong move stack to foundations - $move_line";
123             }
124              
125 15         58 return $self->_perform_and_output_move_wrapper(
126             sprintf( "Move a card from stack %d to the foundations",
127             $src_col_idx ),
128             );
129             }
130             }
131             elsif ( defined( my $src_fc_idx = $try_fc->($src_char) ) )
132             {
133 8 50       23 if ( $src_card ne $self->_st->get_freecell($src_fc_idx)->to_string )
134             {
135 0         0 die "wrong freecell card - $move_line";
136             }
137 8 100       15 if ( $try_foundation->() )
138             {
139 3         12 return $self->_perform_and_output_move_wrapper(
140             sprintf( "Move a card from freecell %d to the foundations",
141             $src_fc_idx ),
142             );
143             }
144              
145 5 50       8 if ( defined( my $dest_col_idx = $try_col->($dest_char) ) )
146             {
147 5 50       9 if ( $is_invalid_dest_col->($dest_col_idx) )
148             {
149 0         0 die "wrong move freecell to stack - $move_line";
150             }
151              
152 5         26 return $self->_perform_and_output_move_wrapper(
153             sprintf(
154             "Move a card from freecell %d to stack %d",
155             $src_fc_idx, $dest_col_idx,
156             ),
157             );
158             }
159             }
160 0         0 die "wrong move - $move_line";
161             }
162              
163             sub _perform_move
164             {
165 83     83   119 my ( $self, $move_line ) = @_;
166 83         136 $self->_move_was_performed(0);
167              
168 83         211 my @fields = ( split /\|/, $move_line, -1 );
169 83 50       191 if ( @fields != 5 )
170             {
171 0         0 die "Wrong $move_line";
172             }
173 83         146 my $idx = shift @fields;
174 83 50       220 if ( $idx ne $self->_input_move_index )
175             {
176 0         0 die "wrong move index - $move_line";
177             }
178              
179 83         186 my ( $move_s, $src_card, $dest, $found_moves ) = @fields;
180              
181 83         310 my @src_dest = ( $move_s =~ /(.)/gms );
182 83 50       149 if ( @src_dest != 2 )
183             {
184 0         0 die "bad length in move <<$move_s>>!";
185             }
186 83         172 my ( $src_char, $dest_char ) = @src_dest;
187 83         195 $self->_analyze_shirl_hart_move( $src_char, $dest_char, $move_line,
188             $src_card, $dest );
189 83 50       224 die "wrong move - $move_line" if not $self->_move_was_performed;
190 83         213 $self->_perform_autoflush_to_foundation_moves( $found_moves, $move_line );
191              
192 83         161 $self->_input_move_index( $self->_input_move_index + 1 );
193 83         197 return;
194             }
195              
196             sub _perform_autoflush_to_foundation_moves
197             {
198 83     83   142 my ( $self, $found_moves, $move_line ) = @_;
199 83 100       141 return if ( not length $found_moves );
200 21         31 my $map;
201             my %suits;
202             $map = sub {
203 43     43   59 my $s = shift;
204 43 100       79 if ( length($s) == 2 )
205             {
206 13         45 return $map->("$s-$s");
207             }
208 30 50       125 my ( $start, $end ) = $s =~ /\A(\S\S)-(\S\S)\z/
209             or die "wrong found_moves <<$s>>!";
210 30         104 my $sc = Games::Solitaire::Verify::Card->new( { string => $start } );
211 30         83 my $ec = Games::Solitaire::Verify::Card->new( { string => $end } );
212 30 50       81 if ( $sc->suit ne $ec->suit )
213             {
214 0         0 die "uiui";
215             }
216 30 50       61 if ( exists $suits{ $sc->suit } )
217             {
218 0         0 die "duplicate";
219             }
220 30 50       72 if ( $sc->rank > $ec->rank )
221             {
222 0         0 die "foo";
223             }
224 30         88 $suits{ $sc->suit } = { start => $sc, end => $ec };
225 30         61 return;
226 21         105 };
227 21         57 foreach my $f ( split /;/, $found_moves, -1 )
228             {
229 30         57 $map->($f);
230             }
231 21         33 my $count = 1;
232             FOUNDATION:
233 21         38 while ( $count > 0 )
234             {
235 107         135 $count = 0;
236 107         353 foreach my $suit ( sort( keys %suits ) )
237             {
238 129         175 my @src_s;
239 129         175 my $rec = $suits{$suit};
240 129         186 my $start = $rec->{start};
241 129         177 eval { @src_s = $self->_find_card_src_string( $start->to_string ); };
  129         237  
242 129 100       245 if ( !$@ )
243             {
244 86         98 ++$count;
245 86         130 my $rank = $start->rank;
246 86 100       180 if ( $rank == $rec->{end}->rank )
247             {
248 30         54 delete $suits{$suit};
249             }
250             else
251             {
252 56         100 $start->rank( 1 + $rank );
253             }
254 86         248 $self->_perform_and_output_move(
255             sprintf( "Move a card from %s to the foundations",
256             $src_s[1] ),
257             );
258 86         388 next FOUNDATION;
259             }
260             }
261             }
262 21 50       52 if (%suits)
263             {
264 0         0 die "cannot move to foundations - $move_line";
265             }
266             }
267              
268             sub _process_main
269             {
270 2     2   11 my $self = shift;
271              
272 2         10 $self->_read_initial_state;
273 2         6 $self->_input_move_index(1);
274              
275 2         12 open my $in_fh, '<', $self->_sol_filename;
276              
277 2         1690 my $l;
278             FIRST_lines:
279 2         87 while ( $l = <$in_fh> )
280             {
281 2         6 chomp($l);
282 2 50       12 last FIRST_lines if ( $l =~ /\|/ );
283             }
284              
285 2         7 while ( $l =~ /\|/ )
286             {
287 83         193 $self->_perform_move($l);
288 83         245 $l = <$in_fh>;
289 83         240 chomp $l;
290             }
291 2         12 close($in_fh);
292              
293 2         942 $self->_append("This game is solveable.\n");
294              
295 2         12 return;
296             }
297              
298             1;
299              
300             __END__