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.2402';
3 1     1   131458 use strict;
  1         13  
  1         32  
4 1     1   7 use warnings;
  1         2  
  1         27  
5 1     1   7 use autodie;
  1         3  
  1         8  
6              
7 1     1   5675 use parent 'Games::Solitaire::Verify::FromOtherSolversBase';
  1         3  
  1         17  
8              
9 1     1   63 use List::Util qw(first);
  1         3  
  1         1954  
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   195 my $self = shift;
31              
32 83         291 $self->_perform_and_output_move(shift);
33 83         726 return $self->_move_was_performed(1);
34             }
35              
36             sub _analyze_shirl_hart_move
37             {
38 83     83   190 my ( $self, $src_char, $dest_char, $move_line, $src_card, $dest ) = @_;
39 83         260 my %fc = ( a => 0, b => 1, c => 2, d => 3 );
40             my $is_invalid_dest_col = sub {
41 43     43   87 my ($dest_col_idx) = @_;
42 43         111 my $dcol = $self->_st->get_column($dest_col_idx);
43             return (
44 43 100       133 $dest eq 'e'
45             ? ( $dcol->len() > 0 )
46             : ( $dest ne $dcol->top->to_string )
47             );
48 83         336 };
49             my $try_col = sub {
50 141     141   277 my $c = shift;
51 141 100       632 if ( $c =~ /\A$COL_RE\z/ )
52             {
53 118         472 return $c - 1;
54             }
55 23         79 return undef;
56 83         226 };
57             my $try_foundation = sub {
58 23 100   23   52 if ( $dest_char eq 'h' )
59             {
60 18 50       63 if ( $dest ne 'h' )
61             {
62 0         0 die "wrong move foundations - $move_line";
63             }
64 18         46 return 1;
65             }
66 5         11 return;
67 83         212 };
68             my $try_fc = sub {
69 83     83   185 my $c = shift;
70 83         233 return $fc{$c};
71 83         194 };
72 83 100       182 if ( defined( my $src_col_idx = $try_col->($src_char) ) )
    50          
73             {
74 75 100       169 if ( defined( my $dest_fc_idx = $try_fc->($dest_char) ) )
75             {
76 22 50       64 if ( $dest ne 'f' )
77             {
78 0         0 die "wrong move to freecell - $move_line";
79             }
80 22 50       90 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         119 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       114 if ( defined( my $dest_col_idx = $try_col->($dest_char) ) )
94             {
95 38         89 $src_card = substr( $src_card, 0, 2 );
96 38         144 my $col = $self->_st->get_column($src_col_idx);
97 167     167   390 my $idx = first { $col->pos($_)->to_string eq $src_card }
98 38         180 ( 0 .. $col->len - 1 );
99 38 50       153 if ( !defined $idx )
100             {
101 0         0 die "wrong move stack to stack - $move_line";
102             }
103 38 50       100 if ( $is_invalid_dest_col->($dest_col_idx) )
104             {
105 0         0 die "wrong move stack to stack - $move_line";
106             }
107              
108 38         101 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       33 if ( $try_foundation->() )
118             {
119 15 50       56 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         81 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       33 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       22 if ( $try_foundation->() )
138             {
139 3         17 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       14 if ( defined( my $dest_col_idx = $try_col->($dest_char) ) )
146             {
147 5 50       14 if ( $is_invalid_dest_col->($dest_col_idx) )
148             {
149 0         0 die "wrong move freecell to stack - $move_line";
150             }
151              
152 5         34 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   182 my ( $self, $move_line ) = @_;
166 83         193 $self->_move_was_performed(0);
167              
168 83         280 my @fields = ( split /\|/, $move_line, -1 );
169 83 50       244 if ( @fields != 5 )
170             {
171 0         0 die "Wrong $move_line";
172             }
173 83         169 my $idx = shift @fields;
174 83 50       259 if ( $idx ne $self->_input_move_index )
175             {
176 0         0 die "wrong move index - $move_line";
177             }
178              
179 83         203 my ( $move_s, $src_card, $dest, $found_moves ) = @fields;
180              
181 83         358 my @src_dest = ( $move_s =~ /(.)/gms );
182 83 50       201 if ( @src_dest != 2 )
183             {
184 0         0 die "bad length in move <<$move_s>>!";
185             }
186 83         196 my ( $src_char, $dest_char ) = @src_dest;
187 83         248 $self->_analyze_shirl_hart_move( $src_char, $dest_char, $move_line,
188             $src_card, $dest );
189 83 50       259 die "wrong move - $move_line" if not $self->_move_was_performed;
190 83         242 $self->_perform_autoflush_to_foundation_moves( $found_moves, $move_line );
191              
192 83         221 $self->_input_move_index( $self->_input_move_index + 1 );
193 83         227 return;
194             }
195              
196             sub _perform_autoflush_to_foundation_moves
197             {
198 83     83   194 my ( $self, $found_moves, $move_line ) = @_;
199 83 100       207 return if ( not length $found_moves );
200 21         42 my $map;
201             my %suits;
202             $map = sub {
203 43     43   85 my $s = shift;
204 43 100       97 if ( length($s) == 2 )
205             {
206 13         78 return $map->("$s-$s");
207             }
208 30 50       165 my ( $start, $end ) = $s =~ /\A(\S\S)-(\S\S)\z/
209             or die "wrong found_moves <<$s>>!";
210 30         142 my $sc = Games::Solitaire::Verify::Card->new( { string => $start } );
211 30         117 my $ec = Games::Solitaire::Verify::Card->new( { string => $end } );
212 30 50       111 if ( $sc->suit ne $ec->suit )
213             {
214 0         0 die "uiui";
215             }
216 30 50       82 if ( exists $suits{ $sc->suit } )
217             {
218 0         0 die "duplicate";
219             }
220 30 50       103 if ( $sc->rank > $ec->rank )
221             {
222 0         0 die "foo";
223             }
224 30         110 $suits{ $sc->suit } = { start => $sc, end => $ec };
225 30         90 return;
226 21         105 };
227 21         73 foreach my $f ( split /;/, $found_moves, -1 )
228             {
229 30         63 $map->($f);
230             }
231 21         40 my $count = 1;
232             FOUNDATION:
233 21         55 while ( $count > 0 )
234             {
235 107         192 $count = 0;
236 107         437 foreach my $suit ( sort( keys %suits ) )
237             {
238 129         242 my @src_s;
239 129         238 my $rec = $suits{$suit};
240 129         236 my $start = $rec->{start};
241 129         224 eval { @src_s = $self->_find_card_src_string(
  129         346  
242             $start->to_string ); };
243 129 100       387 if ( !$@ )
244             {
245 86         141 ++$count;
246 86         194 my $rank = $start->rank;
247 86 100       227 if ( $rank == $rec->{end}->rank )
248             {
249 30         67 delete $suits{$suit};
250             }
251             else
252             {
253 56         121 $start->rank( 1 + $rank );
254             }
255 86         528 $self->_perform_and_output_move(
256             sprintf( "Move a card from %s to the foundations",
257             $src_s[1] ),
258             );
259 86         373 next FOUNDATION;
260             }
261             }
262             }
263 21 50       76 if (%suits)
264             {
265 0         0 die "cannot move to foundations - $move_line";
266             }
267             }
268              
269             sub _process_main
270             {
271 2     2   15 my $self = shift;
272              
273 2         14 $self->_read_initial_state;
274 2         19 $self->_input_move_index(1);
275              
276 2         18 open my $in_fh, '<', $self->_sol_filename;
277              
278 2         3313 my $l;
279             FIRST_lines:
280 2         103 while ( $l = <$in_fh> )
281             {
282 2         12 chomp($l);
283 2 50       14 last FIRST_lines if ( $l =~ /\|/ );
284             }
285              
286 2         10 while ( $l =~ /\|/ )
287             {
288 83         251 $self->_perform_move($l);
289 83         289 $l = <$in_fh>;
290 83         308 chomp $l;
291             }
292 2         16 close($in_fh);
293              
294 2         1541 $self->_append("This game is solveable.\n");
295              
296 2         13 return;
297             }
298              
299             1;
300              
301             __END__