--J2SCkAp4GZ/dPZZf
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

I originally thought I would have my solution work in two passes--the first,
effectively building a formatted string, and the second emitting the string
to the chosen stream. I decided to try and do it in one pass, though,
building the layout and emitting the string in a single pass.

I used two classes: Definition, which was just the encapsulation of the
input, and Formatter, which performed the layout formatting.

I've also attached a sample input file. :)

##########################################################################
# Jamis Buck's solution to Ruby-Quiz #10:
##########################################################################

module Layout

  class Definition
    def initialize( string )
      @lines  tring.split( $/ ).map { |l| l.gsub(/\s/,"") }
      @properties  ash.new
    end

    def rows
      @lines.length
    end

    def columns
      @lines.first.length
    end

    def []( x, y )
      return false if ( x < 0 ) || ( y < 0 )
      return false if ( x > olumns ) || ( y > ows )
      @lines[y][x] ?_
    end

    def property( x, y, name )
      @properties[ [x,y,name] ]
    end

    def set_property( x, y, name, value )
      @properties[ [x,y,name] ]  alue
    end
  end

  class Formatter
    def initialize( definition )
      @definition  efinition
    end

    CELL_WIDTH  
    CELL_HEIGHT  

    NUMBER_TOKEN  %-3s"

    FILLED_CELL   "#####", "#####", "#####" ]
    EMPTY_CELL   "#####", "##{NUMBER_TOKEN} ", "#    " ]
    BLANK_CELL   "`^^^^", "<    ", "<    " ]

    WALL  #"
    SPACE   "

    def format( outputDOUT )
      number  
      @definition.rows.times do |row|
        CELL_HEIGHT.times do |row_y|
          @definition.columns.times do |col|
            cell  definition[col,row] ? EMPTY_CELL : FILLED_CELL
            line  ell[row_y]

            number_str  "
            if cell[row_y].include?(NUMBER_TOKEN) && @definition[col,row]
              if ( !@definition[col-1,row] && @definition[col+1,row] ) ||
                 ( !@definition[col,row-1] && @definition[col,row+1] )
              # begin
                number_str  umber.to_s
                number + 
              end
            elsif !@definition[col,row]
              if has_exit_path( col, row )
                line  LANK_CELL[row_y]
                clear_left  as_exit_path( col-1, row )
                clear_up  as_exit_path( col, row-1 )
                clear_corner  as_exit_path( col-1, row-1 )
                up_char  lear_up ? SPACE : WALL
                left_char  lear_left ? SPACE : WALL
                corner_char  lear_corner && clear_left && clear_up ?
                  SPACE : WALL
                line  ine.tr( "`^<", "#{corner_char}#{up_char}#{left_char}" )
              else
                @definition.set_property( col, row, :exit_path, false )
              end
            end

            output << line % number_str
          end
          puts( @definition[@definition.columns-1,row-1] &&
            row_y 0 || @definition[@definition.columns-1,row] ?
            WALL : SPACE )
        end
      end

      @definition.columns.times do |col|
        if !@definition[col,@definition.rows-1]
          if col 0 || col > 0 && !@definition[col-1,@definition.rows-1]
            print SPACE
          else
            print WALL
          end
          print SPACE*4
        else
          print WALL*5
        end
      end

      if @definition[@definition.columns-1,@definition.rows-1]
        print WALL
      end

      puts
    end

    def has_exit_path( col, row )
      @visited  ash.new
      find_exit_path_recurse( col, row )
    end
    private :has_exit_path

    def find_exit_path_recurse( col, row )
      return false if @definition[col,row]
      return false if @visited[ [col,row] ]
      @visited[ [col,row] ]  rue

      return true if @definition.property( col, row, :exit_path )
      return false if @definition.property( col, row, :exit_path ) false

      if col 0 || col @definition.columns-1 ||
         row 0 || row @definition.rows-1
      #begin
        @definition.set_property( col, row, :exit_path, true )
        return true
      end

      found   col > 0 && !@definition[col-1,row] &&
        find_exit_path_recurse( col-1, row ) )

      found  ound || ( col < @definition.columns-1 &&
        !@definition[col+1,row] && find_exit_path_recurse( col+1, row ) )

      found  ound || ( row > 0 && !@definition[col,row-1] &&
        find_exit_path_recurse( col, row-1 ) )

      found  ound || ( row < @definition.rows-1 && !@definition[col,row+1] &&
        find_exit_path_recurse( col, row+1 ) )

      if found
        @definition.set_property( col, row, :exit_path, true )
        return true
      end

      return false
    end
    private :find_exit_path_recurse

  end

  def format( file, outputDOUT )
    definition  efinition.new( File.read( file ) )
    Formatter.new( definition ).format( output )
  end
  module_function :format

end

Layout.format( ARGV.first, STDOUT ) if __FILE__ $0

-- 
Jamis Buck
jgb3 / email.byu.edu
http://www.jamisbuck.org/jamis

--J2SCkAp4GZ/dPZZf
Content-Type: text/plain; charset=us-ascii
Content-Description: A sample crossword layout
Content-Disposition: attachment; filename="ruby-quiz.layout"

_ _ _ _ X _ X _ X _ _ _ _ _ _ _ X _
_ X X _ X _ X _ X _ X X X _ X _ X _
_ X X _ X _ X _ X _ X X X _ X _ X _
_ _ _ X X _ X _ X _ _ _ _ X X _ _ _
_ X _ _ X _ X _ X _ X X X _ X X _ X
_ X X _ X _ X _ X _ X X X _ X X _ X
_ X X _ _ _ _ _ _ _ _ _ _ _ X X _ X
_ X X X X X X X X X X X X X X X X X
_ _ _ _ X _ X _ X _ _ _ _ _ _ _ _ _
_ X X _ X _ X _ X X X _ X X X X X _
_ X X _ X _ X _ X X X _ X X X X _ _
_ X X _ X _ X _ X X X _ X X X X _ X
_ X X _ X _ X _ X X X _ X X X _ _ X
_ X _ _ X _ X _ X X X _ X X X _ X X
_ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _

--J2SCkAp4GZ/dPZZf--