# Copyright (c) 2002 Douglas Sparling. All rights reserved. This program is free
# software; you can redistribute it and/or modify it under the same terms
# as Perl itself.

package Convert::AcrossLite;

use strict;
use Carp;
use vars qw($VERSION);

$VERSION = '0.04';

sub new {
    my $class = shift;
    my %conf = @_;

    my $self = {};
    $self->{in_file} = $conf{in_file} || 'Default.puz';
    $self->{is_parsed} = 0;

    bless($self, $class);
    return $self;
}

sub in_file {
    my($self) = shift;
    if(@_) { $self->{in_file} = shift }
    return $self->{in_file};
}

sub out_file {
    my($self) = shift;
    if(@_) { $self->{out_file} = shift }
    return $self->{out_file};
}

sub puz2text {
    my($self) = shift;
    my $text;

    # Parse puz file
    _parse_file($self) unless $self->{is_parsed};

    # Format across clues
    my @aclues = split("\n", $self->{aclues});
    foreach my $aclue(@aclues) {
        $aclue =~ s/\d+\s+-\s+//;
        $aclue = "\t$aclue";
    }
    $self->{aclues} = join("\n",@aclues);

    # Format down clues
    my @dclues = split("\n", $self->{dclues});
    foreach my $dclue(@dclues) {
        $dclue =~ s/\d+\s+-\s+//;
        $dclue = "\t$dclue";
    }
    $self->{dclues} = join("\n",@dclues);

    $text = "<ACROSS PUZZLE>\n"; 
    $text .= "<TITLE>\n";
    $text .= "\t$self->{title}\n";
    $text .= "<AUTHOR>\n";
    $text .= "\t$self->{author}\n";
    $text .= "<COPYRIGHT>\n";
    $text .= "\t$self->{copyright}\n";
    $text .= "<SIZE>\n";
    $text .= "\t$self->{rows}x$self->{columns}\n";
    $text .= "<GRID>\n";
    my $solref = $self->{solution};
    my @sol = @$solref;
    foreach my $sol (@sol) {
        $text .= "\t$sol\n";
    }
    $text .= "<ACROSS>\n";
    $text .= "$self->{aclues}\n";
    $text .= "<DOWN>\n";
    $text .= "$self->{dclues}\n";

    if( defined $self->out_file ) {
        my $PUZ_OUT = $self->out_file;

        open FH, ">$PUZ_OUT" or croak "Can't open $PUZ_OUT: $!";
        print FH $text;
        close FH;
    } else {
        return $text;
    }
}

sub get_across_down {
    my($self) = shift;
    my $across_hashref = get_across($self);
    my $down_hashref = get_down($self);

    return($across_hashref, $down_hashref);
}

