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