Hi all!
This is my first partecipation to Ruby Quiz. I developed a pretty messy
solution for clockwise (ck) solution. When I started to tackle the
counter-ck solution I started messing around with lambdas everywhere,
but eventually I found out that I just could reverse each line of a ck
solution to have the correct output. Also I didnt code the ck/cck
picking part, so you need to change it manually in initialize
My approach is still of the kind "over-use all the power of the
language" to crack the solution instead of a more reccomendable
mathematical one. But there is time to it.
I tried to comment my code extensively, maybe to understand it you need
to go through the example spiral output and check what it does ...
I loved all the really compact solutions that have been posted so far,
keep them coming!
This community just rocks!
Take care you all!
Francesco Levorato aka flevour

#! /usr/bin/env ruby
#
# Francesco Levorato aka flevour   <flevour ^a-t^ gmaildotcom>
# Sunday, 14 January 2007
# Solution for Ruby Quiz number 109 - Number Spiral

class Array
  def decrease_all
    self.map! { |x| x = x - 1}
  end
  def increase_all
    self.map! { |x| x = x + 1}
  end
  def enqueue(x)
    self.insert(0, x)
  end

  # sort of a hackish method to remove unwanted numbers from @left and
@right
  # i haven't figured out a valid reason to explain why i need to
remove these values
  # but otherwise things won't work and I haven't time to think more on
the topic
  def delete_invalid
    self.map! {|x| (x > 1) ? x : nil}
    self.compact!
  end
end

class NumberSpiral
  # this solution addresses clockwise from center to outside filling
method
  # my approach is based on the observation that each row of the matrix
is composed
  # of 3 parts: 0 or more columns, a series of consecutive numbers, 0
or more columns
  def initialize(n, direction = :ck)
    @n = n
    @dim = @n*@n
    # left contains the first part of a row
    # right contains the third part of a row
    # in a 8x8: if the row is 54,29,12,13,14,15,16,37
    # left: [54, 29], right: [37]
    @left = []
    @right = []
    # just wanted to try out this block thingie Ruby is so famous about
    @format = Proc.new { |x| print sprintf("%3s", x.to_s + " ") }
    @direction = direction # :ck or :cck
  end

  # the 3 following methods, h,l,d are were the funniest part of the
quiz: finding
  # the relationships intercurring between special elements of the
spiral.
  # they are used to build only the first (N/2 + 1) rows, as the other
ones are
  # built according only to the data structures @left and @right

  # to explain these 3 methods, define the following function
  # pivot(row): returns the number at given row just before the start
of the second part
  #             of the row (the part containing the consecutive
numbers)

  # given a row number
  # returns the distance from the pivot to the first "spiral wall"
below it
  # subtracts 1 not to overlap with l(x) results
  # in a 8x8: given row 7 returns length from 54 down to 50
  def h(x)
    2*x - @n - 1
  end

  # given a row number
  # returns the width of the next horizontal segment going from pivot
toward
  # the center of the spiral
  # in a 8x8: given row 6 returns length from 25 to 20
  def l(x)
    2 * ( x + 1 ) - @n
  end

  # given a row number, returns the difference between the pivot and
the number
  # just at its right
  # in a 8x8: given 7 returns the difference between 55 and 30
  def d(x)
    2 * ( l(x) + h(x) ) - 1
  end

  def print_me
    row = @n
    start = @dim - @n
    # prints first row
    print_row(consecutive_numbers(start))
    print "\n"

    # prepare for loop
    pivot = start - 1
    @left << pivot

    # prints the top rows, it stops after printing the row containing
the zero
    while(pivot >= 0) do
      row = row - 1
      pivot = pivot - d(row)

      # gets middle consecutive numbers
      middle = consecutive_numbers(pivot)
      print_row(middle)

      @left << pivot
      @left.decrease_all

      @right.enqueue(middle.last) # last number of consecutive series
will be in the right part in next iteration
      @right.increase_all

      pivot = @left.last
      print "\n"
    end

    @left.delete_invalid
    @right.delete_invalid
    row = row -1

    # prints the remainder of the spiral
    while(row > 0) do
      from= @left.pop

      middle = consecutive_numbers(from, :down)
      last_printed = middle.last
      print_row(middle)

      @right.delete_at(0)

      @left.decrease_all
      @right.increase_all
      row = row - 1
      print "\n"
    end
  end

  def consecutive_numbers(n, go = :up)
    array = []
    (@n - @left.size - @right.size).times do
      array << n
      if go == :up
        n = n + 1
      else # go == :down
        n = n - 1
      end
    end
    array
  end

  def print_row(middle)
    if @direction == :ck
      (@left + middle + @right).each(&@format)
    else
      (@left + middle + @right).reverse.each(&@format)
    end
  end
end

if ARGV[0]
  NumberSpiral.new(ARGV[0].to_i).print_me
else
  puts "Call me: #{$0} <matrix_dim>\n"
end