#!/usr/bin/env ruby
# Script to print an N by N spiral as shown in the following example:
#
# 56 57 58 59 60 61 62 63
#
# 55 30 31 32 33 34 35 36
#
# 54 29 12 13 14 15 16 37
#
# 53 28 11 2 3 4 17 38
#
# 52 27 10 1 0 5 18 39
#
# 51 26 9 8 7 6 19 40
#
# 50 25 24 23 22 21 20 41
#
# 49 48 47 46 45 44 43 42
#
# Let item with value 0 be at x, y coordinate (0, 0). Consider the
# spiral to be rings of numbers. For the numbers 1 through 8 make
# up ring level 1, and numbers 9 through 24 make up ring level 2.
# To figure out the value at a particular x, y position, note that
# the first value at any level is (2 * level - 1) ** 2 and use that
# value to count up or down to the coordinate.
class Spiral
def initialize(size)
@size = size
@center = size/2
end
# returns the value for a given row and column of output
def position_value(row, col)
x, y = coordinate = coordinate_for(row, col)
level = [x.abs, y.abs].max
if x < level && y > -level
# return number for top left portion of ring
first_number(level) +
steps_between(first_coordinate(level), coordinate)
else
last_number(level) -
steps_between(last_coordinate(level), coordinate)
end
end
def maximum_value
@size * @size - 1
end
def first_number(level)
(2 * level - 1) ** 2
end
def last_number(level)
first_number(level + 1) - 1
end
def first_coordinate(level)
[-level, -level + 1]
end
def last_coordinate(level)
[-level, -level]
end
def coordinate_for(row, col)
[col - @center, @center - row]
end
def steps_between(point1, point2)
(point1[0] - point2[0]).abs + (point1[1] - point2[1]).abs
end
end
if __FILE__ == $0
size = ARGV[0].to_i
spiral = Spiral.new(size)
width = spiral.maximum_value.to_s.length + 3
(0...size).each do |row|
(0...size).each do |col|
print spiral.position_value(row, col).to_s.rjust(width)
end
print "\n\n"
end
end