Search code examples
arraysrubyuniquepermutationsudoku

Sudoku generator in ruby


I'm trying the following code to generate a 3x3 sudoku,

[*1..3].permutation.uniq{
    _1.index(1) && _1.index(2) && _1.index(3)
}

I'm trying to pass multiple condition in uniq method, but I can't do so. I've seen other SO post in which they suggest,

uniq{[cond1, cond2, ...]}

But, it doesn't work, tried || too.

I think && takes only the last, whereas || takes only the first condition, as both of them are truthy!

How to solve this?


Expected output:

3x3 sudoku like:
[1, 2, 3]
[2, 3, 1]
[3, 1, 2]

The order of numbers in above sudoku doesn't matter.


Solution

  • I'm trying to use those three parameters in uniqueness, somehow. Like, if any permutation has any number at same index, then drop that one.

    I doubt that you can generate a sudoku using uniq because the method operates on a single element at a time without taking the other elements into account.

    You could use a loop to remove those values.

    You start by generating all permutations: (I'm using a 4×4 sudoku for demonstration purposes)

    permutations = [*1..4].permutation.to_a
    #=> [[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 3, 4, 2], [1, 4, 2, 3], [1, 4, 3, 2],
    #    [2, 1, 3, 4], [2, 1, 4, 3], [2, 3, 1, 4], [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1],
    #    [3, 1, 2, 4], [3, 1, 4, 2], [3, 2, 1, 4], [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1],
    #    [4, 1, 2, 3], [4, 1, 3, 2], [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]
    

    From this array, you pick a random element as the first row:

    row_1 = permutations.sample #=> [3, 2, 1, 4]
    

    You then remove all elements that have 3 in the 1st spot, or 2 in the 2nd, 1 in 3rd, or 4 in 4th:

    permutations.delete_if { |row| row_1.zip(row).any? { |i, j| i == j } }
    #=> [[1, 3, 4, 2], [1, 4, 2, 3], [1, 4, 3, 2],
    #    [2, 1, 4, 3], [2, 3, 4, 1], [2, 4, 3, 1],
    #    [4, 1, 2, 3], [4, 1, 3, 2], [4, 3, 2, 1]]
    

    From the remaining array, you can pick another random element as the second row and start over:

    row_2 = permutations.sample #=> [2, 3, 4, 1]
    
    permutations.delete_if { |row| row_2.zip(row).any? { |i, j| i == j } }
    #=> [[1, 4, 2, 3], [1, 4, 3, 2],
    #    [4, 1, 2, 3], [4, 1, 3, 2]]
    
    row_3 = permutations.sample #=> [1, 4, 3, 2]
    
    permutations.delete_if { |row| row_3.zip(row).any? { |i, j| i == j } }
    #=> [[4, 1, 2, 3]]
    
    row_4 = permutations.sample #=> [4, 1, 2, 3]   (or permutations.last)
    

    The resulting row_1, row_2, row_3 and row_4 are: (printed as a sudoku)

    ╔═════╤═════╗
    ║ 3 2 │ 1 4 ║
    ║ 2 3 │ 4 1 ║
    ╟─────┼─────╢
    ║ 1 4 │ 3 2 ║
    ║ 4 1 │ 2 3 ║
    ╚═════╧═════╝
    

    You might notice that although rows and columns only contain unique numbers, the 2×2 subgrids contain duplicates. It is therefore invalid.


    You could probably add another condition that takes the subgrids into account, but there are much easier ways to generate a valid sudoku. Here's one:

    You generate a random valid first row, e.g.:

    row_1 = [*1..4].shuffle #=> [3, 4, 2, 1]
    

    For the 2nd row, you rotate the first row by 2: (the subgrid size)

    row_2 = row_1.rotate(2) #=> [2, 1, 3, 4]
    

    For the 3rd and 4th row, you rotate the first row by 1 and 3 respectively:

    row_3 = row_1.rotate(1) #=> [4, 2, 1, 3]
    row_4 = row_1.rotate(3) #=> [1, 3, 4, 2]
    

    Printed as a sudoku:

    ╔═════╤═════╗
    ║ 3 4 │ 2 1 ║
    ║ 2 1 │ 3 4 ║
    ╟─────┼─────╢
    ║ 4 2 │ 1 3 ║
    ║ 1 3 │ 4 2 ║
    ╚═════╧═════╝