Algorithm-X-DLX

 view release on metacpan or  search on metacpan

examples/sudoku/Sudoku.pm  view on Meta::CPAN


  my $self = {};
  my $string = '';
  
  foreach my $arg (@_) {
    if (ref($arg) eq 'SudokuType') {
      $self->{type_} = $arg;
    } elsif (ref($arg) eq 'ARRAY') {
      $self->{values_} = [@$arg];
    } elsif (defined $arg && !ref($arg)) {
      croak "Got empty string" unless length $arg;
      $string = $arg;
    } else {
      die "Unknown blessed parameter.\n";
    }
  }

  if (! $self->{type_} && $string ) {
   $self->{type_} = SudokuType::guess($string);
  }

examples/sudoku/SudokuFormat.pm  view on Meta::CPAN

    my ($c, $self) = @_;
    
    my $pos = index($self->{labels}, $c);
    
    return ($pos == -1) ? 0 : ($pos + 1);
}

sub label {
    my ($i, $self) = @_;

    croak "Index out of bounds" if ($i > length($self->{labels}));
    
    return ($i == 0) ? '.' : substr($self->{labels}, $i - 1, 1);
}

sub labels {
    my ($self) = @_;
    
    return $self->{labels};
}

examples/sudoku/SudokuFormat.pm  view on Meta::CPAN

            if ($type->region_xy($x, $y) != $type->region_xy($x, $y + 1)) {
                for my $dx (0 .. 2) {
                    $set->(2 * $x + $dx, 2 * $y + 2, '-');
                }
            }
        }
    }

    # Step 4: collapse uninteresting rows and columns
    my @keep_row = (0) x @lines;
    my @keep_col = (0) x length($lines[0]);

    for my $y (0 .. $#lines) {
        for my $x (0 .. length($lines[0]) - 1) {
            my $c = substr($lines[$y], $x, 1);
            if ($c ne ' ' && $c ne '|') {
                $keep_row[$y] = 1;
            }
            if ($c ne ' ' && $c ne '-') {
                $keep_col[$x] = 1;
            }
        }
    }

examples/sudoku/SudokuType.pm  view on Meta::CPAN

    $line .= $c;
  }
  push @lines, $line;
  my @region;
  my $next_id = 1;

  my $find_region;
  $find_region = sub {
    my ($id, $x, $y) = @_;

    return 0 if $x < 0 || $y < 0 || $y >= scalar(@lines) || $x >= length($lines[$y]);

    $region[$y][$x] = 0 unless defined $region[$y][$x];
    return 0 if $region[$y][$x] != 0;

    my $c = substr($lines[$y], $x, 1);
    return 0 if !SudokuFormat::is_valid_cell($c) && $c ne ' ';

    $region[$y][$x] = $id;
    my $region_size = 1;
    $region_size += $find_region->($id, $x, $y - 1);
    $region_size += $find_region->($id, $x, $y + 1);
    $region_size += $find_region->($id, $x - 1, $y);
    $region_size += $find_region->($id, $x + 1, $y);
    return $region_size;
  };

  for my $y (0 .. $#lines) {
    for my $x (0 .. length($lines[$y]) - 1) {
      if (!SudokuFormat::is_valid_cell(substr($lines[$y], $x, 1))) {
        next;
      }
      my $region_size = $find_region->($next_id, $x, $y);
      $next_id++ if $region_size > 0;
    }
  }

  my %region_size;
  my @final_regions;
  my $total_size = 0;

  for my $y (0 .. $#lines) {
    for my $x (0 .. length($lines[$y]) - 1) {
      if (SudokuFormat::is_valid_cell(substr($lines[$y], $x, 1))) {
        $total_size++;
        $region_size{$region[$y][$x]}++;
        push @final_regions, $region[$y][$x];
      }
    }
  }
#print "\%region_size = ", Dumper(\%region_size);
#print "guess(): \$total_size = $total_size, \$size = $size\n";
  croak "Total size mismatch" if $total_size != $size;

examples/sudoku/SudokuType.pm  view on Meta::CPAN

sub is_equal {
  my ($self, $other) = @_;
  return $self->{n_} == $other->{n_} && $self->{region_} eq $other->{region_};
}

