line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Games::Affenspiel library, Copyright (C) 2006 Mikhael Goikhman |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify |
4
|
|
|
|
|
|
|
# it under the terms of the GNU General Public License as published by |
5
|
|
|
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or |
6
|
|
|
|
|
|
|
# (at your option) any later version. |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, |
9
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
10
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11
|
|
|
|
|
|
|
# GNU General Public License for more details. |
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License |
14
|
|
|
|
|
|
|
# along with this program; if not, write to the Free Software |
15
|
|
|
|
|
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
package Games::Affenspiel::Board; |
18
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
1135
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
384
|
|
20
|
1
|
|
|
1
|
|
8
|
use warnings; |
|
1
|
|
|
|
|
14
|
|
|
1
|
|
|
|
|
83
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my $is_pipe = !-t STDOUT; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use constant { |
25
|
1
|
|
|
|
|
167
|
GAP => 0, |
26
|
|
|
|
|
|
|
SQUARE1 => 1, |
27
|
|
|
|
|
|
|
VER_BAR => 2, |
28
|
|
|
|
|
|
|
HOR_BAR => 3, |
29
|
|
|
|
|
|
|
SQUARE2 => 4, |
30
|
1
|
|
|
1
|
|
6
|
}; |
|
1
|
|
|
|
|
1
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
use constant { |
33
|
1
|
|
|
|
|
3079
|
UN => 'O', |
34
|
|
|
|
|
|
|
V1 => 'A', |
35
|
|
|
|
|
|
|
V2 => 'V', |
36
|
|
|
|
|
|
|
H1 => '<', |
37
|
|
|
|
|
|
|
H2 => '>', |
38
|
|
|
|
|
|
|
S1 => '/', |
39
|
|
|
|
|
|
|
S2 => '\\', |
40
|
|
|
|
|
|
|
S3 => '[', |
41
|
|
|
|
|
|
|
S4 => ']', |
42
|
|
|
|
|
|
|
GP => ' ', |
43
|
|
|
|
|
|
|
IN => '?', |
44
|
1
|
|
|
1
|
|
6
|
}; |
|
1
|
|
|
|
|
1
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
my $policy = 0; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub set_policy ($) { |
49
|
0
|
|
0
|
0
|
0
|
|
$policy = shift || 0; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub new ($;$) { |
53
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
54
|
0
|
|
|
|
|
|
my $num = shift; |
55
|
|
|
|
|
|
|
|
56
|
0
|
|
|
|
|
|
return bless([], $class)->reset($num); |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub clone ($) { |
60
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
61
|
|
|
|
|
|
|
|
62
|
0
|
|
|
|
|
|
my $new_board = ref($self)->new; |
63
|
0
|
|
|
|
|
|
$new_board->[$_] = [ @{$self->[$_]} ] for 0 .. 4; |
|
0
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
|
return $new_board; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub reset ($;$) { |
69
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
70
|
0
|
|
0
|
|
|
|
my $num = shift || 0; |
71
|
|
|
|
|
|
|
|
72
|
0
|
0
|
|
|
|
|
if ($num == 1) { |
|
|
0
|
|
|
|
|
|
73
|
0
|
|
|
|
|
|
$self->[0] = [ GP, S1, S2, GP, ]; |
74
|
0
|
|
|
|
|
|
$self->[1] = [ GP, S3, S4, GP, ]; |
75
|
0
|
|
|
|
|
|
$self->[2] = [ GP, GP, GP, GP, ]; |
76
|
0
|
|
|
|
|
|
$self->[3] = [ GP, GP, GP, GP, ]; |
77
|
0
|
|
|
|
|
|
$self->[4] = [ GP, GP, GP, GP, ]; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
elsif ($num == 2) { |
80
|
0
|
|
|
|
|
|
$self->[0] = [ V1, S1, S2, V1, ]; |
81
|
0
|
|
|
|
|
|
$self->[1] = [ V2, S3, S4, V2, ]; |
82
|
0
|
|
|
|
|
|
$self->[2] = [ GP, H1, H2, GP, ]; |
83
|
0
|
|
|
|
|
|
$self->[3] = [ UN, H1, H2, UN, ]; |
84
|
0
|
|
|
|
|
|
$self->[4] = [ UN, H1, H2, UN, ]; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
else { |
87
|
0
|
|
|
|
|
|
$self->[0] = [ V1, S1, S2, V1, ]; |
88
|
0
|
|
|
|
|
|
$self->[1] = [ V2, S3, S4, V2, ]; |
89
|
0
|
|
|
|
|
|
$self->[2] = [ GP, H1, H2, GP, ]; |
90
|
0
|
|
|
|
|
|
$self->[3] = [ V1, UN, UN, V1, ]; |
91
|
0
|
|
|
|
|
|
$self->[4] = [ V2, UN, UN, V2, ]; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
|
return $self; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub is_final ($) { |
98
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
return |
101
|
0
|
|
0
|
|
|
|
$self->get_cell_at([4, 1]) eq S3 && |
102
|
|
|
|
|
|
|
$self->get_cell_at([4, 2]) eq S4; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub show ($) { |
106
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
107
|
|
|
|
|
|
|
|
108
|
0
|
|
0
|
|
|
|
my $plain_ascii = $is_pipe || $ENV{DUMB_CHARS} || !$ENV{TERM}; |
109
|
|
|
|
|
|
|
|
110
|
0
|
0
|
|
|
|
|
my $v = $plain_ascii ? '|' : "\cNx\cO"; |
111
|
0
|
0
|
|
|
|
|
my $h = $plain_ascii ? '-' : "\cNq\cO"; |
112
|
0
|
0
|
|
|
|
|
my $ul = $plain_ascii ? '+' : "\cNl\cO"; |
113
|
0
|
0
|
|
|
|
|
my $ur = $plain_ascii ? '+' : "\cNk\cO"; |
114
|
0
|
0
|
|
|
|
|
my $dl = $plain_ascii ? '+' : "\cNm\cO"; |
115
|
0
|
0
|
|
|
|
|
my $dr = $plain_ascii ? '+' : "\cNj\cO"; |
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
|
print "$ul$h$h$h$h$ur\n"; |
118
|
0
|
|
|
|
|
|
foreach my $row (@$self) { |
119
|
0
|
|
|
|
|
|
print "$v"; |
120
|
0
|
|
|
|
|
|
print $_ for @$row; |
121
|
0
|
|
|
|
|
|
print "$v\n"; |
122
|
|
|
|
|
|
|
} |
123
|
0
|
|
|
|
|
|
print "$dl$h$h$h$h$dr\n"; |
124
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
|
return $self; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub hash ($) { |
129
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
130
|
|
|
|
|
|
|
|
131
|
0
|
0
|
|
|
|
|
return join('', map { map { my $v = $self->get_bar_by_first_cell($_); defined $v ? $v : '' } @$_ } @$self); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub hash2 ($) { |
135
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
136
|
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
|
return join('', map { map { $self->get_bar_by_cell($_) } @$_ } @$self); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub stringify_position ($) { |
141
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
142
|
0
|
|
|
|
|
|
my $position = shift; |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
|
return '[' . join(', ', @$position) . ']'; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub get_cell_at ($$) { |
148
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
149
|
0
|
|
|
|
|
|
my $position = shift; |
150
|
|
|
|
|
|
|
|
151
|
0
|
0
|
0
|
|
|
|
return IN |
|
|
|
0
|
|
|
|
|
152
|
|
|
|
|
|
|
if $position->[0] < 0 || $position->[1] < 0 |
153
|
|
|
|
|
|
|
|| !$self->[$position->[0]]; |
154
|
0
|
|
0
|
|
|
|
return $self->[$position->[0]]->[$position->[1]] || IN; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub set_cell_at ($$$) { |
158
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
159
|
0
|
|
|
|
|
|
my $position = shift; |
160
|
0
|
|
|
|
|
|
my $value = shift; |
161
|
|
|
|
|
|
|
|
162
|
0
|
0
|
|
|
|
|
die "Incorrect setting out of board at position " |
163
|
|
|
|
|
|
|
. $self->stringify_position($position) . "\n" |
164
|
|
|
|
|
|
|
unless $self->[$position->[0]]->[$position->[1]]; |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
return $self->[$position->[0]]->[$position->[1]] = $value; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub get_gap_positions ($) { |
170
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
171
|
|
|
|
|
|
|
|
172
|
0
|
|
|
|
|
|
my @gap_positions; |
173
|
0
|
|
|
|
|
|
for my $y (0 .. 4) { |
174
|
0
|
|
|
|
|
|
for my $x (0 .. 3) { |
175
|
0
|
0
|
|
|
|
|
push @gap_positions, [ $y, $x ] if $self->[$y][$x] eq GP; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
return @gap_positions; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub is_adjacent_positions ($$$) { |
183
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
184
|
0
|
|
|
|
|
|
my $position1 = shift; |
185
|
0
|
|
|
|
|
|
my $position2 = shift; |
186
|
|
|
|
|
|
|
|
187
|
0
|
|
|
|
|
|
my ($y1, $x1) = @$position1; |
188
|
0
|
|
|
|
|
|
my ($y2, $x2) = @$position2; |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
return |
191
|
0
|
0
|
0
|
|
|
|
$x1 == $x2 && abs($y1 - $y2) == 1 ? 'v' : |
|
|
0
|
0
|
|
|
|
|
192
|
|
|
|
|
|
|
$y1 == $y2 && abs($x1 - $x2) == 1 ? 'h' : |
193
|
|
|
|
|
|
|
undef; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub is_ver ($) { |
197
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
198
|
0
|
|
|
|
|
|
my $direction = shift; |
199
|
|
|
|
|
|
|
|
200
|
0
|
|
0
|
|
|
|
return $direction eq 'u' || $direction eq 'd'; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub is_hor ($) { |
204
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
205
|
0
|
|
|
|
|
|
my $direction = shift; |
206
|
|
|
|
|
|
|
|
207
|
0
|
|
0
|
|
|
|
return $direction eq 'l' || $direction eq 'r'; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub apply_direction ($$$;$) { |
211
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
212
|
0
|
|
|
|
|
|
my $position = shift; |
213
|
0
|
|
|
|
|
|
my $direction = shift; |
214
|
0
|
|
0
|
|
|
|
my $reverse = shift || 0; |
215
|
|
|
|
|
|
|
|
216
|
0
|
|
|
|
|
|
my $position2 = [ @$position ]; |
217
|
|
|
|
|
|
|
|
218
|
0
|
0
|
|
|
|
|
$position2->[0]-- if $direction eq ($reverse ? 'd' : 'u'); |
|
|
0
|
|
|
|
|
|
219
|
0
|
0
|
|
|
|
|
$position2->[0]++ if $direction eq ($reverse ? 'u' : 'd'); |
|
|
0
|
|
|
|
|
|
220
|
0
|
0
|
|
|
|
|
$position2->[1]-- if $direction eq ($reverse ? 'r' : 'l'); |
|
|
0
|
|
|
|
|
|
221
|
0
|
0
|
|
|
|
|
$position2->[1]++ if $direction eq ($reverse ? 'l' : 'r'); |
|
|
0
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
223
|
0
|
|
|
|
|
|
return $position2; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub get_bar_by_cell ($$) { |
227
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
228
|
0
|
|
|
|
|
|
my $cell = shift; |
229
|
|
|
|
|
|
|
|
230
|
0
|
0
|
|
|
|
|
return SQUARE1 if $cell eq UN; |
231
|
0
|
0
|
0
|
|
|
|
return VER_BAR if $cell eq V1 || $cell eq V2; |
232
|
0
|
0
|
0
|
|
|
|
return HOR_BAR if $cell eq H1 || $cell eq H2; |
233
|
0
|
0
|
0
|
|
|
|
return SQUARE2 if $cell eq S1 || $cell eq S2 || $cell eq S3 || $cell eq S4; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
234
|
0
|
0
|
|
|
|
|
return GAP if $cell eq GP; |
235
|
0
|
|
|
|
|
|
return undef; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub get_bar_by_first_cell ($$) { |
239
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
240
|
0
|
|
|
|
|
|
my $cell = shift; |
241
|
|
|
|
|
|
|
|
242
|
0
|
0
|
|
|
|
|
return SQUARE1 if $cell eq UN; |
243
|
0
|
0
|
|
|
|
|
return VER_BAR if $cell eq V1; |
244
|
0
|
0
|
|
|
|
|
return HOR_BAR if $cell eq H1; |
245
|
0
|
0
|
|
|
|
|
return SQUARE2 if $cell eq S1; |
246
|
0
|
0
|
|
|
|
|
return GAP if $cell eq GP; |
247
|
0
|
|
|
|
|
|
return undef; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
sub move ($$$) { |
251
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
252
|
0
|
|
|
|
|
|
my $gap1_position = shift; |
253
|
0
|
|
|
|
|
|
my $direction = shift; |
254
|
|
|
|
|
|
|
|
255
|
0
|
0
|
|
|
|
|
return undef unless $self->get_cell_at($gap1_position) eq GP; |
256
|
|
|
|
|
|
|
|
257
|
0
|
|
|
|
|
|
my $bar1_position = $self->apply_direction($gap1_position, $direction, 1); |
258
|
0
|
|
|
|
|
|
my $bar1_cell = $self->get_cell_at($bar1_position); |
259
|
0
|
|
|
|
|
|
my $bar = $self->get_bar_by_cell($bar1_cell); |
260
|
0
|
0
|
|
|
|
|
return undef unless $bar; |
261
|
|
|
|
|
|
|
|
262
|
0
|
0
|
|
|
|
|
if ($bar == SQUARE1) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
263
|
0
|
|
|
|
|
|
$self->set_cell_at($gap1_position, UN); |
264
|
0
|
|
|
|
|
|
$self->set_cell_at($bar1_position, GP); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
elsif ($bar == VER_BAR) { |
267
|
0
|
0
|
|
|
|
|
if ($self->is_hor($direction)) { |
268
|
0
|
0
|
|
|
|
|
my $alt_direction = $bar1_cell eq V1 ? 'd' : 'u'; |
269
|
0
|
|
|
|
|
|
my $gap2_position = $self->apply_direction($gap1_position, $alt_direction); |
270
|
0
|
|
|
|
|
|
my $bar2_position = $self->apply_direction($bar1_position, $alt_direction); |
271
|
0
|
0
|
|
|
|
|
return undef unless $self->get_cell_at($gap2_position) eq GP; |
272
|
0
|
|
|
|
|
|
my $bar2_cell = $self->get_cell_at($bar2_position); |
273
|
0
|
0
|
|
|
|
|
return undef unless $self->get_bar_by_cell($bar2_cell) eq VER_BAR; |
274
|
0
|
|
|
|
|
|
$self->set_cell_at($gap1_position, $bar1_cell); |
275
|
0
|
|
|
|
|
|
$self->set_cell_at($gap2_position, $bar2_cell); |
276
|
0
|
|
|
|
|
|
$self->set_cell_at($bar1_position, GP); |
277
|
0
|
|
|
|
|
|
$self->set_cell_at($bar2_position, GP); |
278
|
|
|
|
|
|
|
} else { |
279
|
0
|
|
|
|
|
|
my $bar2_position = $self->apply_direction($bar1_position, $direction, 1); |
280
|
0
|
|
|
|
|
|
my $bar2_cell = $self->get_cell_at($bar2_position); |
281
|
0
|
|
|
|
|
|
$self->set_cell_at($gap1_position, $bar1_cell); |
282
|
0
|
|
|
|
|
|
$self->set_cell_at($bar1_position, $bar2_cell); |
283
|
0
|
|
|
|
|
|
$self->set_cell_at($bar2_position, GP); |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
elsif ($bar == HOR_BAR) { |
287
|
0
|
0
|
|
|
|
|
if ($self->is_ver($direction)) { |
288
|
0
|
0
|
|
|
|
|
my $alt_direction = $bar1_cell eq H1 ? 'r' : 'l'; |
289
|
0
|
|
|
|
|
|
my $gap2_position = $self->apply_direction($gap1_position, $alt_direction); |
290
|
0
|
|
|
|
|
|
my $bar2_position = $self->apply_direction($bar1_position, $alt_direction); |
291
|
0
|
0
|
|
|
|
|
return undef unless $self->get_cell_at($gap2_position) eq GP; |
292
|
0
|
|
|
|
|
|
my $bar2_cell = $self->get_cell_at($bar2_position); |
293
|
0
|
0
|
|
|
|
|
return undef unless $self->get_bar_by_cell($bar2_cell) eq HOR_BAR; |
294
|
0
|
|
|
|
|
|
$self->set_cell_at($gap1_position, $bar1_cell); |
295
|
0
|
|
|
|
|
|
$self->set_cell_at($gap2_position, $bar2_cell); |
296
|
0
|
|
|
|
|
|
$self->set_cell_at($bar1_position, GP); |
297
|
0
|
|
|
|
|
|
$self->set_cell_at($bar2_position, GP); |
298
|
|
|
|
|
|
|
} else { |
299
|
0
|
|
|
|
|
|
my $bar2_position = $self->apply_direction($bar1_position, $direction, 1); |
300
|
0
|
|
|
|
|
|
my $bar2_cell = $self->get_cell_at($bar2_position); |
301
|
0
|
|
|
|
|
|
$self->set_cell_at($gap1_position, $bar1_cell); |
302
|
0
|
|
|
|
|
|
$self->set_cell_at($bar1_position, $bar2_cell); |
303
|
0
|
|
|
|
|
|
$self->set_cell_at($bar2_position, GP); |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
elsif ($bar == SQUARE2) { |
307
|
0
|
0
|
|
|
|
|
my $alt_direction = $self->is_ver($direction) |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
308
|
|
|
|
|
|
|
? $bar1_cell eq S1 ? 'r' : $bar1_cell eq S2 ? 'l' : $bar1_cell eq S3 ? 'r' : 'l' |
309
|
|
|
|
|
|
|
: $bar1_cell eq S1 ? 'd' : $bar1_cell eq S2 ? 'd' : $bar1_cell eq S3 ? 'u' : 'u'; |
310
|
0
|
|
|
|
|
|
my $gap2_position = $self->apply_direction($gap1_position, $alt_direction); |
311
|
0
|
|
|
|
|
|
my $bar2_position = $self->apply_direction($bar1_position, $alt_direction); |
312
|
0
|
|
|
|
|
|
my $bar3_position = $self->apply_direction($bar1_position, $direction, 1); |
313
|
0
|
|
|
|
|
|
my $bar4_position = $self->apply_direction($bar2_position, $direction, 1); |
314
|
0
|
0
|
|
|
|
|
return undef unless $self->get_cell_at($gap2_position) eq GP; |
315
|
0
|
|
|
|
|
|
my $bar2_cell = $self->get_cell_at($bar2_position); |
316
|
0
|
|
|
|
|
|
my $bar3_cell = $self->get_cell_at($bar3_position); |
317
|
0
|
|
|
|
|
|
my $bar4_cell = $self->get_cell_at($bar4_position); |
318
|
0
|
0
|
|
|
|
|
return undef unless $self->get_bar_by_cell($bar2_cell) eq SQUARE2; |
319
|
0
|
|
|
|
|
|
$self->set_cell_at($gap1_position, $bar1_cell); |
320
|
0
|
|
|
|
|
|
$self->set_cell_at($gap2_position, $bar2_cell); |
321
|
0
|
|
|
|
|
|
$self->set_cell_at($bar1_position, $bar3_cell); |
322
|
0
|
|
|
|
|
|
$self->set_cell_at($bar2_position, $bar4_cell); |
323
|
0
|
|
|
|
|
|
$self->set_cell_at($bar3_position, GP); |
324
|
0
|
|
|
|
|
|
$self->set_cell_at($bar4_position, GP); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
0
|
0
|
|
|
|
|
print "$direction -> ", $self->stringify_position($gap1_position), "\n" |
328
|
|
|
|
|
|
|
if $ENV{DEBUG_MOVES}; |
329
|
|
|
|
|
|
|
|
330
|
0
|
|
|
|
|
|
return $bar; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub choose_random_move ($) { |
334
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
335
|
|
|
|
|
|
|
|
336
|
0
|
|
|
|
|
|
my @gap_positions = $self->get_gap_positions; |
337
|
0
|
|
|
|
|
|
my ($bar, $gap_position, $direction); |
338
|
|
|
|
|
|
|
|
339
|
0
|
|
|
|
|
|
until (defined($bar = $self->move( |
340
|
|
|
|
|
|
|
$gap_position = $gap_positions[int(rand(scalar @gap_positions))], |
341
|
|
|
|
|
|
|
$direction = ['u', 'd', 'l', 'r']->[int(rand(4))] |
342
|
|
|
|
|
|
|
))) {} |
343
|
|
|
|
|
|
|
|
344
|
0
|
|
|
|
|
|
return ($bar, $gap_position, $direction); |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub expand_valid_moves ($) { |
348
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
349
|
|
|
|
|
|
|
|
350
|
0
|
|
|
|
|
|
my @gap_positions = $self->get_gap_positions; |
351
|
0
|
|
|
|
|
|
my @move_infos = (); |
352
|
0
|
|
|
|
|
|
my $included_boards = {}; |
353
|
|
|
|
|
|
|
|
354
|
0
|
|
|
|
|
|
for my $gap_position (@gap_positions) { |
355
|
0
|
|
|
|
|
|
for my $direction ('u', 'd', 'l', 'r') { |
356
|
0
|
|
|
|
|
|
my $board = $self->clone; |
357
|
0
|
|
|
|
|
|
my $bar = $board->move($gap_position, $direction); |
358
|
0
|
0
|
|
|
|
|
next unless $bar; |
359
|
0
|
|
|
|
|
|
my $hash = $board->hash; |
360
|
0
|
0
|
|
|
|
|
next if $included_boards->{$hash}; |
361
|
0
|
|
|
|
|
|
$included_boards->{$hash} = 1; |
362
|
0
|
|
|
|
|
|
push @move_infos, [ $bar, $gap_position, $direction, $board ]; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
0
|
0
|
0
|
|
|
|
@move_infos = sort { $b->[0] <=> $a->[0] } @move_infos |
|
0
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
if $policy == 2 || $policy == 3; |
368
|
0
|
0
|
0
|
|
|
|
@move_infos = reverse @move_infos |
369
|
|
|
|
|
|
|
if $policy == 1 || $policy == 3; |
370
|
0
|
0
|
|
|
|
|
@move_infos = sort { rand(2) < 1 ? 1 : -1 } @move_infos |
|
0
|
0
|
|
|
|
|
|
371
|
|
|
|
|
|
|
if $policy == -1; |
372
|
|
|
|
|
|
|
|
373
|
0
|
|
|
|
|
|
return \@move_infos; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
1; |