sub get_across {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parse};

    ###################################################
    # _parse_file will set direction, number and clue #
    # as well as a two-dimension array for solution   # 
    ###################################################
    my $sol_two_ref = $self->{solution_two};
    my @sol_two = @$sol_two_ref;

    ### Get row, column, solution, length ###

    ###################################################
    # We're setting found squares to 1....
    # We need to set them to row and col so we can
    # do a "lookup" later. That is, given a row
    # and col number, we need to know what the clue
    # number is. Or maybe we need to make a key
    # that is row/col so we can look up clue num.
    ###################################################

    ######################################################
    # Determine which squares start with either 
    # an across word (%across_start_squares) 
    # or a down word (%down_start_squares). 
    ######################################################
    # Across
    my $start = 1;
    my $square_num = 0;
    my %across_start_squares;
    for(my $row = 0; $row < $self->{rows}; $row++) {
        $start = 1;
        for(my $col = 0; $col < $self->{columns}; $col++) {
            if($sol_two[$row][$col] ne '.') {
                if($start) {
                    $across_start_squares{$square_num}++;
                    $start = 0;
                    $square_num++;
                } else {
                    $square_num++
                }
            } else {
                $square_num++;
                $start = 1;
            }            
        }
    }
    # Down
    my %down_start_squares;
    $start = 1;
    $square_num = 0;
    for(my $col = 0; $col < $self->{columns}; $col++) {
        $start = 1;
        for(my $row = 0; $row < $self->{rows}; $row++) {
            if($sol_two[$row][$col] ne '.') {
                if($start) {
                    $down_start_squares{$square_num}++;
                    $start = 0;
                    if( $row >= $self->{rows}-1 ) {
                        $square_num = $col+1;
                    } else {
                        $square_num += $self->{columns};
                    }
                } else {
                    if( $row >= $self->{rows}-1 ) {
                        $square_num = $col+1;
                    } else {
                        $square_num += $self->{columns};
                    }
                }
            } else {
                if( $row >= $self->{rows}-1 ) {
                    $square_num = $col+1;
                } else {
                    $square_num += $self->{columns};
                }
                $start = 1;
            }            
        }
    }

    ##########################################################
    # Go back through grid from square 0 to square 
    # (rows x cols - 1) and set the clue number on each 
    # sqaure that is found in the across_start_squares and
    # down_start_square hashes from above.
    #
    # We create two versions....
    # 1) Row/Col - $clue_numbers[$row][$col] = $clue_number
    #    [0,0] to [14,14] for 15x15 ([0,0] to [rows-1,cols-1])
    # 2) Square Num  - $clue_numbers{$square} = $clue_number
    #    Square 0 to 224 for 15x15 (0 to row*cols-1)
    ###########################################################
    # Across
    my $counter = 0;
    my $clue_num = 1;
    my @clue_numbers;
    my %clue_numbers;
    for(my $row = 0; $row < $self->{rows}; $row++) {
        for(my $col = 0; $col < $self->{columns}; $col++) {
            if( $across_start_squares{$counter} || $down_start_squares{$counter} ) {
                # Hash - Square number
                $clue_numbers{$counter} = $clue_num;
                # Array - row/col
                $clue_numbers[$row][$col] = $clue_num;
                $clue_num++;
            }
            $counter++;
        }
    }

    # Now get data and set hash of hashes
    # Across
    my $start_row = 0;         # Square number - row
    my $start_col = 0;         # Square number - col
    my $start_square = 0; 
    my $length = 0;            # Length of current solution word
    my $count = 0;             # Clue number
    my $square = 0;            # Grid square
    my $sol_word = '';         # Solution word
    my $last_square = '';      # Contents of last square visited
    my $last_square_two = '';  # Contents of two squares ago
    my $last_col = 0;          # 0 (isn't) or 1 (is) last col ($self->{columns}-1)
    for(my $row = 0; $row < $self->{rows}; $row++) {
        for(my $col = 0; $col < $self->{columns}; $col++) {
            if ( $sol_two[$row][$col] eq '.' || $col >= $self->{columns} - 1 ) {
                # Save data

                # Set vars if current square eq '.' and is in last col in row
                if( $sol_two[$row][$col] eq '.' && $last_col ) {
                    $square++;
                    $last_square_two = $last_square;
                    $last_square = '.';
                    $last_col = 0;
                    next;
                }

                # Set vars if current square eq '.', last_square eq '.'
                # and we're not in last col of row
                if( $sol_two[$row][$col] eq '.' && !$last_col && $last_square eq '.' ) { 
                    if( $col == $self->{columns} - 1 ) {
                        $start_row++;
                        $start_col = 0;
                        $last_col = 1;
                    } else {
                        $start_col++;
                        $last_col = 0;
                    } 
                    $square++;
                    $last_square_two = $last_square;
                    $last_square = '.';
                    next;
                }

                # Get last square of row
                if( $col == $self->{columns} - 1 ) {
                    $sol_word .= $sol_two[$row][$col];
                    $length++;
                    $last_col = 1;
                } else { 
                    $last_col = 0;
                }

                # Get key and clue num
                my $clue_num = $clue_numbers[$start_row][$start_col];
                next unless defined $clue_num; 
                my $key = $clue_num;

                # Store info into puzzle hash
                $self->{across}{$key}{length} = $length;
                $self->{across}{$key}{solution} = $sol_word;
                $self->{across}{$key}{row} = $start_row+1;
                $self->{across}{$key}{column} = $start_col+1;
                $self->{across}{$key}{clue_number} = $clue_num;

                # Reset variables
                $length = 0;
                $sol_word = '';
                if( $col >= $self->{columns} - 1 ) {
                    $start_row++;
                    $start_col = 0;
                } else {
                    $start_col = $col + 1;
                }
            } else {
                if( $last_square eq '.' && $last_square_two eq '.' && $col != 0 ) {
                    $start_col++;
                }
                $sol_word .= $sol_two[$row][$col];
                $length++;
                $last_col = 0;
            }
            $square++;
            $last_square_two = $last_square;
            $last_square = $sol_two[$row][$col];
        }
    }

    # Return across hash 
    return ($self->{across});
}


