diff options
Diffstat (limited to 'lib/GridFiller.pm')
-rw-r--r-- | lib/GridFiller.pm | 313 |
1 files changed, 15 insertions, 298 deletions
diff --git a/lib/GridFiller.pm b/lib/GridFiller.pm index 38bf7f6..44f0a50 100644 --- a/lib/GridFiller.pm +++ b/lib/GridFiller.pm @@ -1,25 +1,15 @@ package GridFiller; use Moose; use namespace::autoclean; -use List::Util qw(shuffle); -use List::MoreUtils qw(uniq); -use MooseX::Types -declare => [qw( GridT Char LetterCell )]; -use MooseX::Types::Moose qw(Str ArrayRef Bool Int); -use MooseX::Types::Structured qw(Tuple); +use GridFiller::Types qw(GridT WordListT); +use GridFiller::Status; +use GridFiller::Result; use Carp; with 'MooseX::Log::Log4perl'; -my $VERTICAL = 1; -my $HORIZONTAL = 2; - -my $BLACK = 1; -my $WHITE = 2; - -subtype GridT, as ArrayRef[ArrayRef[Str]]; - has words => ( - isa => ArrayRef[Str], + isa => WordListT, required => 1, is => 'ro', ); @@ -30,308 +20,35 @@ has grid => ( is => 'ro', ); -has _words_to_use => ( - isa => ArrayRef[Str], - traits => ['Array'], - handles => { - _has_next_word => 'count', - _get_next_word => 'shift', - }, - is => 'rw', - lazy_build => 1, - clearer => '_reset_words_to_use', -); - -sub _build__words_to_use { - # clone initial word list - return [ shuffle uniq @{shift->words} ]; -} - -has leftover_words => ( - isa => ArrayRef[Str], - is => 'ro', - traits => ['Array'], - handles => { - _mark_leftover => 'push', - }, - clearer => '_reset_leftovers', -); - -has _grid_status => ( - isa => ArrayRef[ArrayRef[Int]], - is => 'rw', - lazy_build => 1, - clearer => '_reset_grid_status', -); - -sub _build__grid_status { - return [ - map { - [ - map { - $_ eq '*' ? $BLACK : $WHITE - } @$_ - ] - } @{shift->grid} - ]; -} - -subtype Char, - as Str, - where { length($_) == 1 }; - -subtype LetterCell, - as Tuple[Int,Char]; - -has result => ( - isa => ArrayRef[ArrayRef[LetterCell]], - is => 'ro', - lazy_build => 1, - clearer => '_reset_result', -); - -sub _reset { - my ($self) = @_; - - $self->_reset_words_to_use(); - $self->_reset_grid_status(); - $self->_reset_leftovers(); - $self->_reset_result(); -} - sub fill { my ($self) = @_; - $self->_reset(); + my $status = GridFiller::Status->new({grid => $self->grid, words => $self->words}); + my $result = GridFiller::Result->new({source_grid => $self->grid}); - while ($self->_unfilled() && $self->_has_next_word()) { - my $word = $self->_get_next_word(); + while ($status->unfilled() && $status->has_next_word()) { + my $word = $status->get_next_word(); $self->log->debug("Placing $word"); - my ($x,$y,$dir) = $self->_find_place_for($word); + my ($x,$y,$dir) = $status->find_place_for($word); if (! defined $x) { $self->log->debug("No place for $word"); - $self->_mark_leftover($word); + $result->mark_leftover($word); next; }; - $self->_place_word_at($word,$x,$y,$dir); + $result->place_word_at($word,$x,$y,$dir); + $status->place_word_at($word,$x,$y,$dir); if ($self->log->is_debug) { - $self->_log_status; - } - } -} - -sub _place_word_at { - my ($self, $word, $x, $y, $dir) = @_; - - my $colour = $self->_find_colour_for($x,$y,$dir); - - $self->log->debug("Placing $word at ${x}:${y} ($dir) in colour $colour"); - - if ($dir == $HORIZONTAL) { - for my $i (0..length($word)-1) { - $self->_mark_occupied($x+$i,$y); - $self->_put_letter_at(substr($word,$i,1), - $x+$i,$y, - $colour); - } - } - elsif ($dir == $VERTICAL) { - for my $i (0..length($word)-1) { - $self->_mark_occupied($x,$y+$i); - $self->_put_letter_at(substr($word,$i,1), - $x,$y+$i, - $colour); + $self->log->debug($status->to_string); + $self->log->debug($result->to_string); } } - else { - croak "What dir $dir?"; - } -} - -sub _mark_occupied { - my ($self,$x,$y) = @_; - - $self->_grid_status->[$y][$x]=0; - return; -} - -sub _unfilled { - my ($self) = @_; - - for my $row (@{$self->_grid_status}) { - for my $cell (@$row) { - return 1 if $cell != 0; - } - } - return 0; -} - -sub _put_letter_at { - my ($self,$letter,$x,$y,$colour) = @_; - - $self->result()->[$y][$x]=[$colour,$letter]; - return; -} - -{ -my %colourmap = ( - '*' => { - $HORIZONTAL => { - 0 => 1, - 1 => 2, - }, - $VERTICAL => { - 0 => 3, - 1 => 4, - }, - }, - ' ' => { - $HORIZONTAL => { - 0 => 5, - 1 => 6, - }, - $VERTICAL => { - 0 => 7, - 1 => 8, - }, - }, -); - -sub _build_result { - return [ - map { - [ - map { - [ $colourmap{$_}->{$HORIZONTAL}->{0}, ' ' ], - } @$_ - ] - } @{shift->grid} - ]; -} - -sub _find_colour_for { - my ($self,$x,$y,$dir) = @_; - - my $parity = ($dir == $HORIZONTAL) ? ($y % 2) : ($x % 2); - - my $chosen = $colourmap{$self->grid->[$y][$x]}->{$dir}->{$parity}; - - # avoid colour runs - if ( ( $x>0 && $dir == $HORIZONTAL && $self->result->[$y][$x-1][0] == $chosen ) - or - ( $y>0 && $dir == $VERTICAL && $self->result->[$y-1][$x][0] == $chosen ) - ) { - $chosen = $colourmap{$self->grid->[$y][$x]}->{$dir}->{1-$parity}; - } - return $chosen; -} -} - -sub _find_place_for { - my ($self,$word) = @_; - - my $dir = int(rand(2)) ? $HORIZONTAL : $VERTICAL; - - my $length = length $word; - - my @ret; - - if ($dir == $HORIZONTAL) { - @ret = $self->_find_place_horiz($length); - @ret = $self->_find_place_vert($length) unless @ret; - } - else { - @ret = $self->_find_place_vert($length); - @ret = $self->_find_place_horiz($length) unless @ret; - } - - return @ret; -} - -sub _find_place_horiz { - my ($self,$length) = @_; - - my $rows = scalar @{$self->grid}; - my $col; - - for my $row (0..$rows-1) { - $col = $self->_find_in_row($row,$length); - return ($col,$row,$HORIZONTAL) if defined $col; - } - return; -} - -sub _find_place_vert { - my ($self,$length) = @_; - - my $cols = scalar @{$self->grid->[0]}; - my $row; - - for my $col (0..$cols-1) { - $row = $self->_find_in_col($col,$length); - return ($col,$row,$VERTICAL) if defined $row; - } - return; -} - -{ -my @symbols=(' ','X','O'); - -sub _do_find { - my ($self,$str,$length) = @_; - - my ($skip) = ($str =~ m{^ (.*?) (?: X{$length} | O{$length} ) }x); - - $self->log->debug(defined $skip ? " skip <$skip>" : " nope"); - - return length($skip) if defined $skip; - return; -} - -sub _find_in_row { - my ($self,$row,$length) = @_; - - my $str = join '',map { $symbols[$_] } @{$self->_grid_status->[$row]}; - - $self->log->debug("row $row = $str"); - - return $self->_do_find($str,$length); -} - -sub _find_in_col { - my ($self,$col,$length) = @_; - my $str = join '',map { $symbols[$_->[$col]] } @{$self->_grid_status}; - - $self->log->debug("col $col = $str"); - - return $self->_do_find($str,$length); -} - -sub _log_status { - my ($self) = @_; - - my $str = "\n"; - - my $rows = scalar @{$self->grid}; - - for my $row (0..$rows-1) { - for my $cell (@{$self->_grid_status->[$row]}) { - $str .= $symbols[$cell]; - } - $str .= ' '; - for my $cell (@{$self->result->[$row]}) { - $str .= $cell->[1] eq ' ' ? '.' : $cell->[1]; - } - $str .= "\n"; - } - $self->log->debug($str); -} + return $result; } 1; |