On 12/01/07, Ruby Quiz <james / grayproductions.net> wrote:
> Write a Ruby program to print out a "spiral" of numbers that fill a NxN square.
> Your program will take a single argument to specify the dimensions of the square
> (1 or higher). The number zero represents the center of the spiral, and the
> succeeding integers spiral out in a clockwise (or counterclockwise; your choice)
> direction from the center until the square is filled.
>
> Your program should write the output line by line, without using an array to
> build up the data first.

Ok, I like a challenge and my maths needed some dusting down.

Here is my solution, after a fair bit of scribbling on paper to work
out the formula for sprial_value_at(x,y).

I found it a bit frustrating that ranges in Ruby can only be
ascending; but soon found that we have #downto, which achieves the
desired result.

Thanks to Bob & James for setting this quiz.

Marcel

#! /usr/bin/env ruby
#
# Marcel Ward   <wardies ^a-t^ gmaildotcom>
# Sunday, 14 January 2007
# Solution for Ruby Quiz number 109 - Number Spiral

# Prints a clockwise spiral, starting with zero at the centre (0,0).
# Note, here x increases to the east and y increases to the north.
def spiral(size)
  # maximum -ve/+ve reach from the centre point "0" at (0,0)
  neg_reach = -pos_reach = size/2
  # we miss out the bottom/left sides for even-sized spirals
  neg_reach += 1 if size % 2 == 0

  # Compute width to allocate a cell based on the max value printed
  cell_width = (size**2 - 1).to_s.size + 3

  pos_reach.downto(neg_reach) do
    |y|
    spiral_line((neg_reach..pos_reach), y, cell_width)
  end
end

def spiral_line(x_range, y, cell_width)
  x_range.each do
    |x|
    print spiral_value_at(x, y).to_s.center(cell_width)
  end
  puts
end

# calculate the value in the spiral at location (x,y)
def spiral_value_at(x, y)
  if x + y > 0  # top/right side
    if x > y    # right side
      4 * x**2 - x - y
    else        # top side
      4 * y**2 - 3 * y + x
    end
  else          # bottom/left side
    if x < y    # left side
      4 * x**2 - 3 * x + y
    else        # bottom side
      4 * y**2 - y - x
    end
  end
end

spiral(10)