[SOLUTION] The Golden Fibbonacci Ratio (#69)

Here's my solution. It's fairly pedestrian, although the size and
aspect ratio of the ASCII-art squares are configurable.

It builds the picture as an array of strings, appending alternately to
the end of the array and to the end of each string. The dimensions of
each added box are taken from the length of the edge to which it is to
be added.

It takes one optional argument on the commandline: an iteration count.

-mental

--- 8< ----

#!/usr/bin/ruby

CELL_WIDTH = 5
CELL_HEIGHT = 3

def box( size )
  width = size * CELL_WIDTH
  height = size * CELL_HEIGHT
  lines = ["#" * width] + ["##{ " " * ( width - 1 ) }"] * ( height - 1 )
  lines.map! { |line| line.dup }
end

lines = box( 1 )
$*[0].to_i.times do
  width = lines.first.size * CELL_HEIGHT
  height = lines.size * CELL_WIDTH
  if width > height
    lines.concat box( width / CELL_WIDTH / CELL_HEIGHT )
  else
    lines.zip box( height / CELL_WIDTH / CELL_HEIGHT ) do |line, box|
      line << box
    end
  end
end
lines.each { |line| puts "#{ line }#" }
puts "#{ lines.first }#"

No fancy output here, just a simple recursive version -- build the largest
rectangle as a matrix of characters, then recursively overwrite each
smaller rectangle (from the origin).

----andrew

#!/usr/bin/ruby -w

Fib = Hash.new{|h,n|n<2?h[n]=n:h[n]=h[n-1]+h[n-2]}

def fibicle(n,dia=[])
  return dia if n == 0
  cols, rows = Fib[n+1], Fib[n]
  cols, rows = rows, cols if n%2 != 0
  cols *= 2
  (0..rows).each{dia << [" "]*cols} if dia.empty?
  (0..cols).each{|i|dia[0][i] = i%2!=0?"_":" "} # top
  (0..cols).each{|i|dia[rows][i] = i%2!=0?"_":" "} # bottom
  dia[1..rows].each{|row| row[0],row[cols] = "|","|"} # sides
  fibicle(n-1,dia)
end

fibicle(ARGV[0].to_i).each{|r|puts r.join}

__END__

Here's my solution. Pretty standard output, I didn't try and do anything fancy like PostScript or OpenGL. I thought I ended up with a pretty good class design though.

#!/bin/env ruby

class Fibonacci
    DIRS = [:left, :down, :right, :up]
       def initialize(num)
        @values = []
        Fibonacci.calc(num) { |x| @values << x }
    end

    def draw
        current_dir = 0
        main = nil
        @values.each do |v|
            next if 0 == v
            b = block_for(v)
            if ! main
                main = b
                next
            end
            main = add_block(main, b, DIRS[current_dir])
            current_dir = current_dir == 3 ? 0 : current_dir + 1
        end

        print_block(main)
    end

    # Linear Fibonacci calculation
    def Fibonacci.calc(num)
        prev, result = -1, 1
        (0..num).each do
            yield sum = result + prev
            prev = result
            result = sum
        end
    end

    private
    def block_for(num)
        return [] if num == 0

        top = []
        0.upto(num * 2) { top << "#" }
               middle = ["#"]
        2.upto(num * 2) { middle << " " }
        middle << "#"

        b = []
        b << top
        2.upto(num * 2) { b << middle }
        b << top
    end

    def add_block(main, b, dir)
        if :left == dir
            return add_left(b, main)
        elsif :right == dir
            return add_left(main, b)
        elsif :down == dir
            return add_bottom(main, b)
        elsif :up == dir
            return add_bottom(b, main)
        end
    end

    def add_left(left, right)
        0.upto(left.length - 1) { |i| left[i] = left[i].slice(0..-2) + right[i] if right[i] }
        return left
    end

    def add_bottom(top, bottom)
        1.upto(bottom.length - 1) { |i| top << bottom[i] }
        return top
    end
       def print_block(b)
        b.each { |x| print x; print "\n" }
    end
end

if __FILE__ == $0
    0.upto(ARGV.length - 1) do |i|
        puts "Fibonacci for: " + ARGV[i]
        f = Fibonacci.new(ARGV[i].to_i)
        f.draw
        puts
    end
end

EEEK! :wink:

if __FILE__ == $0
     ARGV.each do |i|
       puts "Fibonacci for: #{i}"
       f = Fibonacci.new(i.to_i)
       f.draw
       puts
   end
end

···

On Mar 5, 2006, at 6:40 PM, Geoff Lane wrote:

if __FILE__ == $0
   0.upto(ARGV.length - 1) do |i|
       puts "Fibonacci for: " + ARGV[i]
       f = Fibonacci.new(ARGV[i].to_i)
       f.draw
       puts
   end
end

Hi all,

Nothing fancy here either, I just tried to solve this in the simplest way
I could (first). Then I tried 'the Ruby way' (at least how I see it after
only 10 hours of playing with Ruby...).

The output uses a two characters to display one cell of data, that is
"##" not "#". Since a single character is 8x16 bits, two chars together
make a pretty good square, and thus the proportions in the final output
are more close to the reality. The blue rectangle is the current
rectangle, while the white square is the part that gets cut from it.

Needs ANSI capable terminal, don't know if it would work or not on
Windows...

--- cut here ---
cell, blank, clear = "\033[34;1m##", "\033[37;1m##", "\033[30;0m"

next_rect = lambda { |a,b| [[a,b].max, [a,b].min + [a,b].max] }
rect = next_rect

res = [1, 1]
(1..6).each do
  p res
  side = ''
  res[0].times { side = side + cell }
  res[1].times { side = side + blank }
  res[1].times { puts side }
  puts clear
  res = rect.call(res[0], res[1])
end
--- cut here ---

the OO solution, using recursion

--- cut here ---
class GoldenRectangles
  def initialize
    @cell, @blank, @clear = "\033[34;1m##", "\033[37;1m##", "\033[30;0m"
  end

  def next_rectangle(a, b)
    [[a,b].max, [a,b].min + [a,b].max]
  end

  def show_rectangles(rect, count)
    if count > 0
      p rect
      side = ''
      rect[0].times { side = side + @cell }
      rect[1].times { side = side + @blank }
      rect[1].times { puts side }
      puts @clear
      rect = next_rectangle(rect[0], rect[1])
      show_rectangles(rect, count - 1)
    end
  end
end

GoldenRectangles.new.show_rectangles([1,1], 5)
--- cut here ---

Have a nice day all,
Alex

Here is my solution. I decided to go with RMagick for my output, since
I figured it would be easier than ascii. Then I got the idea to write
an ascii "Magick" module to do the output in ascii. It is pretty
limited, but it has enough functionality to handle this quiz. Just
change "require 'RMagick'" to "require 'asciiMagick'".

# file: golden_fibbonacci.rb

require 'RMagick'
#require 'asciiMagick'
include Magick

def fib(n)
  x,y = 1,1
   n.times do
     yield x
     x, y = y, x + y
   end
end

def points(n,multiplier=2)
  x1,y1 = 0,0

  fib(n) do |fib|
    fib *= multiplier
    x2,y2 = (fib + x1),(fib + y1)
      yield x1,y1,x2, y2
    if x1 == 0
      x1,y1 = fib,0
    else
      x1,y1 = 0,fib
    end
  end
end

img = Draw.new
img.stroke('black')
img.fill= 'white'
points(9){|x,y,@x,@y| img.rectangle(x,y,@x,@y)}
canvas = Image.new(@x + 1,@y + 1)
img.draw(canvas)

canvas.write('golden_fibbonacci.jpg')

# file: asciiMagick.rb

module Magick
  class Image
    attr_accessor :args

    def initialize(x,y)
      @a = Array.new(y)
      @a.map! do |i|
        i = Array.new(x,' ')
      end
      @a
    end

    def draw_rectangle
      x1,y1,x2,y2 = @args
      x1.upto(x2) do |i|
        @a[y1][i] = '#'
        @a[y2][i] = '#'
      end
      y1.upto(y2) do |i|
        @a[i][x1] = '#'
        @a[i][x2] = '#'
      end
    end

    def write(string)
      puts @a.map!{|i| i.join('')}
    end

  end

  class Draw

    def initialize
      @cache = Array.new
    end

    def stroke(str)
      ## this isn't used
    end

    def fill=(str)
      ## this isn't used
    end

    def draw(canvas)
      @cache.each do |hash|
        hash.each do|method_name,args|
          canvas.args = args
          canvas.method(method_name).call
        end
      end
    end

    def rectangle(x1,y1,x2,y2)
      @cache << {:draw_rectangle => [x1,y1,x2,y2]}
    end
      
    end

end

Heh, good catch.

···

On Mar 5, 2006, at 7:28 PM, Logan Capaldo wrote:

On Mar 5, 2006, at 6:40 PM, Geoff Lane wrote:

if __FILE__ == $0
   0.upto(ARGV.length - 1) do |i|
       puts "Fibonacci for: " + ARGV[i]
       f = Fibonacci.new(ARGV[i].to_i)
       f.draw
       puts
   end
end

EEEK! :wink:

if __FILE__ == $0
    ARGV.each do |i|
      puts "Fibonacci for: #{i}"
      f = Fibonacci.new(i.to_i)
      f.draw
      puts
  end
end

--
Geoff Lane <geoff@zorched.net>