use
5.012;
has
insert_tiles_on_start
=>
is
=>
'rw'
,
default
=> 2;
has
insert_tiles_on_move
=>
is
=>
'rw'
,
default
=> 1;
has
won
=>
is
=>
'rw'
,
default
=> 0;
has
goal
=>
is
=>
'rw'
,
default
=> 2048;
sub
insert_start_tiles {
my
$self
=
shift
;
return
map
$self
->insert_random_tile, 1..
$self
->insert_tiles_on_start;
}
sub
insert_random_tile {
my
$self
=
shift
;
my
@available_cells
=
$self
->available_cells;
return
if
!
@available_cells
;
my
$cell
=
$available_cells
[
rand
@available_cells
];
my
$value
=
rand
() < 0.9 ? 2 : 4;
$self
->insert_tile(
$cell
,
$value
);
$cell
;
}
sub
insert_tile {
my
(
$self
,
$cell
,
$value
) =
@_
;
my
$tile
= Games::2048::Tile->new(
value
=>
$value
);
$self
->set_tile(
$cell
,
$tile
);
$self
->
next
::method(
$tile
);
}
sub
move_tile {
my
(
$self
,
$cell
,
$next
,
$next_tile
) =
@_
;
$self
->clear_tile(
$cell
);
$self
->set_tile(
$next
,
$next_tile
);
}
sub
merged_tile {
my
(
$self
,
$cell
,
$next
) =
@_
;
my
$tile
=
$self
->tile(
$cell
);
my
$next_tile
=
$self
->tile(
$next
);
my
$merged_tile
= Games::2048::Tile->new(
value
=>
$tile
->value +
$next_tile
->value,
merging_tiles
=> [
$tile
,
$next_tile
],
merged
=> 1,
);
}
sub
move_tiles {
my
(
$self
,
$vec
) =
@_
;
my
$moved
;
my
$move_score
=
"0 but true"
;
my
$reverse
=
$vec
->[0] > 0 ||
$vec
->[1] > 0;
for
my
$cell
(
$reverse
?
reverse
$self
->tile_cells :
$self
->tile_cells) {
my
$tile
=
$self
->tile(
$cell
);
my
$next
=
$cell
;
my
$farthest
;
do
{
$farthest
=
$next
;
$next
= [
map
$next
->[
$_
] +
$vec
->[
$_
], 0..1 ];
}
while
(
$self
->within_bounds(
$next
)
and !
$self
->tile(
$next
));
if
(
$self
->cells_can_merge(
$cell
,
$next
)) {
my
$merged_tile
=
$self
->merged_tile(
$cell
,
$next
);
$self
->move_tile(
$cell
,
$next
,
$merged_tile
);
$move_score
+=
$merged_tile
->value;
$moved
= 1;
}
elsif
(!
$self
->tile(
$farthest
)) {
$self
->move_tile(
$cell
,
$farthest
,
$tile
);
$moved
= 1;
}
}
if
(
$moved
) {
$_
->merged(0)
for
$self
->each_tile;
$self
->
next
::method(
$vec
);
return
$move_score
;
}
return
;
}
sub
move {
my
(
$self
,
$vec
) =
@_
;
my
$move_score
=
$self
->move_tiles(
$vec
);
if
(
$move_score
) {
$self
->insert_random_tile
for
1..
$self
->insert_tiles_on_move;
$self
->score(
$self
->score +
$move_score
);
$self
->best_score(
$self
->score)
if
$self
->score >
$self
->best_score;
if
(
$move_score
>=
$self
->goal and !
$self
->won
and
grep
{
$_
->value >=
$self
->goal }
$self
->each_tile)
{
$self
->win(1);
$self
->won(1);
}
if
(!
$self
->has_moves_remaining) {
$self
->lose(1);
}
return
1;
}
return
;
}
sub
cells_can_merge {
my
(
$self
,
$cell
,
$next
) =
@_
;
my
$tile
=
$self
->tile(
$cell
);
my
$next_tile
=
$self
->tile(
$next
);
$tile
and
$next_tile
and !
$next_tile
->merged and
$next_tile
->value ==
$tile
->value;
}
sub
has_moves_remaining {
my
$self
=
shift
;
return
1
if
$self
->has_available_cells;
for
my
$vec
([0, -1], [-1, 0]) {
for
my
$cell
(
$self
->each_cell) {
my
$next
= [
map
$cell
->[
$_
] +
$vec
->[
$_
], 0..1 ];
return
1
if
$self
->cells_can_merge(
$cell
,
$next
);
}
}
return
;
}
1;