line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Freecell::App::Tableau;
|
2
|
1
|
|
|
1
|
|
5
|
use version;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.03';
|
4
|
1
|
|
|
1
|
|
65
|
use warnings;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
5
|
1
|
|
|
1
|
|
6
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
6
|
1
|
|
|
1
|
|
1076
|
use Storable qw(dclone);
|
|
1
|
|
|
|
|
3581
|
|
|
1
|
|
|
|
|
63
|
|
7
|
1
|
|
|
1
|
|
8
|
use List::Util qw(min);
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3385
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
my %conf = (
|
10
|
|
|
|
|
|
|
winxp_opt => 0, # 1 is solve for XP
|
11
|
|
|
|
|
|
|
winxp_warn => 0, # 1 is invalid for XP
|
12
|
|
|
|
|
|
|
);
|
13
|
|
|
|
|
|
|
sub _property {
|
14
|
0
|
|
|
0
|
|
|
my ($class, $attr, $value) = @_;
|
15
|
0
|
0
|
|
|
|
|
if (defined $value) {
|
16
|
0
|
|
|
|
|
|
my $oldv = $conf{$attr};
|
17
|
0
|
|
|
|
|
|
$conf{$attr} = $value;
|
18
|
0
|
|
|
|
|
|
return $oldv;
|
19
|
|
|
|
|
|
|
}
|
20
|
0
|
|
|
|
|
|
return $conf{$attr};
|
21
|
|
|
|
|
|
|
}
|
22
|
0
|
|
|
0
|
1
|
|
sub winxp_opt () { return shift->_property('winxp_opt', @_) }
|
23
|
0
|
|
|
0
|
1
|
|
sub winxp_warn () { return shift->_property('winxp_warn', @_) }
|
24
|
|
|
|
|
|
|
|
25
|
0
|
|
|
0
|
1
|
|
sub rank { $_[0] & 15 }
|
26
|
0
|
|
|
0
|
1
|
|
sub suit { $_[0] >> 4 & 3 }
|
27
|
0
|
|
|
0
|
1
|
|
sub opposite_colors { ( $_[0] & 16 ) != ( $_[1] & 16 ) }
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub new {
|
30
|
0
|
|
|
0
|
1
|
|
my ( $class, $key, $token ) = @_;
|
31
|
0
|
|
|
|
|
|
my $self = [ map [ (0) x 21 ], 0 .. 7 ];
|
32
|
0
|
|
|
|
|
|
bless $self, $class;
|
33
|
0
|
|
|
|
|
|
$self;
|
34
|
|
|
|
|
|
|
}
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub from_string {
|
37
|
0
|
|
|
0
|
1
|
|
my ( $self, $string ) = @_;
|
38
|
0
|
|
|
|
|
|
my $r = 0;
|
39
|
0
|
|
|
|
|
|
foreach ( split /\n/, $string ) {
|
40
|
0
|
|
|
|
|
|
my $c = 0;
|
41
|
0
|
|
|
|
|
|
while (/(.)(.) ?/g) {
|
42
|
0
|
|
|
|
|
|
my ( $rank, $suit ) = ( $1, $2 );
|
43
|
0
|
0
|
|
|
|
|
unless ( "$rank$suit" eq " " ) {
|
44
|
0
|
|
|
|
|
|
$rank =~ tr/ATJQK/1\:\;\<\=/;
|
45
|
0
|
|
|
|
|
|
$suit =~ tr/DCHS/0123/;
|
46
|
0
|
|
|
|
|
|
$self->[$c][$r] =
|
47
|
|
|
|
|
|
|
64 | ( ( 3 & ord $suit ) << 4 ) + ( 15 & ord $rank );
|
48
|
|
|
|
|
|
|
}
|
49
|
0
|
|
|
|
|
|
$c++;
|
50
|
|
|
|
|
|
|
}
|
51
|
0
|
|
|
|
|
|
$r++;
|
52
|
|
|
|
|
|
|
}
|
53
|
|
|
|
|
|
|
# fix home if out of order
|
54
|
|
|
|
|
|
|
|
55
|
0
|
|
|
|
|
|
my %home = map {
|
56
|
0
|
|
|
|
|
|
my $card = $self->[$_][0];
|
57
|
0
|
|
|
|
|
|
suit($card) + 4, $card;
|
58
|
|
|
|
|
|
|
} 4 .. 7;
|
59
|
0
|
|
|
|
|
|
foreach ( 4 .. 7 ) {
|
60
|
0
|
0
|
|
|
|
|
$self->[$_][0] = exists( $home{$_} ) ? $home{$_} : 0;
|
61
|
|
|
|
|
|
|
}
|
62
|
0
|
|
|
|
|
|
$self;
|
63
|
|
|
|
|
|
|
}
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub from_token {
|
66
|
0
|
|
|
0
|
1
|
|
my ( $self, $key, $token ) = @_;
|
67
|
0
|
|
|
|
|
|
my @i = @{$token};
|
|
0
|
|
|
|
|
|
|
68
|
0
|
|
|
|
|
|
my @t = split / /, $key;
|
69
|
0
|
|
|
|
|
|
my @f = split //, shift @t;
|
70
|
0
|
|
|
|
|
|
foreach ( splice @i, 0, @f ) { # array,offset,length
|
71
|
0
|
|
|
|
|
|
$self->[$_][0] = ord shift @f;
|
72
|
|
|
|
|
|
|
}
|
73
|
0
|
|
|
|
|
|
foreach my $i (@i) {
|
74
|
0
|
|
|
|
|
|
my $j = 1;
|
75
|
0
|
|
|
|
|
|
foreach ( split //, shift @t ) {
|
76
|
0
|
|
|
|
|
|
$self->[$i][ $j++ ] = ord $_;
|
77
|
|
|
|
|
|
|
}
|
78
|
|
|
|
|
|
|
}
|
79
|
0
|
|
|
|
|
|
$self;
|
80
|
|
|
|
|
|
|
}
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub from_deal { # http://rosettacode.org/wiki/Deal_cards_for_FreeCell#Perl
|
83
|
0
|
|
|
0
|
1
|
|
my ( $self, $s ) = @_;
|
84
|
|
|
|
|
|
|
my $rnd = sub {
|
85
|
0
|
|
|
0
|
|
|
return ( ( $s = ( $s * 214013 + 2531011 ) % 2**31 ) >> 16 );
|
86
|
0
|
|
|
|
|
|
};
|
87
|
0
|
|
|
|
|
|
my @d;
|
88
|
0
|
|
|
|
|
|
for my $b ( split "", "A23456789TJQK" ) {
|
89
|
0
|
|
|
|
|
|
push @d, map ( "$b$_", qw/C D H S/ );
|
90
|
|
|
|
|
|
|
}
|
91
|
0
|
|
|
|
|
|
for my $idx ( reverse 0 .. $#d ) {
|
92
|
0
|
|
|
|
|
|
my $r = $rnd->() % ( $idx + 1 );
|
93
|
0
|
|
|
|
|
|
@d[ $r, $idx ] = @d[ $idx, $r ];
|
94
|
|
|
|
|
|
|
}
|
95
|
0
|
|
|
|
|
|
my $cards = [ reverse @d ];
|
96
|
0
|
|
|
|
|
|
my $num_cards_in_height = 8;
|
97
|
0
|
|
|
|
|
|
my $string = '';
|
98
|
0
|
|
|
|
|
|
while (@$cards) {
|
99
|
0
|
|
|
|
|
|
$string .= join( ' ', splice( @$cards, 0, 8 ) ) . "\n";
|
100
|
|
|
|
|
|
|
}
|
101
|
0
|
|
|
|
|
|
$self->from_string( "\n" . $string );
|
102
|
|
|
|
|
|
|
}
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub to_token {
|
105
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
106
|
0
|
|
|
|
|
|
my @t = sort { $a->[1] cmp $b->[1] } grep $_->[1],
|
|
0
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
|
map [ $_, join "", map chr($_), grep $_, @{ $self->[$_] }[ 1 .. 20 ] ],
|
108
|
|
|
|
|
|
|
0 .. 7;
|
109
|
0
|
|
|
|
|
|
my @f = sort { $a->[1] <=> $b->[1] } grep $_->[1],
|
|
0
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
map [ $_, $self->[$_][0] ], 0 .. 7;
|
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
join( " ", join( "", map chr( $_->[1] ), @f ), map $_->[1], @t ),
|
113
|
|
|
|
|
|
|
[ ( map $_->[0], @f ), ( map $_->[0], @t ) ];
|
114
|
|
|
|
|
|
|
}
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub undo {
|
117
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
118
|
0
|
|
|
|
|
|
foreach ( reverse @{ $_[0] } ) {
|
|
0
|
|
|
|
|
|
|
119
|
0
|
|
|
|
|
|
my ( $src_col, $src_row, $dst_col, $dst_row ) = @$_;
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# return dst back to src
|
122
|
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
|
$self->[$src_col][$src_row] = $self->[$dst_col][ $dst_row + 1 ];
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# if dst == home && rank > Ace decrement home else clear
|
126
|
|
|
|
|
|
|
|
127
|
0
|
0
|
0
|
|
|
|
if ( $dst_col > 3
|
|
|
|
0
|
|
|
|
|
128
|
|
|
|
|
|
|
&& $dst_row < 0
|
129
|
|
|
|
|
|
|
&& rank( $self->[$dst_col][ $dst_row + 1 ] ) > 1 )
|
130
|
|
|
|
|
|
|
{
|
131
|
0
|
|
|
|
|
|
$self->[$dst_col][ $dst_row + 1 ]--;
|
132
|
|
|
|
|
|
|
}
|
133
|
|
|
|
|
|
|
else {
|
134
|
0
|
|
|
|
|
|
$self->[$dst_col][ $dst_row + 1 ] = 0;
|
135
|
|
|
|
|
|
|
}
|
136
|
|
|
|
|
|
|
}
|
137
|
|
|
|
|
|
|
}
|
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub play {
|
140
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
141
|
0
|
|
|
|
|
|
my ( $src_col, $src_row, $dst_col, $dst_row ) = @{ $_[0] };
|
|
0
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# dst points to last card in col so move src to dst_row +1
|
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
$self->[$dst_col][ $dst_row + 1 ] = $self->[$src_col][$src_row];
|
146
|
0
|
|
|
|
|
|
$self->[$src_col][$src_row] = 0;
|
147
|
|
|
|
|
|
|
}
|
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub _home {
|
150
|
0
|
|
|
0
|
|
|
my ( $self, $move, $src, $src_col, $src_row, $type ) = @_;
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# src rank == home rank+1 and an A or duece
|
153
|
|
|
|
|
|
|
|
154
|
0
|
0
|
0
|
|
|
|
if (
|
|
|
|
0
|
|
|
|
|
155
|
|
|
|
|
|
|
rank($src) == rank( $self->[ suit($src) + 4 ][0] ) + 1
|
156
|
|
|
|
|
|
|
&& (
|
157
|
|
|
|
|
|
|
rank($src) < 3
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# or src rank <= rank+1 of both home cards of opposite color
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|| 2 ==
|
162
|
|
|
|
|
|
|
grep rank($src) <= rank($_) + 1, # rank($self->[suit($src) + 4][0]
|
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# home cards of opposite colors
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
( map $_->[0], @$self )
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# index of home cards of opposite color; << 4 = 0100.... 0101.... 0110.... 0111....
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
[ grep opposite_colors( $src, $_ << 4 ), 4 .. 7 ]
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
)
|
173
|
|
|
|
|
|
|
)
|
174
|
|
|
|
|
|
|
{
|
175
|
|
|
|
|
|
|
|
176
|
0
|
|
|
|
|
|
$self->play( $_ = [ $src_col, $src_row, suit($src) + 4, -1, $type ] );
|
177
|
0
|
|
|
|
|
|
push @{$move}, $_;
|
|
0
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
|
1;
|
179
|
|
|
|
|
|
|
}
|
180
|
|
|
|
|
|
|
else {
|
181
|
0
|
|
|
|
|
|
0;
|
182
|
|
|
|
|
|
|
}
|
183
|
|
|
|
|
|
|
}
|
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub autoplay {
|
186
|
0
|
|
|
0
|
1
|
|
my ( $self, $move ) = @_;
|
187
|
0
|
|
|
|
|
|
my ( $safe, @z, @auto ) = 1;
|
188
|
0
|
|
|
|
|
|
while ($safe) {
|
189
|
0
|
|
|
|
|
|
map { $z[$_] = grep $_, @{ $self->[$_] }[ 1 .. 20 ] } 0 .. 7;
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
$safe = 0;
|
191
|
0
|
|
|
|
|
|
foreach my $c ( 0 .. 3 ) {
|
192
|
0
|
|
|
|
|
|
my $src = $self->[$c][0];
|
193
|
0
|
0
|
|
|
|
|
next unless $src;
|
194
|
0
|
|
0
|
|
|
|
$safe ||=
|
195
|
|
|
|
|
|
|
$self->_home( $move, $src, $c, 0, 'afh' ); # auto free -> home
|
196
|
|
|
|
|
|
|
}
|
197
|
0
|
|
|
|
|
|
foreach my $c ( 0 .. 7 ) {
|
198
|
0
|
|
|
|
|
|
my $r = $z[$c];
|
199
|
0
|
0
|
|
|
|
|
next unless $r; # any cards in src col?
|
200
|
0
|
|
|
|
|
|
my $src = $self->[$c][$r]; # yes, get last one;
|
201
|
0
|
|
0
|
|
|
|
$safe ||=
|
202
|
|
|
|
|
|
|
$self->_home( $move, $src, $c, $r, 'ach' ); # auto col -> home
|
203
|
|
|
|
|
|
|
}
|
204
|
|
|
|
|
|
|
}
|
205
|
|
|
|
|
|
|
}
|
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub generate_nodelist {
|
208
|
0
|
|
|
0
|
1
|
|
my ( $self ) = @_;
|
209
|
0
|
|
|
|
|
|
my @z = map { scalar grep $_, @$_[ 1 .. 20 ] } @$self;
|
|
0
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
|
my @empty = grep !$self->[$_][1], 0 .. 7;
|
211
|
0
|
|
|
|
|
|
my @free = grep !$self->[$_][0], 0 .. 3;
|
212
|
0
|
|
|
|
|
|
my @moves;
|
213
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
|
foreach my $c ( 0 .. 3 ) {
|
215
|
0
|
|
|
|
|
|
my $src = $self->[$c][0];
|
216
|
0
|
0
|
|
|
|
|
next unless $src;
|
217
|
0
|
0
|
|
|
|
|
if ( rank($src) - 1 == rank( $self->[ suit($src) + 4 ][0] ) ) {
|
218
|
0
|
|
|
|
|
|
push @moves, [ [ $c, 0, suit($src) + 4, -1, 'fh' ] ]; # free->home
|
219
|
|
|
|
|
|
|
}
|
220
|
0
|
0
|
|
|
|
|
if ( @empty > 0 ) {
|
221
|
0
|
|
|
|
|
|
push @moves, [ [ $c, 0, $empty[0], 0, 'fe' ] ]; # free->empty
|
222
|
|
|
|
|
|
|
}
|
223
|
0
|
|
|
|
|
|
foreach my $j ( 0 .. 7 ) {
|
224
|
0
|
0
|
|
|
|
|
next unless $z[$j];
|
225
|
0
|
|
|
|
|
|
my $dst = $self->[$j][ $z[$j] ];
|
226
|
0
|
0
|
0
|
|
|
|
if ( rank($src) + 1 == rank($dst)
|
227
|
|
|
|
|
|
|
&& opposite_colors( $src, $dst ) )
|
228
|
|
|
|
|
|
|
{
|
229
|
0
|
|
|
|
|
|
push @moves, [ [ $c, 0, $j, $z[$j], 'fc' ] ]; # free -> col
|
230
|
|
|
|
|
|
|
}
|
231
|
|
|
|
|
|
|
}
|
232
|
|
|
|
|
|
|
}
|
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
foreach my $c ( 0 .. 7 ) {
|
235
|
0
|
0
|
|
|
|
|
next unless $z[$c]; # any cards in src col?
|
236
|
0
|
|
|
|
|
|
my $src = $self->[$c][ $z[$c] ]; # then get last one;
|
237
|
0
|
0
|
|
|
|
|
if ( rank($src) - 1 == rank( $self->[ suit($src) + 4 ][0] ) ) {
|
238
|
0
|
|
|
|
|
|
push @moves,
|
239
|
|
|
|
|
|
|
[ [ $c, $z[$c], suit($src) + 4, -1, 'ch' ] ]; # col->home
|
240
|
|
|
|
|
|
|
}
|
241
|
0
|
0
|
|
|
|
|
if ( @free > 0 ) {
|
242
|
0
|
|
|
|
|
|
push @moves, [ [ $c, $z[$c], $free[0], -1, 'cf' ] ]; # col->free
|
243
|
|
|
|
|
|
|
}
|
244
|
0
|
0
|
0
|
|
|
|
if ( @empty > 0
|
245
|
|
|
|
|
|
|
&& $z[$c] > 1 )
|
246
|
|
|
|
|
|
|
{
|
247
|
0
|
|
|
|
|
|
push @moves, [ [ $c, $z[$c], $empty[0], 0, 'ce' ] ]; # col->empty
|
248
|
|
|
|
|
|
|
}
|
249
|
|
|
|
|
|
|
|
250
|
0
|
|
|
|
|
|
my $flag = 1;
|
251
|
0
|
|
|
|
|
|
foreach my $j ( 0 .. 7 ) {
|
252
|
0
|
0
|
|
|
|
|
next if $c == $j;
|
253
|
0
|
0
|
|
|
|
|
next unless $z[$j];
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# my $src = $self->[$c][$z[$c]]; # then get last one;
|
256
|
0
|
|
|
|
|
|
my $dst = $self->[$j][ $z[$j] ];
|
257
|
|
|
|
|
|
|
|
258
|
0
|
0
|
0
|
|
|
|
if ( rank($src) + 1 == rank($dst)
|
259
|
|
|
|
|
|
|
&& opposite_colors( $src, $dst ) )
|
260
|
|
|
|
|
|
|
{
|
261
|
0
|
|
|
|
|
|
push @moves, [ [ $c, $z[$c], $j, $z[$j], 'cc' ] ]; # col->col
|
262
|
|
|
|
|
|
|
}
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# super move
|
265
|
0
|
0
|
|
|
|
|
if ( $z[$c] > 1 ) {
|
266
|
0
|
|
|
|
|
|
foreach my $k ( reverse 1 .. $z[$c] - 1 ) {
|
267
|
0
|
|
|
|
|
|
my $srx = $self->[$c][$k];
|
268
|
0
|
0
|
0
|
|
|
|
unless ( rank($srx) - 1 == rank( $self->[$c][ $k + 1 ] )
|
269
|
|
|
|
|
|
|
&& opposite_colors( $srx, $self->[$c][ $k + 1 ] ) )
|
270
|
|
|
|
|
|
|
{
|
271
|
0
|
|
|
|
|
|
last;
|
272
|
|
|
|
|
|
|
}
|
273
|
0
|
0
|
0
|
|
|
|
if ( @empty > 0
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
274
|
|
|
|
|
|
|
&& $k > 1
|
275
|
|
|
|
|
|
|
&& $flag == 1
|
276
|
|
|
|
|
|
|
&& ( $conf{winxp_opt} ? min( 1, scalar @empty ) : @empty ) *
|
277
|
|
|
|
|
|
|
( @free + 1 ) >= ( @_ = $k .. $z[$c] ) )
|
278
|
|
|
|
|
|
|
{ # e*(f+1)
|
279
|
0
|
|
|
|
|
|
my $x = 0;
|
280
|
0
|
|
|
|
|
|
push @moves,
|
281
|
0
|
|
|
|
|
|
[ map { [ $c, $_, $empty[0], $x++, 'sce' ] }
|
282
|
|
|
|
|
|
|
$k .. $z[$c] ]; # col->empty
|
283
|
|
|
|
|
|
|
}
|
284
|
0
|
0
|
0
|
|
|
|
if ( rank($srx) + 1 == rank($dst)
|
|
|
0
|
0
|
|
|
|
|
285
|
|
|
|
|
|
|
&& opposite_colors( $srx, $dst )
|
286
|
|
|
|
|
|
|
&& (
|
287
|
|
|
|
|
|
|
( $conf{winxp_opt} ? min( 1, scalar @empty ) : @empty ) + 1 )
|
288
|
|
|
|
|
|
|
* ( @free + 1 ) >= ( @_ = $k .. $z[$c] ) )
|
289
|
|
|
|
|
|
|
{ # (e+1)*(f+1)
|
290
|
0
|
|
|
|
|
|
my $x = $z[$j];
|
291
|
0
|
|
|
|
|
|
push @moves,
|
292
|
0
|
|
|
|
|
|
[ map { [ $c, $_, $j, $x++, 'scc' ] } $k .. $z[$c] ]
|
293
|
|
|
|
|
|
|
; # col->col
|
294
|
|
|
|
|
|
|
}
|
295
|
|
|
|
|
|
|
}
|
296
|
0
|
|
|
|
|
|
$flag = 0;
|
297
|
|
|
|
|
|
|
}
|
298
|
|
|
|
|
|
|
}
|
299
|
|
|
|
|
|
|
}
|
300
|
0
|
|
|
|
|
|
\@moves;
|
301
|
|
|
|
|
|
|
}
|
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
sub to_card {
|
304
|
0
|
|
|
0
|
1
|
|
qw(0 A 2 3 4 5 6 7 8 9 T J Q K) [ rank( $_[0] ) ]
|
305
|
|
|
|
|
|
|
. qw(D C H S) [ suit( $_[0] ) ];
|
306
|
|
|
|
|
|
|
}
|
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub to_string {
|
309
|
0
|
|
|
0
|
1
|
|
my $self = shift;
|
310
|
0
|
|
|
|
|
|
my ( $x, $result ) = 0;
|
311
|
0
|
|
|
|
|
|
while (1) {
|
312
|
0
|
|
|
|
|
|
my @r = map {
|
313
|
0
|
|
|
|
|
|
my $card = $_->[$x];
|
314
|
0
|
0
|
|
|
|
|
$card == 0 ? " " : to_card($card) . " ";
|
315
|
|
|
|
|
|
|
} @$self;
|
316
|
0
|
|
|
|
|
|
$result .= sprintf "%s\n", join "", @r;
|
317
|
0
|
0
|
0
|
|
|
|
last if $x++ > 0 && 8 == grep $_ eq " ", @r;
|
318
|
|
|
|
|
|
|
}
|
319
|
0
|
|
|
|
|
|
$result;
|
320
|
|
|
|
|
|
|
}
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub notation {
|
323
|
0
|
|
|
0
|
1
|
|
my $self = dclone shift;
|
324
|
|
|
|
|
|
|
my (
|
325
|
0
|
|
|
|
|
|
$i, $super_cnt, $super_orig, $std_src,
|
326
|
|
|
|
|
|
|
$std_dst, @dsc_src, $dsc_dst, %auto,
|
327
|
|
|
|
|
|
|
@z, @empty, @free
|
328
|
|
|
|
|
|
|
) = ( 0, 0, "" );
|
329
|
|
|
|
|
|
|
|
330
|
0
|
|
|
|
|
|
map {
|
331
|
0
|
|
|
|
|
|
my ( $src_col, $src_row, $dst_col, $dst_row, $origin ) = @$_;
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# build both standard and descriptive notation
|
334
|
|
|
|
|
|
|
|
335
|
0
|
0
|
|
|
|
|
if ( $i == 0 ) {
|
336
|
0
|
0
|
|
|
|
|
$std_src =
|
|
|
0
|
|
|
|
|
|
337
|
|
|
|
|
|
|
( $src_row > 0 ? $src_col + 1
|
338
|
|
|
|
|
|
|
: $src_col > 3 ? "h"
|
339
|
|
|
|
|
|
|
: qw(a b c d) [$src_col] );
|
340
|
0
|
0
|
|
|
|
|
$std_dst =
|
|
|
0
|
|
|
|
|
|
341
|
|
|
|
|
|
|
( $dst_row > -1 ? $dst_col + 1
|
342
|
|
|
|
|
|
|
: $dst_col > 3 ? "h"
|
343
|
|
|
|
|
|
|
: qw(a b c d) [$dst_col] );
|
344
|
0
|
0
|
|
|
|
|
$dsc_dst =
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
345
|
|
|
|
|
|
|
$dst_row == 0 ? "empty column"
|
346
|
|
|
|
|
|
|
: $std_dst =~ /\d/ ? to_card( $self->[$dst_col][$dst_row] )
|
347
|
|
|
|
|
|
|
: $std_dst =~ /h/ ? "home"
|
348
|
|
|
|
|
|
|
: "freecell";
|
349
|
|
|
|
|
|
|
}
|
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# gather move card cnt for super move
|
352
|
|
|
|
|
|
|
|
353
|
0
|
0
|
|
|
|
|
if ( $origin =~ /^s/ ) {
|
354
|
0
|
0
|
|
|
|
|
if ( $super_cnt == 0 ) {
|
355
|
0
|
|
|
|
|
|
$super_orig = $origin;
|
356
|
0
|
|
|
|
|
|
@empty = grep !$self->[$_][1], 0 .. 7;
|
357
|
0
|
|
|
|
|
|
@free = grep !$self->[$_][0], 0 .. 3;
|
358
|
|
|
|
|
|
|
}
|
359
|
0
|
|
|
|
|
|
$super_cnt++;
|
360
|
|
|
|
|
|
|
}
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# build descriptive source notation
|
363
|
|
|
|
|
|
|
|
364
|
0
|
|
|
|
|
|
my $num = $self->[$src_col][$src_row];
|
365
|
0
|
0
|
|
|
|
|
if ( $origin =~ /^a/ ) {
|
366
|
0
|
|
|
|
|
|
$auto{ suit($num) }[ rank($num) ] = to_card($num);
|
367
|
|
|
|
|
|
|
}
|
368
|
|
|
|
|
|
|
else {
|
369
|
0
|
|
|
|
|
|
push @dsc_src, to_card($num);
|
370
|
|
|
|
|
|
|
}
|
371
|
0
|
|
|
|
|
|
$self->play($_);
|
372
|
0
|
|
|
|
|
|
$i++;
|
373
|
0
|
|
|
|
|
|
} @{ $_[0] }; # node array
|
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# if a super move, is it valid for XP ?
|
376
|
|
|
|
|
|
|
|
377
|
0
|
0
|
0
|
|
|
|
if (
|
378
|
|
|
|
|
|
|
$super_cnt
|
379
|
|
|
|
|
|
|
&& !(
|
380
|
|
|
|
|
|
|
( min( 1, scalar @empty ) + $super_orig =~ /c$/ ) * ( @free + 1 )
|
381
|
|
|
|
|
|
|
>= $super_cnt
|
382
|
|
|
|
|
|
|
)
|
383
|
|
|
|
|
|
|
)
|
384
|
|
|
|
|
|
|
{
|
385
|
0
|
|
|
|
|
|
$conf{winxp_warn} = 1;
|
386
|
|
|
|
|
|
|
}
|
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
# output notation
|
389
|
|
|
|
|
|
|
|
390
|
0
|
|
|
|
|
|
$std_src . $std_dst, # standard notation
|
391
|
|
|
|
|
|
|
$dsc_src[0] . ( @dsc_src == 1 ? "" : "-" . $dsc_src[-1] ),
|
392
|
|
|
|
|
|
|
$dsc_dst, # descriptive notation
|
393
|
|
|
|
|
|
|
join ", ", map {
|
394
|
0
|
0
|
|
|
|
|
my @h = grep $_, @{ $auto{$_} }; # autoplay notation
|
|
0
|
|
|
|
|
|
|
395
|
0
|
0
|
|
|
|
|
$h[0] . ( @h == 1 ? "" : "-" . $h[-1] );
|
396
|
|
|
|
|
|
|
} sort keys %auto;
|
397
|
|
|
|
|
|
|
}
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub heuristic {
|
400
|
0
|
|
|
0
|
1
|
|
my ($self) = @_;
|
401
|
0
|
|
|
|
|
|
my $score = 64;
|
402
|
0
|
|
|
|
|
|
my @z = map { scalar grep $_, @$_[ 1 .. 20 ] } @$self;
|
|
0
|
|
|
|
|
|
|
403
|
0
|
|
|
|
|
|
map $score -= rank( $self->[$_][0] ), 4 .. 7; # -sum home
|
404
|
0
|
|
|
|
|
|
$score -= grep !$self->[$_][1], 0 .. 7; # -empty
|
405
|
0
|
|
|
|
|
|
$score -= grep !$self->[$_][0], 0 .. 3; # -free
|
406
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
|
my $seq = 0;
|
408
|
0
|
|
|
|
|
|
foreach my $c ( 0 .. 7 ) { # +sum column sequence breaks
|
409
|
0
|
0
|
|
|
|
|
next unless $z[$c] > 1;
|
410
|
0
|
|
|
|
|
|
foreach my $r ( 1 .. ( $z[$c] - 1 ) ) {
|
411
|
0
|
|
|
|
|
|
my $src0 = $self->[$c][$r];
|
412
|
0
|
|
|
|
|
|
my $src1 = $self->[$c][ $r + 1 ];
|
413
|
0
|
|
0
|
|
|
|
my $brk = !opposite_colors( $src1, $src0 )
|
414
|
|
|
|
|
|
|
|| rank($src1) + 1 != rank($src0);
|
415
|
0
|
0
|
|
|
|
|
if ($brk) {
|
416
|
0
|
|
|
|
|
|
$score += $brk; # algorithn 1
|
417
|
0
|
|
|
|
|
|
$seq += $src1 >= $src0; # algorithm 2 - major seq break
|
418
|
|
|
|
|
|
|
}
|
419
|
|
|
|
|
|
|
}
|
420
|
|
|
|
|
|
|
}
|
421
|
0
|
|
|
|
|
|
[ $score, $score + $seq ];
|
422
|
|
|
|
|
|
|
}
|
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
__END__
|