sub get_down {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parse};

    ###################################################
    # _parse_file will set direction, number and clue #
    # as well as a two-dimension array for solution   # 
    ###################################################
    my $sol_two_ref = $self->{solution_two};
    my @sol_two = @$sol_two_ref;

    ### Get row, column, solution, length ###

    ###################################################
    # We're setting found squares to 1....
    # We need to set them to row and col so we can
    # do a "lookup" later. That is, given a row
    # and col number, we need to know what the clue
    # number is. Or maybe we need to make a key
    # that is row/col so we can look up clue num.
    ###################################################

    ######################################################
    # Determine which squares start with either 
    # an across word (%across_start_squares) 
    # or a down word (%down_start_squares). 
    ######################################################
    # Across
    my $start = 1;
    my $square_num = 0;
    my %across_start_squares;
    for(my $row = 0; $row < $self->{rows}; $row++) {
        $start = 1;
        for(my $col = 0; $col < $self->{columns}; $col++) {
            if($sol_two[$row][$col] ne '.') {
                if($start) {
                    $across_start_squares{$square_num}++;
                    $start = 0;
                    $square_num++;
                } else {
                    $square_num++
                }
            } else {
                $square_num++;
                $start = 1;
            }            
        }
    }
    # Down
    my %down_start_squares;
    $start = 1;
    $square_num = 0;
    for(my $col = 0; $col < $self->{columns}; $col++) {
        $start = 1;
        for(my $row = 0; $row < $self->{rows}; $row++) {
            if($sol_two[$row][$col] ne '.') {
                if($start) {
                    $down_start_squares{$square_num}++;
                    $start = 0;
                    if( $row >= $self->{rows}-1 ) {
                        $square_num = $col+1;
                    } else {
                        $square_num += $self->{columns};
                    }
                } else {
                    if( $row >= $self->{rows}-1 ) {
                        $square_num = $col+1;
                    } else {
                        $square_num += $self->{columns};
                    }
                }
            } else {
                if( $row >= $self->{rows}-1 ) {
                    $square_num = $col+1;
                } else {
                    $square_num += $self->{columns};
                }
                $start = 1;
            }            
        }
    }


    ##########################################################
    # Go back through grid from square 0 to square 
    # (rows x cols - 1) and set the clue number on each 
    # sqaure that is found in the across_start_squares and
    # down_start_square hashes from above.
    #
    # We create two versions....
    # 1) Row/Col - $clue_numbers[$row][$col] = $clue_number
    #    [0,0] to [14,14] for 15x15 ([0,0] to [rows-1,cols-1])
    # 2) Square Num  - $clue_numbers{$square} = $clue_number
    #    Square 0 to 224 for 15x15 (0 to row*cols-1)
    ###########################################################
    # Across
    my $counter = 0;
    my $clue_num = 1;
    my @clue_numbers;
    my %clue_numbers;
    for(my $row = 0; $row < $self->{rows}; $row++) {
        for(my $col = 0; $col < $self->{columns}; $col++) {
            if( $across_start_squares{$counter} || $down_start_squares{$counter} ) {
                # Hash - Square number
                $clue_numbers{$counter} = $clue_num;
                # Array - row/col
                $clue_numbers[$row][$col] = $clue_num;
                $clue_num++;
            }
            $counter++;
        }
    }

    # Now get data and set hash of hashes
    # Down
    my $start_row = 0;        # Square number - row
    my $start_col = 0;        # Square number - col
    my $start_square = 0;
    my $length = 0;           # Length of current solution word
    my $count = 0;            # Clue number
    my $square = 0;           # Grid square
    my $sol_word = '';        # Solution word
    my $last_square = '';     # Contents of last square visted
    my $last_square_two = ''; # Contents of two squares back
    my $last_row = 0;      # 0 (isn't) or 1 (is) last row ($self->{rows}-1)
    for(my $col = 0; $col < $self->{columns}; $col++) {
        for(my $row = 0; $row < $self->{rows}; $row++) {
            if ( $sol_two[$row][$col] eq '.' || $row >= $self->{rows} - 1 ) {
                # Save data

                # Set vars if current square eq '.' and is in last row in col 
                if( $sol_two[$row][$col] eq '.' && $last_row ) {
                    $square++;
                    $last_square_two = $last_square;
                    $last_square = '.';
                    $last_row = 0;
                    next;
                }

                # Set vars if current square eq '.', last_square eq '.'
                # and we're not in last row of col 
                if( $sol_two[$row][$col] eq '.' && !$last_row && $last_square eq '.' ) { 
                    if( $row == $self->{rows} - 1 ) {
                        $start_col++;
                        $start_row = 0;
                        $last_row = 1;
                    } else {
                        $start_row++;
                        $last_row = 0;
                    } 
                    $square++;
                    $last_square_two = $last_square;
                    $last_square = '.';
                    next;
                }

                # Get last square of each column
                if( $row == $self->{rows} - 1 ) {
                    $sol_word .= $sol_two[$row][$col];
                    $length++;
                    $last_row = 1;
                } else {
                    $last_row = 0;
                }

                # Get key and clue nem
                my $clue_num = $clue_numbers[$start_row][$start_col];
                next unless defined $clue_num;
                my $key = $clue_num;

                # Store info into puzzle hash
                $self->{down}{$key}{length} = $length;
                $self->{down}{$key}{solution} = $sol_word;
                $self->{down}{$key}{row} = $start_row+1;
                $self->{down}{$key}{column} = $start_col+1;
                $self->{down}{$key}{clue_number} = $clue_num;

                # Reset variables
                $length = 0;
                $sol_word = '';
                if($row >= $self->{rows} - 1 ) {
                    $start_col++;
                    $start_row = 0;
                } else {
                    $start_row = $row + 1;
                }
            } else {
                if( $last_square eq '.' && $last_square_two eq '.' && $row != 0 ) {
                    $start_row++;
                }
                $sol_word .= $sol_two[$row][$col];
                $length++;
                $last_row = 0;
            }
            $square++;
            $last_square_two = $last_square;
            $last_square = $sol_two[$row][$col];
        }
    }

    # Return down hash 
    return ($self->{down});
}


