Algorithm-X-DLX

 view release on metacpan or  search on metacpan

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

    for (my $y = 0; $y < $type->n(); ++$y) {
        $result .= $line;
    }
    
    my $self = { type => $type, template => $result, labels => choose_labels($result) };
    weaken($self->{type});

    return bless $self, $class;
}

sub oneline {
    my ($class, $type) = @_;

    my $result = '.' x $type->size() . "\n";
    my $self = { type => $type, template => $result, labels => choose_labels($result) };
    weaken($self->{type});

    return bless $self, $class;
}

sub with_labels {
    my ($self, $str) = @_;
    my @labels = choose_labels_n($str, scalar split //, $self->{type}->n());
    
    my %new_self = %{$self};
    @new_self{qw(labels)} = @labels; 
   
    return bless \%new_self, ref($self);
}

sub count_cells {
    my $str = shift() or return 0;
    my $count = 0;

    foreach my $c (split //, $str) {
        if (is_valid_label($c) || is_empty($c)) {
            ++$count;
        }
    }
    
    return $count;
}

sub is_cell {
    my ($c, $self) = @_;

    return is_empty($c) || index($self->{labels}, $c) != -1;
}

sub value {
    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};
}

sub get_values {
    my ($str, $self) = @_;
    
    my @labels = choose_labels($str);
    my @values;

    foreach my $c (split //, $str) {
        my $pos = index(join('', @labels), $c);
        
        if ($pos != -1) {
            push @values, ($pos + 1);
        } elsif (is_empty($c)) {
            push @values, 0;
        }
    }
    
    return \@values;
}

sub to_string {
    my ($self, @values) = @_;

    my $value_size = scalar(@values);
    my $type_size = $self->type->size();
    croak "to_string(): wrong number of values ($value_size vs. $type_size)" if $value_size != $type_size;
    
    my @result_chars = split //, $self->{template};
    
    my $j = 0;
    for (my $i=0; $i < @result_chars; ++$i) {
        if (is_cell($result_chars[$i], $self)) {
            croak "Logic error" if (@values <= $j);
            $result_chars[$i] = label($values[$j], $self);
            $j++;
        }
    }        

    croak "Logic error" if (@values != $j);
        
    return join('', @result_chars);
}

sub type {
   return shift->{type};
}

sub is_empty {
   my $c = shift;
   return $c eq '.' || $c eq '0';
}

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


    if (scalar(keys %used) < $n) {
        croak "Sudoku too large, not enough labels";
    }

    return join('', sort keys %used);
}

sub default_template {
    my ($type) = @_;
    
    # Step 1: empty
    my @lines;
    my $n = $type->n();
    my $header = '+' . ('-' x ($n * 2 - 1)) . '+';
    my $empty = '|' . (' ' x ($n * 2 - 1)) . '|';

    push @lines, $header;
    push @lines, ($empty) x (2 * $n - 1);
    push @lines, $header;

    for my $y (0 .. $n - 1) {
        for my $x (0 .. $n - 1) {
            substr($lines[2 * $y + 1], 2 * $x + 1, 1) = '.';
        }
    }

    # Step 2: add vertical lines
    my $set = sub {
        my ($x, $y, $c) = @_;
        if (substr($lines[$y], $x, 1) eq ' ') {
            substr($lines[$y], $x, 1) = $c;
        } elsif (substr($lines[$y], $x, 1) ne $c) {
            substr($lines[$y], $x, 1) = '+';
        }
    };

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

    # Step 3: add horizontal lines
    for my $y (0 .. $n - 2) {
        for my $x (0 .. $n - 1) {
            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;
            }
        }
    }

    my $result = '';
    
    for my $y (0 .. $#lines) {
        next unless $keep_row[$y];
        
        for my $x (0 .. $#keep_col) {
            if ($keep_col[$x]) {
                # Append character to result
                $result .= substr($lines[$y], $x, 1);
            }
        }
        $result .= "\n";
    }

    return $result;
}

1;



( run in 0.755 second using v1.01-cache-2.11-cpan-39bf76dae61 )