| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Games::2048::Game; | 
| 2 | 4 |  |  | 4 |  | 288 | use 5.012; | 
|  | 4 |  |  |  |  | 14 |  | 
|  | 4 |  |  |  |  | 1001 |  | 
| 3 | 4 |  |  | 4 |  | 26 | use Moo; | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 29 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | # increment this whenever we break compat with older game objects | 
| 6 |  |  |  |  |  |  | our $VERSION = '0.02'; | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 4 |  |  | 4 |  | 28041 | use Storable; | 
|  | 4 |  |  |  |  | 26209 |  | 
|  | 4 |  |  |  |  | 499 |  | 
| 9 | 4 |  |  | 4 |  | 7489 | use File::Spec::Functions; | 
|  | 4 |  |  |  |  | 5476 |  | 
|  | 4 |  |  |  |  | 535 |  | 
| 10 | 4 |  |  | 4 |  | 19044 | use File::HomeDir; | 
|  | 4 |  |  |  |  | 48482 |  | 
|  | 4 |  |  |  |  | 6874 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | extends 'Games::2048::Board'; | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | has won     => is => 'rw', default => 0; | 
| 15 |  |  |  |  |  |  | has version => is => 'rw', default => __PACKAGE__->VERSION; | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | sub insert_start_tiles { | 
| 18 | 2 |  |  | 2 | 0 | 1010 | my ($self, $start_tiles) = @_; | 
| 19 | 2 |  |  |  |  | 9 | $self->insert_random_tile for 1..$start_tiles; | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | sub insert_random_tile { | 
| 23 | 30 |  |  | 30 | 0 | 2608 | my $self = shift; | 
| 24 | 30 |  |  |  |  | 170 | my @available_cells = $self->available_cells; | 
| 25 | 30 | 100 |  |  |  | 433 | return if !@available_cells; | 
| 26 | 20 |  |  |  |  | 119 | my $cell = $available_cells[rand @available_cells]; | 
| 27 | 20 | 50 |  |  |  | 55 | my $value = rand() < 0.9 ? 2 : 4; | 
| 28 | 20 |  |  |  |  | 332 | $self->insert_tile($cell, $value); | 
| 29 |  |  |  |  |  |  | } | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | sub move_tiles { | 
| 32 | 38 |  |  | 38 | 0 | 1059 | my ($self, $vec) = @_; | 
| 33 | 38 |  |  |  |  | 54 | my $moved; | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 38 |  | 100 |  |  | 191 | my $reverse = $vec->[0] > 0 || $vec->[1] > 0; | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 38 |  |  |  |  | 145 | for my $cell (sort { $reverse } $self->tile_cells) { | 
|  | 741 |  |  |  |  | 1108 |  | 
| 38 | 400 |  |  |  |  | 3042 | my $tile = $self->tile($cell); | 
| 39 | 400 |  |  |  |  | 2593 | my $next = $cell; | 
| 40 | 400 |  |  |  |  | 438 | my $farthest; | 
| 41 | 400 |  | 100 |  |  | 435 | do { | 
| 42 | 571 |  |  |  |  | 1834 | $farthest = $next; | 
| 43 | 571 |  |  |  |  | 3750 | $next = [ map $next->[$_] + $vec->[$_], 0..1 ]; | 
| 44 |  |  |  |  |  |  | } while ($self->within_bounds($next) | 
| 45 |  |  |  |  |  |  | and !$self->tile($next)); | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 400 | 100 |  |  |  | 3003 | if ($self->cells_can_merge($cell, $next)) { | 
|  |  | 100 |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | # merge | 
| 49 | 60 |  |  |  |  | 472 | my $next_tile = $self->tile($next); | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 60 |  |  |  |  | 498 | $tile->moving_from($cell); | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 60 |  |  |  |  | 111 | $tile->merging_tiles(undef); | 
| 54 | 60 |  |  |  |  | 207 | $tile->appear(undef); | 
| 55 | 60 |  |  |  |  | 118 | $next_tile->merging_tiles(undef); | 
| 56 | 60 |  |  |  |  | 129 | $next_tile->appear(undef); | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 60 |  |  |  |  | 1499 | my $merged_tile = Games::2048::Tile->new( | 
| 59 |  |  |  |  |  |  | value => $tile->value + $next_tile->value, | 
| 60 | 60 |  |  |  |  | 244 | merging_tiles => [ sort { $reverse } $tile, $next_tile ], | 
| 61 |  |  |  |  |  |  | merged => 1, | 
| 62 |  |  |  |  |  |  | ); | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 60 |  |  |  |  | 1500 | $self->clear_tile($cell); | 
| 65 | 60 |  |  |  |  | 515 | $self->set_tile($next, $merged_tile); | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 60 |  |  |  |  | 517 | $self->score($self->score + $merged_tile->value); | 
| 68 | 60 | 50 |  |  |  | 285 | $self->best_score($self->score) if $self->score > $self->best_score; | 
| 69 | 60 | 50 | 33 |  |  | 189 | if ($merged_tile->value >= 2048 and !$self->won) { | 
| 70 | 0 |  |  |  |  | 0 | $self->win(1); | 
| 71 | 0 |  |  |  |  | 0 | $self->won(1); | 
| 72 |  |  |  |  |  |  | } | 
| 73 | 60 |  |  |  |  | 169 | $moved = 1; | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  | elsif (!$self->tile($farthest)) { | 
| 76 |  |  |  |  |  |  | # slide | 
| 77 | 102 |  |  |  |  | 1206 | $tile->moving_from($cell); | 
| 78 | 102 |  |  |  |  | 221 | $tile->merging_tiles(undef); | 
| 79 | 102 |  |  |  |  | 374 | $tile->appear(undef); | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 102 |  |  |  |  | 310 | $self->clear_tile($cell); | 
| 82 | 102 |  |  |  |  | 10111 | $self->set_tile($farthest, $tile); | 
| 83 | 102 |  |  |  |  | 823 | $moved = 1; | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 38 |  |  |  |  | 352 | $_->merged(0) for $self->each_tile; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 38 |  |  |  |  | 798 | return $moved; | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | sub move { | 
| 93 | 0 |  |  | 0 | 0 | 0 | my ($self, $vec) = @_; | 
| 94 | 0 | 0 |  |  |  | 0 | if ($self->move_tiles($vec)) { | 
| 95 | 0 |  |  |  |  | 0 | $self->insert_random_tile; | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 0 |  |  |  |  | 0 | $self->needs_redraw(1); | 
| 98 | 0 |  |  |  |  | 0 | $self->moving_vec($vec); | 
| 99 | 0 |  |  |  |  | 0 | $self->moving(Games::2048::Animation->new( | 
| 100 |  |  |  |  |  |  | duration => 0.2, | 
| 101 |  |  |  |  |  |  | )); | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 0 | 0 |  |  |  | 0 | if (!$self->has_moves_remaining) { | 
| 104 | 0 |  |  |  |  | 0 | $self->lose(1); | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | sub cells_can_merge { | 
| 110 | 473 |  |  | 473 | 0 | 9054 | my ($self, $cell, $next) = @_; | 
| 111 | 473 |  |  |  |  | 1201 | my $tile = $self->tile($cell); | 
| 112 | 473 |  |  |  |  | 3729 | my $next_tile = $self->tile($next); | 
| 113 | 473 | 100 | 66 |  |  | 6442 | $tile and $next_tile and !$next_tile->merged and $next_tile->value == $tile->value; | 
|  |  |  | 100 |  |  |  |  | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | sub has_moves_remaining { | 
| 117 | 6 |  |  | 6 | 0 | 14 | my $self = shift; | 
| 118 | 6 | 100 |  |  |  | 25 | return 1 if $self->has_available_cells; | 
| 119 | 3 |  |  |  |  | 43 | for my $vec ([0, -1], [-1, 0]) { | 
| 120 | 5 |  |  |  |  | 14 | for my $cell ($self->each_cell) { | 
| 121 | 73 |  |  |  |  | 290 | my $next = [ map $cell->[$_] + $vec->[$_], 0..1 ]; | 
| 122 | 73 | 100 |  |  |  | 154 | return 1 if $self->cells_can_merge($cell, $next); | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  | } | 
| 125 | 2 |  |  |  |  | 12 | return; | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub _game_file { | 
| 129 | 0 |  |  | 0 |  | 0 | state $dir = eval { | 
| 130 | 0 | 0 |  |  |  | 0 | my $my_dist_method = "my_dist_" . ($^O eq "MSWin32" ? "data" : "config"); | 
| 131 | 0 |  |  |  |  | 0 | File::HomeDir->$my_dist_method("Games-2048", {create => 1}); | 
| 132 |  |  |  |  |  |  | }; | 
| 133 | 0 | 0 |  |  |  | 0 | return if !defined $dir; | 
| 134 | 0 |  |  |  |  | 0 | return catfile($dir, "game.dat"); | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | sub save { | 
| 138 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 139 | 0 |  |  |  |  | 0 | $self->version(__PACKAGE__->VERSION); | 
| 140 | 0 |  |  |  |  | 0 | eval { store($self, _game_file); 1 }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | sub restore { | 
| 144 | 0 |  |  | 0 | 0 | 0 | my $self = eval { retrieve(_game_file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 145 | 0 |  |  |  |  | 0 | $self; | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | sub is_valid { | 
| 149 | 1 |  |  | 1 | 0 | 4912 | my $self = shift; | 
| 150 | 1 | 50 |  |  |  | 30 | defined $self->version and $self->version >= __PACKAGE__->VERSION; | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | 1; |