sub parse_file {
    my($self) = shift;
    _parse_file($self);
}

sub _parse_file {
    my($self) = shift;
    my($buf, $parse_word, $oe);
    my($aclues, $dclues);

    my $PUZ_IN = $self->{in_file};

    open FH, $PUZ_IN or croak "Can't open $PUZ_IN: $!";
    binmode(FH); # Be nice to windoz

    # Skip unneeded data
    seek(FH, 44, 0);

    # Width and Height
    read(FH, $buf, 2);
    my ($width, $height) = unpack "C C", $buf;
    $self->{rows} = $height;
    $self->{columns} = $width;

    # Skip more unneeded data
    read(FH, $buf, 6);

    # Solution
    my @solution;
    my @solution_two; # two-dimensional array
    for(my $j=0; $j<$height; $j++) {
        my $twodim_col = 0;
        read(FH, $solution[$j], $width);
        my @letters = split(//,$solution[$j]);
        foreach my $letter (@letters) {
            $solution_two[$j][$twodim_col] = $letter;
            $twodim_col++;
        }
    }
    $self->{solution} = \@solution;
    $self->{solution_two} = \@solution_two;

    # Diagram
    my @diagram;
    for(my $j=0;$j<$height;$j++) {
        read(FH, $diagram[$j], $width);
    }
    $self->{diagram} = \@diagram;

    # Title
    $oe = 0;
    while(1) {
        read(FH, $buf, 1) or last;
        my ($char) = unpack "C", $buf;
        last if $char == 0;
        $parse_word .= $buf;
    }
    $parse_word =~ s/^\s+//;
    $parse_word =~ s/\s+$//;
    $self->{title} = $parse_word;

    # Author
    $parse_word = '';
    $oe = 0;
    while(1) {
        read(FH, $buf, 1) or last;
        my ($char) = unpack "C", $buf;
        last if $char == 0;
        $parse_word .= $buf;
    }
    $parse_word =~ s/^\s+//;
    $parse_word =~ s/\s+$//;
    $self->{author} = $parse_word;

    # Copyright
    $parse_word = '';
    $oe = 0;
    while(1) {
        read(FH, $buf, 1) or last;
        my ($char) = unpack "C", $buf;
        last if $char == 0;
        $parse_word .= $buf;
    }
    $parse_word =~ s/^\s+//;
    $parse_word =~ s/\s+$//;
    $self->{copyright} = $parse_word;

    # Clues
    my ($apos,$dpos);
    my $ccount = 0;
    my $mcount = 0;

    for (my $j=0;$j<$height;$j++) {
        my $rowtext;
        for(my $k=0;$k<$width;$k++) {
            # Check position for across number
            # Left edge non-black followed by non-black
            my $anum = 0; # across number
            if( ($k == 0 &&
                 substr($diagram[$j],$k,1) eq '-' &&
                 substr($diagram[$j],$k+1,1) eq '-') ||
              # Previous black - nonblack - nonblack
                ( ($k+1)<$width &&
                  ($k-1)>=0 &&
                  substr($diagram[$j],$k,1) eq '-' &&
                  substr($diagram[$j],$k-1,1) eq '.' &&
                  substr($diagram[$j],$k+1,1) eq '-' ) ) {
                      $ccount++;
                      $anum = $ccount;
            }


            # Check position for down number
            my $dnum = 0;
            if( ($j == 0 &&
                 substr($diagram[$j],$k,1) eq '-' &&
                 substr($diagram[$j+1],$k,1) eq '-') ||
              # Black above - nonblack - nonblack below
                ( ($j-1)>=0&&
                  ($j+1)<$height &&
                  substr($diagram[$j],$k,1) eq '-' &&
                  substr($diagram[$j-1],$k,1) eq '.' &&
                  substr($diagram[$j+1],$k,1) eq '-' ) ) {
                      # Don't double number the same space
                      if( $anum == 0 ) {
                          $ccount++;
                      }
                      $dnum = $ccount;
            }

            # Get clues
            # Across
            if( $anum != 0 ) {
                my $tmp;
                $parse_word = '';
                $oe = 0;
                while(1) {
                    read(FH, $buf, 1) or last;
                    my ($char) = unpack "C", $buf;
                    last if $char == 0;
                    $parse_word .= $buf;
                }
                $parse_word =~ s/^\s+//;
                $parse_word =~ s/\s+$//;
                $tmp = $parse_word;
                $aclues .= "$anum - $tmp\n";

                my $key = "$anum";
                $self->{across}{$key}{direction} = 'across';
                $self->{across}{$key}{clue_number} = $anum;
                $self->{across}{$key}{clue} = $tmp;
            }
 
            # Down
            if( $dnum != 0 ) {
                my $tmp;
                $parse_word = '';
                $oe = 0;
                while(1) {
                    read(FH, $buf, 1) or last;
                    my ($char) = unpack "C", $buf;
                    last if $char == 0;
                    $parse_word .= $buf;
                }
                $parse_word =~ s/^\s+//;
                $parse_word =~ s/\s+$//;
                $tmp = $parse_word;
                $dclues .= "$dnum - $tmp\n";

                my $key = "$dnum";
                $self->{down}{$key}{direction} = 'down';
                $self->{down}{$key}{clue_number} = $dnum;
                $self->{down}{$key}{clue} = $tmp;
            }
        }
    }
    close FH;

    $self->{aclues} = $aclues;
    $self->{dclues} = $dclues;
    $self->{is_parsed} = 1;

}

sub is_parsed { 
    my($self) = shift;
    return $self->{is_parsed};
}

sub get_rows {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    return $self->{rows};
}

sub get_columns {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    return $self->{columns};
}

sub get_solution {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    my $solref = $self->{solution};
    my @sol = @$solref;
    return @sol;
}

sub get_solution_two {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    my $soltworef = $self->{solution_two};
    my @soltwo = @$soltworef;
    return @soltwo;
}

sub get_diagram {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    my $diagref = $self->{diagram};
    my @diag = @$diagref;
    return @diag;
}

sub get_title {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    return $self->{title};
}

sub get_author {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    return $self->{author};
}

sub get_copyright {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    return $self->{copyright};
}

sub get_across_clues {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    return $self->{aclues};
}

sub get_down_clues {
    my($self) = shift;
    _parse_file($self) unless $self->{is_parsed}; 
    return $self->{dclues};
}


1;

__END__

=head1 NAME

Convert::AcrossLite - Convert binary AcrossLite puzzle files to text.

=head1 SYNOPSIS

  use Convert::AcrossLite;

  my $ac = Convert::AcrossLite->new();
  $ac->in_file('/home/doug/puzzles/Easy.puz');
  $ac->out_file('/home/doug/puzzles/Easy.txt');
  $ac->puz2text;

  or

  use Convert::AcrossLite;

  my $ac = Convert::AcrossLite->new();
  $ac->in_file('/home/doug/puzzles/Easy.puz');
  my $text = $ac->puz2text;

  or

  use Convert::AcrossLite;

  my $ac = Convert::AcrossLite->new();
  $ac->in_file('/home/doug/puzzles/Easy.puz');
  my $ac->parse_file;
  my $title = $ac->get_title;
  my $author = $ac->get_author;
  my $copyright = $ac->get_copyright;
  my @solution = $ac->get_solution;
  my @diagram = $ac->get_diagram;
  my $across_clues = $ac->get_across_clues;
  my $down_clues = $ac->get_down_clues;

  or

  use Convert::AcrossLite;

  my $ac = Convert::AcrossLite->new();
  $ac->in_file('/home/doug/puzzles/Easy.puz');

  my($across_hashref, $down_hashref) = get_across_down;

  my %across= %$across_hashref;
  foreach my $key (sort { $a <=> $b } keys %across) {
      print "Direction: $across{$key}{direction}\n";
      print "Clue Number: $across{$key}{clue_number}\n";
      print "Row: $across{$key}{row}\n";
      print "Col: $across{$key}{column}\n";
      print "Clue: $across{$key}{clue}\n";
      print "Solution: $across{$key}{solution}\n";
      print "Length: $across{$key}{length}\n\n";
  }

  my %down= %$down_hashref;
  foreach my $key (sort { $a <=> $b } keys %down) {
      print "Direction: $down{$key}{direction}\n";
      print "Clue Number: $down{$key}{clue_number}\n";
      print "Row: $down{$key}{row}\n";
      print "Col: $down{$key}{column}\n";
      print "Clue: $down{$key}{clue}\n";
      print "Solution: $down{$key}{solution}\n";
      print "Length: $down{$key}{length}\n\n";
  }


=head1 DESCRIPTION

Convert::AcrossLite is used to convert binary AcrossLite puzzle files to text.

Convert::AcrossLite is loosely based on the C program written by Bob Newell (http://www.gtoal.com/wordgames/gene/AcrossLite).

=head1 CONSTRUCTOR

=head2 new

This is the contructor. You can pass the full path to the puzzle input file.

  my $ac = Convert::AcrossLite->new(in_file => '/home/doug/puzzles/Easy.puz');

The default value is 'Default.puz'.

=head1 METHODS

=head2 in_file

This method returns the current puzzle input path/filename. 

  my $in_filename = $ac->in_file;

You may also set the puzzle input file by passing the path/filename.

  $ac->in_file('/home/doug/puzzles/Easy.puz');

=head2 out_file

This method returns the current puzzle output path/filename. 

  my $out_filename = $ac->out_file;

You may also set the puzzle output file by passing the path/filename.

  $ac->out_file('/home/doug/puzzles/Easy.txt');


=head2 puz2text

This method will produce a basic text file in the same format as the easy.txt file provided with AcrossLite. This method will read the input file set by in_file and write to the file set by out_file. 

  $ac->puz2text;

If out_file is not set, then the text is returned.

  print $ac->puz2text;

  or

  my $text = $ac->puz2text;

=head2 get_across_down

This method will get all the information needed to build any type of output 
you may need(some info is set by parse_file): direction (across/down), 
clue_number, clue, solution, solution length, grid row and column. This method 
will return two hash references (across and down).

  my($across_hashref, $down_hashref) = get_across_down;

  my %across= %$across_hashref;
  foreach my $key (sort { $a <=> $b } keys %across) {
      print "Direction: $across{$key}{direction}\n";
      print "Clue Number: $across{$key}{clue_number}\n";
      print "Row: $across{$key}{row}\n";
      print "Col: $across{$key}{column}\n";
      print "Clue: $across{$key}{clue}\n";
      print "Solution: $across{$key}{solution}\n";
      print "Length: $across{$key}{length}\n\n";
  }

  my %down= %$down_hashref;
  foreach my $key (sort { $a <=> $b } keys %down) {
      print "Direction: $down{$key}{direction}\n";
      print "Clue Number: $down{$key}{clue_number}\n";
      print "Row: $down{$key}{row}\n";
      print "Col: $down{$key}{column}\n";
      print "Clue: $down{$key}{clue}\n";
      print "Solution: $down{$key}{solution}\n";
      print "Length: $down{$key}{length}\n\n";
  }

=head2 get_across

This method will return all the across information (some info is set by
parse_file): direction, clue_number, clue, solution, solution length, 
grid row and column. This method will return a hash reference.

  my $across_hashref = get_across;
 
  my %across= %$across_hashref;
  foreach my $key (sort { $a <=> $b } keys %across) {
      print "Direction: $across{$key}{direction}\n";
      print "Clue Number: $across{$key}{clue_number}\n";
      print "Row: $across{$key}{row}\n";
      print "Col: $across{$key}{column}\n";
      print "Clue: $across{$key}{clue}\n";
      print "Solution: $across{$key}{solution}\n";
      print "Length: $across{$key}{length}\n\n";
  }


=head2 get_down

This method will return all the down information (some info is set by
parse_file): direction, clue_number, clue, solution, solution length, 
grid row and column. This method will return a hash reference.

  my $down_hashref = get_down;

  my %down= %$down_hashref;
  foreach my $key (sort { $a <=> $b } keys %down) {
      print "Direction: $down{$key}{direction}\n";
      print "Clue Number: $down{$key}{clue_number}\n";
      print "Row: $down{$key}{row}\n";
      print "Col: $down{$key}{column}\n";
      print "Clue: $down{$key}{clue}\n";
      print "Solution: $down{$key}{solution}\n";
      print "Length: $down{$key}{length}\n\n";
  }

=head2 parse_file

This method will parse the puzzle file by calling _parse_file. 

=head2 is_parsed

This method returns file parse status: 0 if input file has not been parsed, 1 if input file has been parsed.

=head2 get_rows

This method returns the number of rows in puzzle.

  my $rows = $ac->get_rows;

=head2 get_columns

This method returns the number of columns in puzzle.

  my $columns = $ac->get_columns;

=head2 get_solution

This method returns the puzzle solution.

  my @solution = $ac->get_solution;

=head2 get_diagram

This method returns the puzzle solution diagram.

  my @solution = $ac->get_diagram;

=head2 get_title

This method returns the puzzle title.

  my $title = $ac->get_title;

=head2 get_author

This method returns the puzzle author.

  my $author = $ac->get_author;

=head2 get_copyright

This method returns the puzzle copyright.

  my $copyright = $ac->get_copyright;

=head2 get_across_clues

This method returns the puzzle across clues.

  my $across_clues = $ac->get_across_clues;

=head2 get_down_clues

This method returns the puzzle down clues.

  my $down_clues = $ac->get_down_clues;

=head1 AUTHOR

Doug Sparling E<lt>F<doug@dougsparling.com>E<gt>

=head1 COPYRIGHT

Copyright (c) 2002 Douglas Sparling. All rights reserved. This program is free
software; you can redistribute it and/or modify it under the same terms
as Perl itself.

=cut