File Coverage

blib/lib/Games/Solitaire/Verify/App/CmdLine/From_ShirlHartSolver.pm
Criterion Covered Total %
statement 122 140 87.1
branch 43 66 65.1
condition n/a
subroutine 15 15 100.0
pod n/a
total 180 221 81.4


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