From: Bill Dolinar Date: 2007-01-15T08:25:12+09:00 Subject: Re: [QUIZ] Number Spiral (#109) [solution] #!/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