Here is my second solution.
Timings are on bottom but include size=15000 in under 5min. The way
this works is it starts with two solved 5x5 games with special
properties (end in center, and start on either the middle of the top,
or the middle of the left side -- pretty print them if that's
confusing). it then places as many of those as needed to fill the
entire board. it also has to deal with incrementing the numbers in
the original 2 patterns. i included some test code which makes sure
the final solution is valid.
The way placing the 5x5 pre-solved patterns works is i make a column
with a "left" on top and all the rest are the "up" variety. moving
from the middle of a 5x5 grid straight down takes you one square off
the grid, to they line up and all connect. now we're in the middle of
the bottom 5x5 pattern, so what we need is to move right and have
that transition to the next pattern, and then go back up to the top,
then back down again, etc to get the second variety of column which
goes back up, i just reversed the rows in the first column. before
combining the columns for the final solution, they are transposed to
be rows so I can just use +
maybe a better way to get a feel for how it works is to set the size
to 10 or 15 and pp the solution. then scan the answer for
1,25,26,50,51,75 etc to see how it's laid out
require "pp"
s = ARGV[0].to_i
if s <= 0
s = 165
end
if s % 5 != 0
puts "Invalid Input. Must be multiple of 5."
exit
end
Left = [[17, 9, 2, 18, 24], [4, 14, 22, 7, 13], [1, 19, 25, 10, 20],
[16, 8, 3, 15, 23], [5, 11, 21, 6, 12]]
Up = [[17, 4, 1, 16, 5], [9, 14, 19, 8, 11], [2, 22, 25, 3, 21], [18,
7, 10, 15, 6], [24, 13, 20, 23, 12]]
num = s / 5
$items_in_a_column = 25 * num
$inc_col_count = -$items_in_a_column
def inc_col m
$inc_col_count += $items_in_a_column
m.map { |e| e.map { |e2| e2 + $inc_col_count} }
end
$inc_count = 0
def inc m
$inc_count += 25
m.map { |e| e.map { |e2| e2 + $inc_count} }
end
col = Left
(num-1).times do |j|
col += inc(Up)
end
col2 = col.reverse.transpose
col = col.transpose
sol = []
(num/2).times {sol += inc_col(col) + inc_col(col2)}
if num % 2 == 1
sol += inc_col(col)
end
# pp sol
# =========
# = tests =
# =========
Connections = [
[3,0],
[-3,0],
[0,3],
[0,-3],
[2,2],
[2,-2],
[-2,2],
[-2,-2]
]
def check_valid_movement m
flat = m.flatten
n = flat.length
rt_n = Math.sqrt(n).to_i
1.upto(n-1) do |i|
a = flat.index i
b = flat.index i+1
fail "num not found #{i}" if a.nil? or b.nil?
x1 = a % rt_n
y1 = a / rt_n
x2 = b % rt_n
y2 = b / rt_n
move = [x1-x2, y1-y2]
return false unless Connections.include? move
end
true
end
if true # warning: SLOW. took 9 minutes with size = 265. it's n^4 if
you treat the input number as n.
fail "dups" unless sol.flatten.uniq.length == sol.flatten.length
fail "invalid moves" unless check_valid_movement(sol)
puts "valid moves!"
end
__END__
cg5:~/cs/rb/quizzes >time ruby 90take3.rb 10000
real 1m34.483s
user 1m29.471s
sys 0m3.695s
cg5:~/cs/rb/quizzes >time ruby 90take3.rb 15000
real 4m47.336s
user 4m30.390s
sys 0m11.935s
cg5:~/cs/rb/quizzes >time ruby 90take3.rb 25000
real 23m0.525s
user 21m47.343s
sys 0m50.721s
26700 = gave up after 45min, incomplete. 30000 = gave up after 3
hours, incomplete.