sub box_regions {
  my ($w, $h) = @_;
  my @regions;
  my $n = $w * $h;

  # Creates an array of length size(), holding zero based region indexes (0-8) for the standard 3x3 box regions.
  for my $y (0 .. $n - 1) {
    for my $x (0 .. $n - 1) {
        push @regions, floor($y / $h) * $h + floor($x / $w);
    }
  }
  return \@regions;
}

# This function renumerates the given region values in ascending order, beginning with 0.
sub normalize_regions {

examples/sudoku/sudoku.pl  view on Meta::CPAN

  $input = '';
}

sub print_side_by_side {
  my ($left, $right) = @_;

  my @ls = split("\n", $left);
  my @rs = split("\n", $right);
  my $max_left = 0;
  foreach my $l (@ls) {
    $max_left = length($l) if length($l) > $max_left;
  }

  my $max_lines = scalar(@ls) > scalar(@rs) ? scalar(@ls) : scalar(@rs);
  for (my $y = 0; $y < $max_lines; $y++) {
    my $pos = 0;
    if ($y < scalar(@ls)) {
      print $ls[$y];
      $pos = length($ls[$y]);
    }
    if ($y < scalar(@rs)) {
      print ' ' x (4 + $max_left - $pos);
      print $rs[$y];
    }
    print "\n";
  }
}

lib/Algorithm/X/ExactCoverProblem.pm  view on Meta::CPAN

  
  if (!@$bit_rows_ref) {
    return $class->new(0, undef, $secondary_columns);
  }

  my $width = scalar @{$bit_rows_ref->[0]};
  my $problem = $class->new($width, undef, $secondary_columns);

  foreach my $bits (@$bit_rows_ref) {
    if (scalar @$bits != $width) {
      croak("rows have different lengths");
    }
    
    my @row;
    for (my $i = 0; $i < @$bits; ++$i) {
      if ($bits->[$i] != 0 && $bits->[$i] != 1) {
        croak("dense matrix must contain only 0s and 1s");
      }
      push @row, $i if $bits->[$i];
    }
    $problem->add_row(\@row);

t/02-exactcoverproblem.t  view on Meta::CPAN

  throws_ok { Algorithm::X::ExactCoverProblem->new(5, [[5]]) }    qr/column out of range/i, 'size mismatch';
  throws_ok { Algorithm::X::ExactCoverProblem->new(1, [[0, 0]]) } qr/duplicate columns/i, 'duplicate columns';

  lives_ok { Algorithm::X::ExactCoverProblem->new(1, [[0]]) } 'size matches';
  lives_ok { Algorithm::X::ExactCoverProblem->new(2, [[1]]) } 'size matches';
  lives_ok { Algorithm::X::ExactCoverProblem->new(6, [[5]]) } 'size matches';
};

subtest 'dense matrix' => sub {
  plan tests => 9;
  throws_ok { Algorithm::X::ExactCoverProblem->dense([[0], []]) }    qr/rows have different lengths/i, 'row size mismatch';
  throws_ok { Algorithm::X::ExactCoverProblem->dense([[2]]) }    qr/dense matrix must contain only 0s and 1s/i, 'non boolean content';
  throws_ok { Algorithm::X::ExactCoverProblem->dense([[0], 2]) }    qr/Can't use string \("2"\) as an ARRAY ref/i, 'corrupted matrix';

  lives_ok { Algorithm::X::ExactCoverProblem->dense([]) } 'size matches';
  lives_ok { Algorithm::X::ExactCoverProblem->dense([[], []]) } 'size matches';
  lives_ok { Algorithm::X::ExactCoverProblem->dense([[0], [1]]) } 'size matches';
  lives_ok { Algorithm::X::ExactCoverProblem->dense([[0]], 1) } 'size matches';

  is 0, Algorithm::X::ExactCoverProblem->dense([[]])->width(), 'empty matrix width';
  is 2, Algorithm::X::ExactCoverProblem->dense([[0, 0]])->width(), 'column count';



( run in 0.320 second using v1.01-cache-2.11-cpan-65fba6d93b7 )