| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Games::Sokoban::Controller; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 72724 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 42 |  | 
| 4 | 1 |  |  | 1 |  | 7 | use warnings; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 31 |  | 
| 5 | 1 |  |  | 1 |  | 1133 | use Games::Sokoban; | 
|  | 1 |  |  |  |  | 6867 |  | 
|  | 1 |  |  |  |  | 1614 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | our $VERSION = '0.01'; | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | sub new { | 
| 10 | 0 |  |  | 0 | 1 |  | my $class = shift; | 
| 11 | 0 |  |  |  |  |  | bless { model => Games::Sokoban->new, @_ }, $class; | 
| 12 |  |  |  |  |  |  | } | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | sub set_data { | 
| 15 | 0 |  |  | 0 | 1 |  | my ($self, $data, $format) = @_; | 
| 16 | 0 |  |  |  |  |  | $self->{model}->data($data, $format); | 
| 17 | 0 |  |  |  |  |  | $self->{level_id} = $self->{model}->normalise; | 
| 18 | 0 |  |  |  |  |  | $self->{model}->data($data, $format); | 
| 19 | 0 |  |  |  |  |  | $self->reset; | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  |  | 
| 22 | 0 |  |  | 0 | 1 |  | sub level_id { shift->{level_id} } | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | sub reset { | 
| 25 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 26 | 0 |  |  |  |  |  | my $model = $self->{model}; | 
| 27 | 0 |  |  |  |  |  | $self->{data} = $model->data; | 
| 28 | 0 |  |  |  |  |  | $self->{size} = [$model->{w}, $model->{h}]; | 
| 29 | 0 |  |  |  |  |  | $self->{pos}  = [$model->start]; | 
| 30 | 0 |  |  |  |  |  | $self->{replaced} = []; | 
| 31 | 0 |  |  |  |  |  | $self->{step} = 0; | 
| 32 |  |  |  |  |  |  | } | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 0 |  |  | 0 | 1 |  | sub size   { @{shift->{size}} } | 
|  | 0 |  |  |  |  |  |  | 
| 35 | 0 |  |  | 0 | 1 |  | sub width  { shift->{size}[0] } | 
| 36 | 0 |  |  | 0 | 1 |  | sub height { shift->{size}[1] } | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | sub get { | 
| 39 | 0 |  |  | 0 | 1 |  | my ($self, $pos) = @_; | 
| 40 | 0 |  |  |  |  |  | substr($self->{data}, $self->_pos($pos), 1); | 
| 41 |  |  |  |  |  |  | } | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | sub _rel { | 
| 44 | 0 |  |  | 0 |  |  | my ($self, $rel_pos) = @_; | 
| 45 | 0 |  |  |  |  |  | $rel_pos->[0] += $self->{pos}[0]; | 
| 46 | 0 |  |  |  |  |  | $rel_pos->[1] += $self->{pos}[1]; | 
| 47 | 0 |  |  |  |  |  | $self->get($rel_pos); | 
| 48 |  |  |  |  |  |  | } | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | sub _replace { | 
| 51 | 0 |  |  | 0 |  |  | my ($self, $pos, $char) = @_; | 
| 52 | 0 |  |  |  |  |  | $pos->[0] += $self->{pos}[0]; | 
| 53 | 0 |  |  |  |  |  | $pos->[1] += $self->{pos}[1]; | 
| 54 | 0 |  |  |  |  |  | substr($self->{data}, $self->_pos($pos), 1, $char); | 
| 55 | 0 |  |  |  |  |  | push @{$self->{replaced}}, $pos; | 
|  | 0 |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | sub _pos { | 
| 59 | 0 |  |  | 0 |  |  | my ($self, $pos) = @_; | 
| 60 | 0 |  |  |  |  |  | my ($x, $y) = @$pos; | 
| 61 | 0 |  |  |  |  |  | my ($w, $h) = @{$self->{size}}; | 
|  | 0 |  |  |  |  |  |  | 
| 62 | 0 | 0 |  |  |  |  | if ($x < 0)   { $x = 0; } | 
|  | 0 |  |  |  |  |  |  | 
| 63 | 0 | 0 |  |  |  |  | if ($y < 0)   { $y = 0; } | 
|  | 0 |  |  |  |  |  |  | 
| 64 | 0 | 0 |  |  |  |  | if ($x >= $w) { $x = $w - 1; } | 
|  | 0 |  |  |  |  |  |  | 
| 65 | 0 | 0 |  |  |  |  | if ($y >= $h) { $y = $h - 1; } | 
|  | 0 |  |  |  |  |  |  | 
| 66 | 0 |  |  |  |  |  | $x + $y * ($w + 1);  # +1 for "\n" | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | sub _dump { | 
| 70 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 71 | 0 |  |  |  |  |  | my $data = $self->{data}; | 
| 72 | 0 |  |  |  |  |  | my $left = $data =~ /([\.\+])/; | 
| 73 | 0 |  |  |  |  |  | my $done = $data =~ /(\*)/; | 
| 74 | 0 |  | 0 |  |  |  | my $dump = join "", | 
|  |  |  | 0 |  |  |  |  | 
| 75 |  |  |  |  |  |  | $data, "\n", | 
| 76 |  |  |  |  |  |  | "pos: (", $self->{pos}[0], ", ", $self->{pos}[1], ")\n", | 
| 77 |  |  |  |  |  |  | "step: ", $self->{step}, "\n", | 
| 78 |  |  |  |  |  |  | "left: ", ($left || 0), "\n", | 
| 79 |  |  |  |  |  |  | "done: ", ($done || 0), "\n"; | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 0 | 0 |  |  |  |  | if (defined wantarray) { | 
| 82 | 0 |  |  |  |  |  | return $dump; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  | else { | 
| 85 | 0 |  |  |  |  |  | print STDERR "\n", $dump; | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | sub _move { | 
| 90 | 0 |  |  | 0 |  |  | my ($self, $delta, $direction) = @_; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 0 |  |  |  |  |  | my ($x, $y) = @{$self->{pos}}; | 
|  | 0 |  |  |  |  |  |  | 
| 93 | 0 |  |  |  |  |  | my ($dx, $dy) = @$delta; | 
| 94 | 0 |  |  |  |  |  | my $me   = $self->_rel([0, 0]); | 
| 95 | 0 |  |  |  |  |  | my $dest = $self->_rel([$dx, $dy]); | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 0 |  |  |  |  |  | $self->{direction} = $direction; | 
| 98 | 0 |  |  |  |  |  | @{$self->{replaced}} = (); | 
|  | 0 |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 0 |  |  |  |  |  | my $moved; | 
| 101 | 0 | 0 | 0 |  |  |  | if ($dest eq ' ' or $dest eq '.') { | 
|  |  | 0 | 0 |  |  |  |  | 
| 102 | 0 | 0 |  |  |  |  | $self->_replace([0, 0] => ($me eq '@' ? ' ' : '.')); | 
| 103 | 0 | 0 |  |  |  |  | $self->_replace([$dx, $dy] => ($dest eq ' ' ? '@' : '+')); | 
| 104 | 0 |  |  |  |  |  | $moved = 1; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  | elsif ($dest eq '$' or $dest eq '*') { | 
| 107 | 0 |  |  |  |  |  | my $next = $self->_rel([$dx * 2, $dy * 2]); | 
| 108 | 0 | 0 | 0 |  |  |  | if ($next eq ' ' or $next eq '.') { | 
| 109 | 0 | 0 |  |  |  |  | $self->_replace([0, 0] => ($me eq '@' ? ' ' : '.')); | 
| 110 | 0 | 0 |  |  |  |  | $self->_replace([$dx, $dy] => ($dest eq '$' ? '@' : '+')); | 
| 111 | 0 | 0 |  |  |  |  | $self->_replace([$dx * 2, $dy * 2] => ($next eq ' ' ? '$' : '*')); | 
| 112 | 0 |  |  |  |  |  | $moved = 1; | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  | else { | 
| 115 | 0 |  |  |  |  |  | $self->_debug("blocked"); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | else { | 
| 119 | 0 |  |  |  |  |  | $self->_debug("wall"); | 
| 120 |  |  |  |  |  |  | } | 
| 121 | 0 | 0 |  |  |  |  | if ($moved) { | 
| 122 | 0 |  |  |  |  |  | $self->{step}++; | 
| 123 | 0 |  |  |  |  |  | $self->{pos} = [$x + $dx, $y + $dy]; | 
| 124 | 0 |  |  |  |  |  | return @{$self->{replaced}}; | 
|  | 0 |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | } | 
| 126 | 0 |  |  |  |  |  | return; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 0 |  |  | 0 | 1 |  | sub go_right { shift->_move([1, 0], 'right') } | 
| 130 | 0 |  |  | 0 | 1 |  | sub go_left  { shift->_move([-1, 0], 'left') } | 
| 131 | 0 |  |  | 0 | 1 |  | sub go_up    { shift->_move([0, -1], 'up') } | 
| 132 | 0 |  |  | 0 | 1 |  | sub go_down  { shift->_move([0, 1], 'down') } | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 0 | 0 |  | 0 | 1 |  | sub direction { shift->{direction} || 'left' } | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | sub solved { | 
| 137 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 138 | 0 |  |  |  |  |  | my $left = $self->{data} =~ /([\.\+\$])/; | 
| 139 | 0 |  |  |  |  |  | !$left; | 
| 140 |  |  |  |  |  |  | } | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | sub _debug { | 
| 143 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 144 | 0 | 0 |  |  |  |  | print STDERR @_, "\n" if $self->{debug}; | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | 1; | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | __END__ |