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