Solution to #90

The week is nearly up but here is my solution. My program solved it for
5
and 6 in about a second but is taking much longer for the rest.

(5…10).each { |i| Quiz90.new(i).solve }
Success after 440 iterations!
±------------------+
| 13 3 22 14 4|
| 24 16 11 25 17|
| 21 8 5 20 9|
| 12 2 23 15 1|
| 6 19 10 7 18|
±------------------+
Success after 19953 iterations!
±----------------------+
| 6 26 12 7 27 13|
| 23 9 4 24 10 1|
| 17 33 21 18 36 31|
| 5 25 11 8 28 14|
| 22 19 3 32 20 2|
| 16 34 29 15 35 30|
±----------------------+

class Array
def shuffle
ret = self.dup
len = self.length
rand(2*len).times do
r1 = rand(len)
r2 = rand(len)
ret[r1],ret[r2] = ret[r2],ret[r1]
end
return ret
end
end

class Point
attr_accessor :x, :y

two constructors in one!

x,y for creation from integers

“[x,y]” for creation from a Point.to_s string

This is needed for a hash lookup because the hash lookup using a

point

with a certain xy value can’t be found with a different point that

has

the same xy values. Arrrgh! Am I just doing something wrong?

def initialize *args
if 2 == args.length
@x = args[0]
@y = args[1]
else
@x,@y = args[0].gsub(/[|]/,’’).split(’,’)
@x = @x.to_i
@y = @y.to_i
end
end

reminder: “==” implicitly defines “!=”

def == (aPoint)
aPoint.x == x and aPoint.y == y
end

def + relativePoint
Point.new( x + relativePoint.x,
y + relativePoint.y )
end

def to_s
“[#{x},#{y}]”
end
end

class Quiz90
attr_accessor :size, :grid

def initialize( sz )
@dead_ends = 0
sz = 5 if sz < 5 # minimum solvable size
@size = sz
@jumps = [ Point.new(-3,0), Point.new(3,0),
Point.new(0,-3), Point.new(0,3),
Point.new(-2,-2), Point.new(-2,2),
Point.new(2,-2), Point.new(2,2) ]
@grid = {}
(1…sz).each do |x|
(1…sz).each do |y|
@grid[ Point.new(x,y).to_s ] = 0
end
end
end

def solve
start_positions = @grid.keys

start_positions.shuffle.each do |pos|
  solution = place_next_number( 1, Point.new(pos), @grid )
  #self.print( "Solution!", solution ) if solution
end

end

def dead_end( n, g )
@dead_ends += 1
#self.print( “Dead end #{@dead_ends} after #{n} moves:”, g ) if
(@dead_ends % 10) == 0
end

place_next_number

Return nil unless we’re finished

def place_next_number( number, pos, g )
#puts “place_next_number( #{number}, #{pos} ) => #{g[pos.to_s]}”
#return nil if @dead_ends >= 50
return nil unless g[pos.to_s] and g[pos.to_s] == 0
g[pos.to_s] = number
if number == (@size * @size)
#pp pos
self.print( “Success after #{@dead_ends} iterations!”, g )
return g
end
@jumps.shuffle.each do |jump|
return g if place_next_number( number + 1, pos+jump, g )
dead_end( number, g )
end
g[pos.to_s] = 0
return nil
end

def print( text, g )
puts text
puts “+#{’-’(4@size-1)}+”
(1…@size).each do |x|
line = []
(1…@size).each do |y|
#line << g[Point.new(x,y).to_s]
line << sprintf("%3d",g[ Point.new(x,y).to_s ])
end
puts “|#{line.join(’ ')}|”
end
puts “+#{’-’(4@size-1)}+”
end
end