Hi all,

Although I'm quite new to both Ruby and programming, sudoku generator

was the problem I picked to practice the basics over the last couple

of weeks. I just came across this thread by chance, so I thought I

might as well put my code here. I know nothing about algorithm or CS

stuff, so I just used a fairly naive approach.

(1) Fill a matrix using 1-9 in each row, column and block.

(2) Pick one cell, and see if punching the hole there will produce

another solution.

(3) If there's a uniq solution, then punch out the cell. And, repeat

this until you check all the cells.

I found that (1) wasn't as easy as I thought. You need some sort of

good way to do this, but again, this was just my practice of Ruby

programming, so I just brute forced: trial and error.

#!/usr/bin/ruby

=begin

= sudoku

* Data structure

matrix = [1,2,3,4,,,,,,,81]

row_index = [[0,1,2,...8], [9,10,11...17],

col_index = [[0,9,18,...72], [1,10,19...73],

block_index = [[0,1,2,9,10,11,],[3,4,5,12,],,]

* Block numbering

|0|1|2|

|3|4|5|

|6|7|8|

=end

class Matrix

attr_accessor :row_index, :col_index, :block_index, :matrix

def initialize

@matrix = Array.new(81,0)

@row_index = Array.new

(0..

.each{|i|

@row_index[i] = Array.new

s = i * 9

(s..s+

.each{|j|

@row_index[i] << j

}

}

@col_index = Array.new

(0..

.each{|i|

@col_index[i] = Array.new

(0..

.each{|j|

@col_index[i] << (j * 9) + i

}

}

@block_index = Array.new

block_pattern = [0,1,2,9,10,11,18,19,20]

(0..

.each{|i|

@block_index[i] = block_pattern.map{|j|

(j + (i / 3) * 27) + ((i % 3) * 3)}

}

end

def row(x)

@row_index[x].collect{|x| @matrix[x]}

end

def col(x)

@col_index[x].collect{|x| @matrix[x]}

end

def block(x)

@block_index[x].collect{|x| @matrix[x]}

end

def which_block(x,y)

((y / 3) * 3) + (x / 3)

end

def index(x,y)

x + (y * 9)

end

def fill_matrix

srand

100.times{|i|

break if self.try_fill_matrix

}

# average 7.53 times

end

def try_fill_matrix

count = 0

abandon_flag = false

@matrix.fill(0)

(0..

.each{|y|

repeat_flag = true

break if(abandon_flag == true)

until(repeat_flag == false)

count += 1

if (count > 20)

abandon_flag = true

@matrix.fill(0)

break

end

seeds = (1..9).to_a

(0..

.each{|x|

appear = col(x) | block(which_block(x,y))

n = (seeds - appear).pick_one

@matrix[index(x,y)] = n

seeds.delete(n)

if((x ==

&& (!row(y).include?(nil)))

repeat_flag = false

end

}

end

}

!abandon_flag

end

def make_new_puzzle

self.fill_matrix

self.reduce

end

def reduce

srand

candidate = (0..80).to_a

candidate.delete_if{|i| @matrix[i] == 0}

while(candidate.size > 0)

c = candidate.pick_one

if(uniq_solution?(c))

@matrix[c] = 0

end

candidate.delete(c)

end

end

def reduce_by_quadruple

srand

candidate = (0..80).to_a

candidate.find{|i| ((i % 9) <= 4) && ((i / 9) <= 4)}

while(candidate.size > 0)

c1 = candidate.pick_one

c2 = 80 - c1

if(uniq_solution?(c1) && (uniq_solution?(c2)))

@matrix[c1] = @matrix[c2] = 0

end

candidate.delete(c1)

candidate.delete(c2)

end

end

def uniq_solution?(n)

i = @matrix[n]

x = n % 9

y = n / 9

(1..9).to_a.delete_if{|n| n == i}.each{|j|

if(!col(x).include?(j) &&

!row(y).include?(j) &&

!block(which_block(x,y)).include?(j))

return false

end

}

end

def to_s

print "-"*19,"\n"

(0..

.each{|y|

i = 0

row(y).each{|n|

if((i % 3) == 0)

separator = "|"

else

separator = " "

end

n = " " if n == 0

print separator, n

i += 1

}

print "|\n"

if(((y + 1) % 3) == 0)

print "-"*19,"\n"

end

}

end

def to_line

self.matrix.join

end

end

class Array

def pick_one

r = rand(self.size)

self[r]

end

end

m = Matrix.new

m.make_new_puzzle

puts m

This script seemed to generate decent sudoku puzzles like the one

below, and I went further.

-------------------

| 1 | 8 | 5|

|8 7 5|3 9| |

| 3|5 7 |9 4|

-------------------

|4 5 | 2| 3 |

|6 8 |1 5| |

|3 |8 6 |2 5 1|

-------------------

| 2 8|6 1 3|5 |

| 9|4 |6 2 8|

|5 | |7 |

-------------------

Puzzles generated with this thing are not as fun, difficult to solve

at all. All the puzzles were too easy with many hints left. The

numbers of hints are between 35-50, averaging 42.7. Thinking that

maybe symmetry is a key to a good sudoku, I added this method:

def reduce_by_quadruple

srand

candidate = (0..80).to_a

candidate.find{|i| ((i % 9) <= 4) && ((i / 9) <= 4)}

while(candidate.size > 0)

c1 = candidate.pick_one

c2 = 80 - c1

if(uniq_solution?(c1) && (uniq_solution?(c2)))

@matrix[c1] = @matrix[c2] = 0

end

candidate.delete(c1)

candidate.delete(c2)

end

end

This method reduces a set of 4 cells that are in symmetric positions at

a time. The results were somewhat interesting. The numbers remaining

for each approach were:

(a) Reduce one by one : 42.7

(b) Reduce 4 cells at a time : 47.8

(c) Reduce 4 cells at a time, when that ends, reduce one by one: 42.5

You can see apparent symmetry in the puzzles generated with (b) and (c),

yet even (c) doesn't yield any better puzzles at all.

Any given matrix filled with arbitrary numbers ends up either a

symmetric or a random puzzle with almost same amount of hints?

By the way, I was kind of sure that the puzzles generated with this

script are okay because there's nothing complicated involved, however,

I used somebody else's solver to check if any of the puzzles has a

uniq solution. Alas, 3-5 out of 100 puzzles, there were more than 1

solution...

I don't know what I did wrong. I just lost interest and felt content

with the fact that I learnt many things with Ruby and had fun doing

this. And then, I found this thread, couldn't resist.

--

Ken Nishimura, Tokyo

Matthew Moss wrote:

> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

>

> The three rules of Ruby Quiz 2:

>

> 1. Please do not post any solutions or spoiler discussion for this

> quiz until 48 hours have passed from the time on this message.

>

> 2. Support Ruby Quiz 2 by submitting ideas as often as you can!

> Visit <http://splatbang.com/rubyquiz/>.

>

> 3. Enjoy!

>

> Suggestion: A [QUIZ] in the subject of emails about the problem

> helps everyone on Ruby Talk follow the discussion. Please reply to

> the original quiz message, if you can.

>

> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

>

> ## Sudoku Generator (#182)

>

>

> _Quiz idea provided by Lloyd Linklater_

>

> A bit over three years ago, we had a quiz to [solve sudoku puzzles]

> [1]. Now it's time to write a script that generates sudoku puzzles.

>

> The output of your script should be the puzzle to solve. (Since we

> already have solver scripts from quiz #43, there is no need to output

> the solution.) In addition to generating the puzzle, you should adhere

> either one or the other of these two methods:

>

> 1. Reduce a generated puzzle to the fewest clues that will still

> suffice for finding a solution. To your output, include an estimated

> difficulty level.

>

> 2. Accept a command line parameter: the estimated difficulty level.

> Generate the puzzle such that it roughly matches that difficulty level.

>

> The difficulty level should be a number from 1 (easiest) to 10

> (hardest). Difficulty level, obviously, is somewhat subjective.

> However, there are [various sudoku techniques][2] that may be able to

> help you decide whether a puzzle is more difficult or not. Some

> suggested metrics include: number of clues, number of "gimmes", number

> of possible solutions, cascading singletons, etc.

>

>

> [1]: http://rubyquiz.com/quiz43.html

> [2]: http://www.sadmansoftware.com/sudoku/techniques.htm
--

Posted via

http://www.ruby-forum.com/.