On May 18, 6:57 pm, Ruby Quiz <j... / grayproductions.net> wrote: > The three rules of Ruby Quiz: > > 1. Please do not post any solutions or spoiler discussion for this quiz until > 48 hours have passed from the time on this message. > > 2. Support Ruby Quiz by submitting ideas as often as you can: > > http://www.rubyquiz.com/ > > 3. Enjoy! > > Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone > on Ruby Talk follow the discussion. Please reply to the original quiz message, > if you can. > > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= > > A magic square of size N is a square with the numbers from 1 to N ** 2 arranged > so that each row, column, and the two long diagonals have the same sum. For > example, a magic square for N = 5 could be: > > +------------------------+ > | 15 | 8 | 1 | 24 | 17 | > +------------------------+ > | 16 | 14 | 7 | 5 | 23 | > +------------------------+ > | 22 | 20 | 13 | 6 | 4 | > +------------------------+ > | 3 | 21 | 19 | 12 | 10 | > +------------------------+ > | 9 | 2 | 25 | 18 | 11 | > +------------------------+ > > In this case the magic sum is 65. All rows, columns, and both diagonals add up > to that. > > This week's Ruby Quiz is to write a program that builds magic squares. To keep > the problem easy, I will say that your program only needs to work for odd values > of N. Try to keep your runtimes pretty reasonable even for the bigger values of > N: > > $ time ruby magic_square.rb 9 > +--------------------------------------------+ > | 45 | 34 | 23 | 12 | 1 | 80 | 69 | 58 | 47 | > +--------------------------------------------+ > | 46 | 44 | 33 | 22 | 11 | 9 | 79 | 68 | 57 | > +--------------------------------------------+ > | 56 | 54 | 43 | 32 | 21 | 10 | 8 | 78 | 67 | > +--------------------------------------------+ > | 66 | 55 | 53 | 42 | 31 | 20 | 18 | 7 | 77 | > +--------------------------------------------+ > | 76 | 65 | 63 | 52 | 41 | 30 | 19 | 17 | 6 | > +--------------------------------------------+ > | 5 | 75 | 64 | 62 | 51 | 40 | 29 | 27 | 16 | > +--------------------------------------------+ > | 15 | 4 | 74 | 72 | 61 | 50 | 39 | 28 | 26 | > +--------------------------------------------+ > | 25 | 14 | 3 | 73 | 71 | 60 | 49 | 38 | 36 | > +--------------------------------------------+ > | 35 | 24 | 13 | 2 | 81 | 70 | 59 | 48 | 37 | > +--------------------------------------------+ > > real 0m0.012s > user 0m0.006s > sys 0m0.006s > > For extra credit, support even values of N. You don't need to worry about N = 2 > though as it is impossible. Here it is: # magic_square.rb # Magic Square with Odd Number class OddMagicSquare attr_reader :square def initialize(n) @square = Array.new(n) @square.each_index {|i| @square[i] = Array.new(n)} middle = n/2 @square[0][middle] = 1 @pos = [0,middle] @len = n end def printing_magic_square v_border = '+' + '-' * (6 * @len - 1) + '+' @square.each do |row| puts v_border row.each do |r| if r then print format('|' + "%4d" + ' ', r) else print '| nil ' end end print "|\n" end puts v_border end def iterate_square value = 2 last_value = @len ** 2 while true do move fill value break if value == last_value value = value + 1 end end private def fill(value) @square[@pos[0]][@pos[1]] = value end def move move_down if not move_diagonal_up end def move_diagonal_up # get future position future_pos = Array.new(2) @pos[0] == 0 ? future_pos[0] = @len - 1 : future_pos[0] = @pos[0] - 1 @pos[1] == @len - 1 ? future_pos[1] = 0 : future_pos[1] = @pos[1] + 1 # check if it is empty or not if @square[future_pos[0]][future_pos[1]] then return false else @pos = future_pos end return true end def move_down @pos[0] == @len - 1 ? @pos[0] = 0 : @pos[0] = @pos[0] + 1 end end The test case: #tc_magic_square.rb require 'test/unit' require 'magic_square' class TestOddMagicSquare < Test::Unit::TestCase def setup @n = 5 @odd_magic_square = OddMagicSquare.new(@n) @odd_magic_square.iterate_square @square = @odd_magic_square.square @sum = (@n ** 2 / 2 + 1) * @n end def test_sum_row_and_col @n.times do |t| assert_equal @sum, get_sum_column(t) assert_equal @sum, get_sum_row(t) end assert_equal @sum, get_sum_diagonal('left') assert_equal @sum, get_sum_diagonal('right') end private def get_sum_column(i) sum = 0 @n.times do |t| sum += @square[t][i] end sum end def get_sum_row(i) sum = 0 @n.times do |t| sum += @square[i][t] end sum end def get_sum_diagonal(alignment) if alignment == 'left' then sum = i = 0 @n.times do |t| sum += @square[i][i] i = i + 1 end return sum elsif alignment == 'right' then sum = 0 i = @n - 1 @n.times do |t| sum += @square[i][i] i = i - 1 end return sum else raise 'Alignment must be left or right.' end end end Here how it is run: # use_magic_square.rb require 'magic_square' # getting input n = ARGV[0].to_i # input must be odd and bigger than 2 raise 'Argument must be odd and bigger than 2' if n % 2 == 0 or n < 3 odd_magic_square = OddMagicSquare.new(n) odd_magic_square.iterate_square odd_magic_square.printing_magic_square $ ruby use_magic_square.rb 5 +-----------------------------+ | 17 | 24 | 1 | 8 | 15 | +-----------------------------+ | 23 | 5 | 7 | 14 | 16 | +-----------------------------+ | 4 | 6 | 13 | 20 | 22 | +-----------------------------+ | 10 | 12 | 19 | 21 | 3 | +-----------------------------+ | 11 | 18 | 25 | 2 | 9 | +-----------------------------+