-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmagic_square.rb
126 lines (107 loc) · 2.6 KB
/
magic_square.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# --------------------------------------------------------
# Program: Magic Square
# Author: Harsh Gupta
# Date: 23 February 2015, Monday
# Reference: http://mathworld.wolfram.com/MagicSquare.html
# --------------------------------------------------------
class MagicSquare
class << self
protected :new
def Factory arg
order = arg.to_i
if order == 0 || order == 2
raise ArgumentError.new('Magic Square cannot be of order 0 or 2')
end
case order % 4
when 0
DoublyEvenMagicSquare.new order
when 2
LuxMagicSquare.new order
else
OddMagicSquare.new(order)
end
end
end
def pp
digits = @size.to_s.size
separator = '+' << ('-' * (digits + 2) << '+') * @order << "\n"
(0...@order).inject(separator) do |s, i|
(0...@order).inject(s + '|') do |s, j|
s << " #{self[i, j].to_s.rjust(digits)} |"
end << "\n#{separator}"
end
end
alias_method :to_s, :pp
def compute
raise NoMethodError.new('compute method not defined in factory.')
end
def [](i, j)
raise NoMethodError.new('[] method not defined with row and col arguments')
end
attr_accessor :order, :size
private
def initialize order
@order = order
@size = @order * @order
compute
end
end
class OddMagicSquare < MagicSquare
def compute
# Siamese Movement order
@data = Array.new(@size)
i, j = 0, @order / 2
(1..@size).each do |value|
self[i, j] = value
ii, jj = i - 1, j + 1
i, j = self[ii, jj] ? [i + 1, j] : [ii, jj]
end
self
end
def [] i, j
@data[get_index(i, j)]
end
def []= i, j, v
@data[get_index(i, j)] = v
end
def get_index i, j
(i % @order) * @order + (j % @order)
end
end
class DoublyEvenMagicSquare < MagicSquare
def compute
# Nothing to compute here
end
def [](i, j)
i, j = i % @order, j % @order
value = (i * @order) + j + 1
i, j = i % 4, j % 4
((i == j) || (i + j == 3)) ? (@size + 1 - value) : value
end
end
class LuxMagicSquare < MagicSquare
L = [4, 1, 2, 3]
U = [1, 4, 2, 3]
X = [1, 4, 3, 2]
def initialize order
@odd_magic_square = MagicSquare::Factory(order / 2)
super
end
def compute
end
def [](i, j)
i, j = i % @order, j % @order
ii, jj = i / 2, j / 2
center = @order / 2 / 2
value = @odd_magic_square[ii, jj]
case
when ii < center then L
when ii == center then (jj == center) ? U : L
when ii == center+1 then (jj == center) ? L : U
else X
end [i%2*2 + j%2] + 4 * (value - 1)
end
end
if __FILE__ == $0
puts MagicSquare::Factory(ARGV.shift)
end