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..8).each{|i| @row_index[i] = Array.new s = i * 9 (s..s+8).each{|j| @row_index[i] << j } } @col_index = Array.new (0..8).each{|i| @col_index[i] = Array.new (0..8).each{|j| @col_index[i] << (j * 9) + i } } @block_index = Array.new block_pattern = [0,1,2,9,10,11,18,19,20] (0..8).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..8).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..8).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 == 8) && (!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..8).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/.