Hello Everybody,

so I finally got my internet access again after having struggled with
a big german internet provider for nearly two months and first thing I
did was to complete the ruby quiz of the week.

I decided to fit the binary tree into a very strict mask, I'm not
using information about the width of the subtrees to allow for better
packing, for this kind of things I prefer outputting dot code and
piping it into doc to get a nicely layouted tree in postscript format.
But this was not asked here, so following is my solution.

  def to_s(  )
    # We have log_2(n) levels
    #
    # Level i includes at most 2**i nodes when indexed starting from zero
    # That means last level needs the space for at most (2**(ceil(log_2(n)))) 
    #
    # Basic implementation: Reserve the space and fill it

    # Some constants for the ascii art
    short_arm_left = '/'
    short_arm_right = '\'

    arm_left_start = "'"
    arm_left_end = "."

    arm_right_start = "`"
    arm_right_end = "."

    arm_line = '-'

    join_string = ' '

    # Constants for position calculation
    levels = size.log2.ceil

    max_node_width = @heap[1..-1].map{ | node | node.to_s.length }.max
    max_line_width = 2**(levels-1) * max_node_width +
(2**(levels-1)-1) * join_string.length

    (0...levels).inject([]) { | result, level |
      level_node_width = max_line_width / 2**level

      # Draw the arms leading to the nodes on this level
      if level > 0
        result <<
        Array.new(2**level) { | j |
          if @heap[2**level+j]          # Only draw an arm, if there is a node
            if level_node_width < 5 # Draw short arm for short distances
              (j.even? ? short_arm_left :
short_arm_right).center(level_node_width)
            else                    # Draw long arm for long distances
              if j.even?
                (arm_left_end    + arm_line * (level_node_width / 2 -
1) + arm_left_start).rjust(level_node_width)
              else
                (arm_right_start + arm_line * (level_node_width / 2 -
1) + arm_right_end ).ljust(level_node_width)
              end
            end
          else
            ' ' * level_node_width
          end
        }.join(join_string)#.center(max_line_width)
      end

      # Draw the node on this level
      result << @heap[2**level...2**(level+1)].map { | node |
node.to_s.center(level_node_width)
}.join(join_string)#.center(max_line_width)
    }.join("\n")
  end

I'm drawing each level at a time filling the raster that is given by
the number of nodes at this level. Extra sugar is drawing of different
kinds of arms depending on how much space the arm has to span. If it
is a long distance, I draw a real arm, for short distances I only use
a slash or backslash. This gives quite good results in my opinion.

best regards,

Brian

PS: Can somebody reverse engineer the sentence that lead to the word
heap? I'd be interested in how many insertion orders lead to this same
heap.

Example output:

Start
          12           
     .----' `----.     
    20          15     
  .-' `-.     .-' `-.  
 29    23    17    22  
/    /    /          
35 40 26 51 19

Insert 13
          12           
     .----' `----.     
    20          13     
  .-' `-.     .-' `-.  
 29    23    15    22  
/    /    /         
35 40 26 51 19 17

Pop minimum
          13           
     .----' `----.     
    20          15     
  .-' `-.     .-' `-.  
 29    23    17    22  
/    /    /          
35 40 26 51 19

Word heap
                            another                            
               .--------------' `--------------.               
              are                            data              
       .------' `------.               .------' `------.       
    random           heap             of             kind      
   .--' `--.       .--' `--.       .--' `--.       .--'        
 this     the    words    on      to     some    test  

-- 
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/