Magic Squares (#124)

#!/usr/bin/env ruby

Dan M. - http://www.dcmanges.com

Ruby Q. #124 - Ruby Quiz - Magic Squares (#124)

module ArrayExtension
def sum
inject { |x,y| x + y } || 0
end
end
Array.send :include, ArrayExtension

class MagicSquare
def initialize(size)
@size = size
end

def row
result
end

def column
row[0].zip(*row[1…-1])
end

def diagonal
first_diagonal = (0…@size).map { |index| row[index][index] }
second_diagonal = (0…@size).map { |index|
row[index][@size-index-1] }
[first_diagonal, second_diagonal]
end

protected

def result
@result ||= MagicSquareGenerator.new(@size).generate
end

def method_missing(method, *args, &block)
result.send(method, *args, &block)
end
end

class MagicSquareGenerator
def initialize(size)
@size = size
end

def generate
square = (0…@size).map { [nil] * @size }
x, y = 0, @size / 2
1.upto(@size**2) do |current|
x, y = add(x,2), add(y,1) if square[x][y]
square[x][y] = current
x, y = add(x, -1), add(y, -1)
end
square
end

private

def add(x,y)
value = x + y
value = @size + value if value < 0
value = value % @size if value >= @size
value
end

end

class MagicSquareFormatter
def initialize(magic_square)
@magic_square = magic_square
end

def formatted_square
formatting = “|” + " %#{number_width}s |" * size
rows = @magic_square.map { |row| formatting % row }
body = rows.join(“\n#{row_break}\n”)
“#{row_break}\n#{body}\n#{row_break}”
end

private

def row_break
dashes = ‘-’ * (row_width-2)
‘+’ + dashes + ‘+’
end

def number_width
(@magic_square.size**2).to_s.length
end

def row_width
(number_width+3) * size + 1
end

def size
@magic_square.size
end
end

if ARGV.first =~ /^\d+$/
size = ARGV.first.to_i
puts “Generating #{size}x#{size} magic square…”
magic_square = MagicSquare.new(size)
puts MagicSquareFormatter.new(magic_square).formatted_square
elsif FILE == $0
require ‘test/unit’
class MagicSquare3x3Test < Test::Unit::TestCase

def setup
  @magic_square = MagicSquare.new(3)
end

def test_sum_of_rows_columns_and_diagonals
  (0...3).each do |index|
    assert_equal 15, @magic_square.row[index].sum
    assert_equal 15, @magic_square.column[index].sum
  end
  assert_equal 15, @magic_square.diagonal[0].sum
  assert_equal 15, @magic_square.diagonal[1].sum
end

def test_expected_values
  assert_equal [1,2,3,4,5,6,7,8,9], @magic_square.flatten.sort
end

end

class MagicSquare9x9Test < Test::Unit::TestCase
def setup
@magic_square = MagicSquare.new(9)
end

def test_sum_of_rows_columns_and_diagonals
  (0...9).each do |index|
    assert_equal 369, @magic_square.row[index].sum
    assert_equal 369, @magic_square.column[index].sum
  end
  assert_equal 369, @magic_square.diagonal[0].sum
  assert_equal 369, @magic_square.diagonal[1].sum
end

def test_expected_values
  assert_equal (1..81).to_a, @magic_square.flatten.sort
end

end
end
END
$ ruby rubyquiz124.rb 9
Generating 9x9 magic square…
±-------------------------------------------+
| 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 |
±-------------------------------------------+

On May 20, 2007, at 2:53 PM, Robert D. wrote:

Hmm does not seem that the attachments are too readable on the Ruby
Quiz site.

http://www.rubyquiz.com/quiz115.html

:wink:

James Edward G. II

On 5/20/07, Harry K. [email protected] wrote:

On 5/18/07, Ruby Q. [email protected] wrote:

This week’s Ruby Q. 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:

Slight improvement to my first solution.

I moved the 1 into the range (1…num**2) and deleted the old line

that input the 1.

I didn’t see a reason to keep it out of the range. So I saved a step.

Also, I initialized the arrays with the value “empty”.

Code Start

num = ARGV[0].to_i
if num % 2 != 0 and num > 0
mid = ((num + 1) / 2) - 1
tot = []
num.times {tot.push(Array.new(num,“empty”))}

(1…num**2).each do |x|
tot.unshift(tot.pop)
tot.each {|g| g.push(g.shift)}

if tot[0][mid] != "empty"
2.times {tot.push(tot.shift)}
tot.each {|g| g.unshift(g.pop)}
tot[0][mid] = x.to_s.rjust((num**2).to_s.length)
end

tot[0][mid] = x.to_s.rjust((num**2).to_s.length) if tot[0][mid] == 

“empty”
end

tot.push(tot.shift)
tot.each {|x| p x.join(" ")}
end

Harry

–

A Look into Japanese Ruby List in English
http://www.kakueki.com/

Ahh, this reminds me of college. Some other people may have had to
look up the algorithm or figure it out by the supplied magic squares,
but a decade later, I still remember. Of course, back then we started
at the bottom middle, not the top middle. Doesn’t make much of a
difference in the end, though, and that’s why it’s for odd side
lengths.

#!/usr/bin/env ruby

class MagicSquare
Coords = Struct.new(:x, :y)

def initialize(size)
@size = size.to_i
@final_num = @size**2
@coords = Coords.new(@size/2, 0)

create_square

end

def init_square
@square = []
1.upto(@size) do
@square << [0] * @size
end
end

def create_square
init_square

n = 1
while n <= @final_num
  @square[@coords.y][@coords.x] = n
  n += 1
  next_coords
end

end

def to_s
output = []
num_length = @final_num.to_s.length
num_length+2 * @size
hline = ‘+’ + Array.new(@size, ‘-’ * (num_length + 2)).join(’+’) +
‘+’

output.push(hline)
(0...@size).each do |x|
  output.push('| ' + @square[x].collect { |n|

sprintf("%#{num_length}d", n) }.join(’ | ‘) + ’ |’)
output.push(hline)
end
output.join("\n")
end

private
def next_coords
new_coords = Coords.new((@coords.x-1 + @size) % @size,
(@coords.y-1 + @size) % @size)
if @square[new_coords.y][new_coords.x] != 0
new_coords = Coords.new(@coords.x, (@coords.y+1) % @size)
end
@coords = new_coords
end
end

square = MagicSquare.new(ARGV[0])

puts square

My solution, without extra credits.

#! /usr/bin/ruby

class MagicSquare
def initialize( ord )
@ord = ord

 checkOrd
 initSquare
 makeSquare
 printNiceSquare

end

def checkOrd
if @ord%2 != 1 || @ord < 0
puts “Not implemented or not possible…”
exit
end
end

def setCoord( row, col, number )
loop do
if @square[row][col].nil?
@square[row][col] = number
@oldCoord = [row, col]
return
else
row = @oldCoord[0] + 1
col = @oldCoord[1]
row -= @ord if row >= @ord
end
end
end

def initSquare
@square = Array.new(@ord)
@square.each_index do |row|
@square[row] = Array.new(@ord)
end
end

def makeSquare
(@ord**2).times do |i|
setNewCoord( i + 1 )
end
end

def setNewCoord( i )
if @oldCoord.nil?
setCoord(0, (@ord + 1)/2-1, i)
else
row = @oldCoord[0] + 2
col = @oldCoord[1] + 1

   row -= @ord if row >= @ord
   col -= @ord if col >= @ord

   setCoord(row, col, i)
 end

end

def printNiceSquare
width = (@ord**2).to_s.length

 @square.each do |row|
   row.each do |nr|
     nr = nr.nil? ? "." : nr
     spaces = width - nr.to_s.length
     print " "*spaces + "#{nr}" + "  "
   end
   puts
 end

end
end

ord = ARGV[0].to_i
MagicSquare.new( ord )

On 5/21/07, Yossef M. [email protected] wrote:

Ahh, this reminds me of college. Some other people may have had to
look up the algorithm or figure it out by the supplied magic squares,
but a decade later, I still remember. Of course, back then we started
at the bottom middle, not the top middle. Doesn’t make much of a
difference in the end, though, and that’s why it’s for odd side
lengths.

You’re right that it doesn’t mattter if you just want to generate an
odd-order magic square.

On the other hand, Conway’s LUX algorithm for generating a magic
square of an order which has a single 2 in its prime factorization,
makes use of an odd order magic square which starts in the center cell
of the top row to seed the values for generating the larger square.

–
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

$ cat magic_square.rb

#!/usr/bin/env ruby

G.D.Prasad

class Array
def / len # Thanks to _why
a=[]
each_with_index do |x,i|
a <<[] if i%len == 0
a.last << x
end
a
end
def rotate_left
self.push(self.shift)
end
def rotate_right
self.unshift(self.pop)
end
end

def rotate_array_right_index_times(arrays)
arrays.each_with_index{|array,i| i.times{array = array.rotate_right}}
end

def show(rows,n)
string = rows.map{|r| r.inject(""){|s,e| s + e.to_s.center(5," “)
+”|"}}.join("\n"+"-“6n+”\n")
puts string
end

n=ARGV[0].to_i
raise "Usage: magic_square (ODD_NUMBER>3) " if n%2==0 or n<3
nsq=nn
arrays = ((1…nsq).to_a/n).each{|a| a.reverse!}
sum = nsq
(nsq+1)/(2*n)
(n/2).times{arrays = arrays.rotate_left}
rotate_array_right_index_times(arrays)
cols=arrays.transpose
rotate_array_right_index_times(cols)
rows=cols.transpose

puts;puts
show(rows,n)
puts
puts " sum of each row,column or diagonal = #{sum}"
puts;puts

On May 18, 6:57 am, Ruby Q. [email protected] wrote:

The three rules of Ruby Q.:

A little late, but did it.
As always, enjoyed it vey much. Here is my solution

===============================

#! /usr/bin/ruby

Ruby quiz 124 - Magic Squares

Author: Ruben M.

class Array
def sum
inject(0){|a,v|a+=v}
end
end

class MagicSquare

attr_accessor :grid
SHAPES = {:L => [3, 0, 1, 2], :U => [0, 3, 1, 2], :X => [0, 3, 2, 1]}

Validates the size, and then fills the grid

according to its size.

For reference, see

Weisstein, Eric W. “Magic Square.” From MathWorld–A Wolfram Web

Resource.

http://mathworld.wolfram.com/ MagicSquare.html

def initialize(n)
raise ArgumentError if n < 3
@grid = Array.new(n){ Array.new(n) }
if n % 2 != 0
initialize_odd(n)
else
if n % 4 == 0
initialize_double_even(n)
else
initialize_single_even(n)
end
end
end

def [](x, y)
@grid[x][y]
end

def display
n = @grid.size
space = (n**2).to_s.length
sep = ‘+’ + (“-” * (space+2) + “+”) * n
@grid.each do |row|
print sep, “\n|”
row.each{|number| print " " + (“%#{space}d” % number) + " |"}
print “\n”
end
print sep, “\n”
end

def is_magic?
n = @grid.size
magic_number = (n * (n**2 + 1)) / 2
for i in 0…n
return false if @grid[i].sum != magic_number
return false if @grid.map{|e| e[i]}.sum != magic_number
end
return true
end

private

Fill by crossing method

def initialize_double_even(n)
current = 1
max = n**2
for x in 0…n
for y in 0…n
if is_diag(x) == is_diag(y)
@grid[x][y] = current
else
@grid[x][y] = max - current + 1
end
current += 1
end
end
end

def is_diag(n)
n % 4 == 0 || n % 4 == 3
end

Fill by LUX method

def initialize_single_even(n)
# Build an odd magic square and fill the new one based on it
# according to the LUX method
square = MagicSquare.new(n/2)
m = (n+2)/4
for x in 0…(n/2)
for y in 0…(n/2)
if(x < m)
shape = (x == m-1 and x == y) ? :U : :L
fill(x, y, square[x,y], shape)
elsif ( x == m )
shape = (x == y+1) ? :L : :U
fill(x, y, square[x,y], shape)
else
fill(x, y, square[x,y], :X)
end
end
end
end

def fill(x, y, number, shape)
number = ((number-1) * 4) + 1
numbers = [* number…(number + 4)]
@grid[x2][y2] = numbers[ SHAPES[shape][0] ]
@grid[x2][y2+1] = numbers[ SHAPES[shape][1] ]
@grid[x2+1][y2] = numbers[ SHAPES[shape][2] ]
@grid[x2+1][y2+1] = numbers[ SHAPES[shape][3] ]
end

Fill by Kraitchik method

def initialize_odd(n)
x, y = 0, n/2
for i in 1…(n**2)
@grid[x][y] = i
x = (x-1)%n
y = (y+1)%n
# If the new square is not empty, return to the inmediate empty
# square below the former.
unless @grid[x][y].nil?
x = (x+2)%n
y = (y-1)%n
end
end
end

end

$stdout = File.new(‘magic_square’, ‘w’) if ARGV.delete(“-f”)

$m = MagicSquare.new(ARGV[0] && ARGV[0].to_i || 5)
$m.display

if $DEBUG
require ‘test/unit’
class SquareTest < Test::Unit::TestCase
def test_magic
assert($m.is_magic?)
end
end
end

$stdout.close

==============================

Thanks for the quizes.
Ruben.

On 5/18/07, Ruby Q. [email protected] wrote:

This week’s Ruby Q. 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:

I know that people are already working on the next Ruby Q.,

but this idea just hit me and I made this improvement to my solution.

If I make more improvements, I won’t bother everyone with more posts.

I just thought this quiz was interesting.

Code Start

num = ARGV[0].to_i
if num % 2 != 0 and num > 0
tot = (0…num).map {Array.new(num)}

(1…num2).each do |x|
tot.unshift(tot.pop).each {|g| g.push(g.shift)} if x % num != 1
tot.push(tot.shift) if x % num == 1
tot[0][((num + 1) / 2) - 1] = x.to_s.rjust((num
2).to_s.length)
end

tot.push(tot.shift)
tot.each {|x| p x.join(" ")}
end

Harry

–

A Look into Japanese Ruby List in English
http://www.kakueki.com/

Some interesting background history on Magic Squares in art, science and
culture on this blog: www.glennwestmore.com.au