简体   繁体   English

如何在 Python 中创建数独谜题

[英]How to create a Sudoku puzzle in Python

Goal is to create a 9x9 Sudoku matrix in Python.目标是在 Python 中创建一个 9x9 数独矩阵。

So I got this far.所以我走到了这一步。 But I cannot seem to get the program to get the interior contingent boxes correct.但我似乎无法让程序让内部条件框正确。

def sudoku(size):
    import random as rn
    mydict = {}
    n = 0
    while len(mydict) < 9:
        n += 1
        x = range(1, size+1)
        testlist = rn.sample(x, len(x))

        isgood = True
        for dictid,savedlist in mydict.items():
            if isgood == False:
                break
            for v in savedlist:
                if testlist[savedlist.index(v)] == v:
                    isgood = False
                    break
        if isgood == True:
            #print 'success', testlist
            mydict[len(mydict)] = testlist
    return mydict, n

return_dict, total_tries = sudoku(9)
for n,v in return_dict.items():
    print n,v
print 'in',total_tries,'tries'

You can generate a random sudoku solution board where all numbers are filled in and then remove some of them to create the puzzle.您可以生成一个随机数独解决方案板,在其中填写所有数字,然后删除其中一些以创建拼图。 This will ensure that the puzzle always has a solution.这将确保谜题始终有解决方案。 Making sure that it has exactly one solution is a bit more challenging (hint: you must leave at least 17 numbers for a 9x9 sudoku)确保它只有一个解决方案更具挑战性(提示:您必须为 9x9 数独保留至少 17 个数字)

The algorithm below will generate a NxN random sudoku solution board instantly for N < 1000.下面的算法将在 N < 1000 的情况下立即生成 NxN 随机数独解板。

base  = 3
side  = base*base

# pattern for a baseline valid solution
def pattern(r,c): return (base*(r%base)+r//base+c)%side

# randomize rows, columns and numbers (of valid base pattern)
from random import sample
def shuffle(s): return sample(s,len(s)) 
rBase = range(base) 
rows  = [ g*base + r for g in shuffle(rBase) for r in shuffle(rBase) ] 
cols  = [ g*base + c for g in shuffle(rBase) for c in shuffle(rBase) ]
nums  = shuffle(range(1,base*base+1))

# produce board using randomized baseline pattern
board = [ [nums[pattern(r,c)] for c in cols] for r in rows ]

for line in board: print(line)

[6, 2, 5, 8, 4, 3, 7, 9, 1]
[7, 9, 1, 2, 6, 5, 4, 8, 3]
[4, 8, 3, 9, 7, 1, 6, 2, 5]
[8, 1, 4, 5, 9, 7, 2, 3, 6]
[2, 3, 6, 1, 8, 4, 9, 5, 7]
[9, 5, 7, 3, 2, 6, 8, 1, 4]
[5, 6, 9, 4, 3, 2, 1, 7, 8]
[3, 4, 2, 7, 1, 8, 5, 6, 9]
[1, 7, 8, 6, 5, 9, 3, 4, 2]

You can then remove some of the numbers from the sudoku solution to create the puzzle:然后,您可以从数独解决方案中删除一些数字来创建拼图:

squares = side*side
empties = squares * 3//4
for p in sample(range(squares),empties):
    board[p//side][p%side] = 0

numSize = len(str(side))
for line in board:
    print(*(f"{n or '.':{numSize}} " for n in line))

6  .  .  .  .  3  .  .  1
.  9  .  .  .  .  .  .  3
4  .  3  .  .  .  6  .  .
.  .  .  5  9  .  2  .  6
.  .  .  .  .  .  .  .  .
.  .  7  .  .  .  .  .  4
.  .  .  .  .  .  1  7  .
.  .  2  .  .  8  .  .  .
.  .  8  .  .  .  .  4  2

For 4x4 up to 36x36 puzzles, you could make a nicer print of the board like this:对于 4x4 到 36x36 的拼图,您可以像这样制作更好的电路板打印:

def expandLine(line):
    return line[0]+line[5:9].join([line[1:5]*(base-1)]*base)+line[9:13]
line0  = expandLine("╔═══╤═══╦═══╗")
line1  = expandLine("║ . │ . ║ . ║")
line2  = expandLine("╟───┼───╫───╢")
line3  = expandLine("╠═══╪═══╬═══╣")
line4  = expandLine("╚═══╧═══╩═══╝")

symbol = " 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
nums   = [ [""]+[symbol[n] for n in row] for row in board ]
print(line0)
for r in range(1,side+1):
    print( "".join(n+s for n,s in zip(nums[r-1],line1.split("."))) )
    print([line2,line3,line4][(r%side==0)+(r%base==0)])

╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗
║ 6 │   │   ║   │   │ 3 ║   │   │ 1 ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │ 9 │   ║   │   │   ║   │   │ 3 ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║ 4 │   │ 3 ║   │   │   ║ 6 │   │   ║
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
║   │   │   ║ 5 │ 9 │   ║ 2 │   │ 6 ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │   │   ║   │   │   ║   │   │   ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │   │ 7 ║   │   │   ║   │   │ 4 ║
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
║   │   │   ║   │   │   ║ 1 │ 7 │   ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │   │ 2 ║   │   │ 8 ║   │   │   ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │   │ 8 ║   │   │   ║   │ 4 │ 2 ║
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝

[EDIT] [编辑]

Here are some additional information on the shuffling process ...以下是有关改组过程的一些其他信息...

Shuffling rows is broken down in groups of 3 rows.洗牌行被分成 3 行一组。 It is ok to swap groups as a whole but we can't swap rows across groups without breaking the integrity of the blocks.可以将组作为一个整体进行交换,但我们不能在不破坏块完整性的情况下跨组交换行。 (the same reasoning applies to columns) (同样的推理适用于列)

For example:例如:

0 [6, 2, 5,  8, 4, 3,  7, 9, 1] \                 -|
1 [7, 9, 1,  2, 6, 5,  4, 8, 3] |  group 0 -|     -| r in shuffle(rBase) 
2 [4, 8, 3,  9, 7, 1,  6, 2, 5] /           |     -|
                                            |
3 [8, 1, 4,  5, 9, 7,  2, 3, 6] \           |     -|
4 [2, 3, 6,  1, 8, 4,  9, 5, 7] |  group 1 -| *   -| r in shuffle(rBase)
5 [9, 5, 7,  3, 2, 6,  8, 1, 4] /           |     -|
                                            |
6 [5, 6, 9,  4, 3, 2,  1, 7, 8] \           |     -|
7 [3, 4, 2,  7, 1, 8,  5, 6, 9] |  group 2 -|     -| r in shuffle(rBase)
8 [1, 7, 8,  6, 5, 9,  3, 4, 2] /                 -|

                                * for g in shuffle(rBase)

We can swap groups 0,1,2 by moving all 3 of their rows at the same time:我们可以通过同时移动所有 3 行来交换组 0、1​​、2:

3 [8, 1, 4,  5, 9, 7,  2, 3, 6] \           |     -|
4 [2, 3, 6,  1, 8, 4,  9, 5, 7] |  group 1 -|     -| r in shuffle(rBase)
5 [9, 5, 7,  3, 2, 6,  8, 1, 4] /           |     -|
                                            |
6 [5, 6, 9,  4, 3, 2,  1, 7, 8] \           |     -|
7 [3, 4, 2,  7, 1, 8,  5, 6, 9] |  group 2 -| *   -| r in shuffle(rBase)
8 [1, 7, 8,  6, 5, 9,  3, 4, 2] /                 -|
                                            |
0 [6, 2, 5,  8, 4, 3,  7, 9, 1] \           |     -|
1 [7, 9, 1,  2, 6, 5,  4, 8, 3] |  group 0 -|     -| r in shuffle(rBase) 
2 [4, 8, 3,  9, 7, 1,  6, 2, 5] /           |     -|

                                * for g in shuffle(rBase)

And we can swap between the 3 rows of a group (eg 3,4,5) ...我们可以在一个组的 3 行之间交换(例如 3,4,5)...

0 [6, 2, 5,  8, 4, 3,  7, 9, 1] \                 -|
1 [7, 9, 1,  2, 6, 5,  4, 8, 3] |  group 0 -|     -| r in shuffle(rBase) 
2 [4, 8, 3,  9, 7, 1,  6, 2, 5] /           |     -|
                                            |
5 [9, 5, 7,  3, 2, 6,  8, 1, 4] \           |     -|
4 [2, 3, 6,  1, 8, 4,  9, 5, 7] |  group 1 -| *   -| r in shuffle(rBase)
3 [8, 1, 4,  5, 9, 7,  2, 3, 6] /           |     -|
                                            |
6 [5, 6, 9,  4, 3, 2,  1, 7, 8] \           |     -|
7 [3, 4, 2,  7, 1, 8,  5, 6, 9] |  group 2 -|     -| r in shuffle(rBase)
8 [1, 7, 8,  6, 5, 9,  3, 4, 2] /                 -|

                                * for g in shuffle(rBase)

We CANNOT swap rows across groups (eg 1 <--> 3):我们不能跨组交换行(例如 1 <--> 3):

0 [6, 2, 5,  8, 4, 3,  7, 9, 1] \                 -|
3 [8, 1, 4,  5, 9, 7,  2, 3, 6] |  group 0 -|     -| r in shuffle(rBase) 
2 [4, 8, 3,  9, 7, 1,  6, 2, 5] /           |     -|
                                            |
1 [7, 9, 1,  2, 6, 5,  4, 8, 3] \           |     -|
4 [2, 3, 6,  1, 8, 4,  9, 5, 7] |  group 1 -| *   -| r in shuffle(rBase)
5 [9, 5, 7,  3, 2, 6,  8, 1, 4] /           |     -|
                                            |
6 [5, 6, 9,  4, 3, 2,  1, 7, 8] \           |     -|
7 [3, 4, 2,  7, 1, 8,  5, 6, 9] |  group 2 -|     -| r in shuffle(rBase)
8 [1, 7, 8,  6, 5, 9,  3, 4, 2] /                 -|

                                * for g in shuffle(rBase)

See the duplicate 8 in the top left block, duplicate 7 below that, etc.请参见左上块中的重复 8,下面的重复 7,等等。

Single solution puzzle单解谜题

In order to generate a sudoku puzzle with only one solution you will need a solver function that can tell you if there are more than one solution.为了生成只有一个解决方案的数独游戏,您需要一个求解器函数,它可以告诉您是否有多个解决方案。 The strategy I would suggest is to start with 75% (or more) of the numbers removed, then check that there is only one solution.我建议的策略是从删除 75%(或更多)的数字开始,然后检查是否只有一个解决方案。 If there is more than one solution, put back a number and check again.如果有多个解决方案,请放回一个数字并再次检查。 You can put back a number at a random position or select a position where the solutions differ (which will converge faster to a single solution puzzle)您可以在随机位置放回一个数字或选择解决方案不同的位置(这将更快地收敛到单个解决方案难题)

First write a solver that will generate all solutions that it finds (ideally as a generator because we only need the first 2).首先编写一个求解器,它将生成它找到的所有解决方案(理想情况下作为生成器,因为我们只需要前 2 个)。 Here's a simple one:这是一个简单的:

def shortSudokuSolve(board):
    size    = len(board)
    block   = int(size**0.5)
    board   = [n for row in board for n in row ]      
    span    = { (n,p): { (g,n)  for g in (n>0)*[p//size, size+p%size, 2*size+p%size//block+p//size//block*block] }
                for p in range(size*size) for n in range(size+1) }
    empties = [i for i,n in enumerate(board) if n==0 ]
    used    = set().union(*(span[n,p] for p,n in enumerate(board) if n))
    empty   = 0
    while empty>=0 and empty<len(empties):
        pos        = empties[empty]
        used      -= span[board[pos],pos]
        board[pos] = next((n for n in range(board[pos]+1,size+1) if not span[n,pos]&used),0)
        used      |= span[board[pos],pos]
        empty     += 1 if board[pos] else -1
        if empty == len(empties):
            solution = [board[r:r+size] for r in range(0,size*size,size)]
            yield solution
            empty -= 1

Starting with a solution variable with all numbers present and board variable containing the puzzle with 3/4 of numbers cleared, you can add numbers back to the board until there is only one way to solve it:从包含所有数字的solution变量和包含已清除 3/4 数字的拼图的board变量开始,您可以将数字添加回棋盘,直到只有一种方法可以解决它:

solution=[[9, 5, 3, 1, 6, 7, 4, 2, 8],
          [4, 2, 8, 3, 5, 9, 7, 6, 1],
          [7, 6, 1, 8, 2, 4, 9, 5, 3],
          [5, 8, 4, 9, 3, 6, 2, 1, 7],
          [6, 3, 9, 7, 1, 2, 5, 8, 4],
          [2, 1, 7, 4, 8, 5, 6, 3, 9],
          [3, 4, 5, 6, 9, 1, 8, 7, 2],
          [8, 7, 2, 5, 4, 3, 1, 9, 6],
          [1, 9, 6, 2, 7, 8, 3, 4, 5]]    
board=[ [0, 0, 0, 0, 0, 0, 0, 0, 8],
        [0, 2, 0, 0, 5, 0, 7, 6, 0],
        [0, 6, 0, 0, 0, 0, 0, 0, 3],
        [5, 0, 0, 0, 0, 0, 2, 0, 7],
        [0, 3, 0, 0, 1, 0, 0, 0, 0],
        [2, 0, 0, 4, 0, 0, 0, 3, 0],
        [0, 0, 0, 6, 0, 0, 0, 0, 0],
        [8, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 0, 0, 2, 7, 0, 0, 4, 0]]
    
import random
from itertools import islice
while True:
    solved  = [*islice(shortSudokuSolve(board),2)]
    if len(solved)==1:break
    diffPos = [(r,c) for r in range(9) for c in range(9)
               if solved[0][r][c] != solved[1][r][c] ] 
    r,c = random.choice(diffPos)
    board[r][c] = solution[r][c]

output:输出:

╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗
║   │   │   ║   │   │ 7 ║   │   │ 8 ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │ 2 │   ║   │ 5 │   ║ 7 │ 6 │   ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │ 6 │   ║ 8 │   │ 4 ║   │   │ 3 ║
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
║ 5 │   │   ║   │   │   ║ 2 │   │ 7 ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║   │ 3 │   ║   │ 1 │   ║   │   │   ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║ 2 │   │   ║ 4 │   │   ║   │ 3 │   ║
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
║   │ 4 │   ║ 6 │   │   ║   │   │   ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║ 8 │   │   ║   │   │   ║ 1 │   │ 6 ║
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
║ 1 │   │   ║ 2 │ 7 │   ║   │ 4 │   ║
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝

Note that this will work in a reasonable time for 9x9 sudoku boards but you will need a much better/faster solver function for larger boards请注意,对于 9x9 数独板,这将在合理的时间内工作,但对于较大的板,您将需要更好/更快的求解器功能

Also note that this will often produce "easy" puzzles as it will add in more numbers than absolutely necessary in some cases.另请注意,这通常会产生“简单”的谜题,因为在某些情况下它会增加比绝对必要的数量更多的数字。 Choosing the minimum set of numbers to add back-in would require a layered or backtracking approach which would be a bit more complex and much slower to run.选择最小的一组数字来添加回需要一个分层或回溯的方法,这会更复杂一些,运行起来也慢得多。 Starting out with more empty spaces (eg 80% or more) will have a good chance of producing a more difficult puzzle within a reasonable timeframe.从更多的空白空间开始(例如 80% 或更多)将很有可能在合理的时间范围内产生更难的谜题。

This should work.这应该有效。

def sudoku(size):
    import time
    start_time=time.time()

    import sys
    import random as rn
    mydict = {}
    n = 0
    print '--started calculating--'
    while len(mydict) < 9:
        n += 1
        x = range(1, size+1)
        testlist = rn.sample(x, len(x))

        isgood = True
        for dictid,savedlist in mydict.items():
            if isgood == False:
                break
            for v in savedlist:
                if testlist[savedlist.index(v)] == v:
                    isgood = False
                    break

        if isgood == True:
            isgoodafterduplicatecheck = True
            mod = len(mydict) % 3
            dsavedlists = {}
            dtestlists = {}
            dcombindedlists = {}
            for a in range(1,mod + 1):
                savedlist = mydict[len(mydict) - a]               
                for v1 in savedlist:
                    modsavedlists = (savedlist.index(v1) / 3) % 3
                    dsavedlists[len(dsavedlists)] = [modsavedlists,v1]
                for t1 in testlist:
                    modtestlists = (testlist.index(t1) / 3) % 3
                    dtestlists[len(dtestlists)] = [modtestlists,t1]
                for k,v2 in dsavedlists.items():
                    dcombindedlists[len(dcombindedlists)] = v2
                    dcombindedlists[len(dcombindedlists)] = dtestlists[k]
            vsave = 0
            lst1 = []
            for k, vx in dcombindedlists.items():
                vnew = vx[0]
                if not vnew == vsave:
                    lst1 = []
                    lst1.append(vx[1])
                else:
                    if vx[1] in lst1:
                        isgoodafterduplicatecheck = False
                        break
                    else:
                        lst1.append(vx[1])
                vsave = vnew

            if isgoodafterduplicatecheck == True:

                mydict[len(mydict)] = testlist
                print 'success found', len(mydict), 'row'   

    print '--finished calculating--'
    total_time = time.time()-start_time
    return mydict, n, total_time

return_dict, total_tries, amt_of_time = sudoku(9)
print ''
print '--printing output--'
for n,v in return_dict.items():
    print n,v
print 'process took',total_tries,'tries in', round(amt_of_time,2), 'secs'
print '-------------------'

If your goal is to create 9 x 9 Sudoku, then why not a simpler program?如果您的目标是创建9 x 9数独,那么为什么不做一个更简单的程序呢? Works on any of n^2 xn^2 (boards) in poly-time.多时间在任何n^2 xn^2 (板)上工作。 To create a puzzle, you may have to remove elements manually.要创建拼图,您可能必须手动删除元素。 Guaranteeing one solution requires some backtracking.保证一种解决方案需要一些回溯。 Poly-time is what you want for larger n^2 xn^2 Sudoku Latin-Squares.对于较大的 n^2 xn^2 数独拉丁方,多时间是您想要的。

#Provide a list of non-repeating n-elements to output a valid sudoku grid.
#this code runs on python3
print('enter with [1,2,3...] brackets')
tup = input()[1:-1].split(',')
    #Input required to map out valid n x m or n^2 x n^2 Sudoku Grid
x = input('Enter mapping valid Sudoku eg. 3 for 9 x 9:')
e = input('Enter 9 for 9 x 9 ...12 for 12 x 12:')
f = input('Enter 3 if its a 9 x 9 ... n^2 x n^2:')
x = int(x)
e = int(e)
f = int(f)
    #Manipulation of Elements to prepare valid grid
squares = []
for i in range(len(tup)):
      squares.append(tup[i:] + tup[:i])

        #Everything below here is just printing
for s in range(x):
          for d in range(0,e,f):
            for si in range(s,e,f):
              for li in range(d,d+f):
                print(squares[si][li], end = '')
            print('')

#Remember that if you want
#to create a single board of n^2 x n^2
#you need to edit the below
#for a valid grid
#for example
#a 9 x 9 
#would be a 3 x 3
#and so on.

No repeating elements!没有重复元素! For grids larger than 9 x 9 please use additonal brackets for readablity.对于大于 9 x 9 的网格,请使用附加括号以提高可读性。 eg.例如。 [[01],[02],[03],....] Also, please remember that you need to know multiplication to output a properly mapped n^2 xn^2. [[01],[02],[03],....] 另外,请记住,您需要知道乘法才能输出正确映射的 n^2 xn^2。 For example, a 25 x 25 should be a 5 x 5 for the inputs as follows例如,对于如下输入,25 x 25 应该是 5 x 5

For x, x = 5对于 x,x = 5

For e, e = 25对于 e,e = 25

for f, f = 5对于 f,f = 5

Also, I had a buddy who helped me convert my algorithm into this python code for my amateur Sudoku project.另外,我有一个朋友帮我将我的算法转换成这个 Python 代码,用于我的业余数独项目。 Thanks to that Reddit user.感谢那个 Reddit 用户。

By the way, it's actually O(m^2) time.顺便说一句,实际上是 O(m^2) 时间。 Proof证明

Thank you Reddit buddy for the help.感谢 Reddit 好友的帮助。

Here's my solution这是我的解决方案

import random

def create_board(height, width):
    board = [[(i + k) % 9 + 1 for i in range(1, height + 1)] for k in range(width)] # Creates a board where each row counts to 9 such that no row contains more than one kind of each number. You can run this separately to see what it generates.
    random.shuffle(board) # Shuffles this list of lists
    board = [[board[x][y] for x in range(9)] for y in range(9)] # Reads each row and puts it into a column. (basically rotates it to its side)
    random.shuffle(board) # Shuffles this list again but while its on its side
    return board
    

Hope yall like it.希望大家喜欢。 It doesn't remove numbers but this can be done after you use the function randomly with this function它不会删除数字,但这可以在您使用此函数随机使用该函数后完成

def remove_numbers(board, remove_amount):
    h, w, r = len(board), len(board[0]), []
    spaces = [[x, y] for x in range(h) for y in range(w)]
    for k in range(remove_amount):
        r = random.choice(spaces)
        board[r[0]][r[1]] = 0
        spaces.remove(r)
    return board
# import pygame library
import pygame
 
# initialise the pygame font
pygame.font.init()
 
# Total window
screen = pygame.display.set_mode((500, 600))
 
# Title and Icon
pygame.display.set_caption("SUDOKU SOLVER USING BACKTRACKING")
img = pygame.image.load('icon.png')
pygame.display.set_icon(img)
 
x = 0
y = 0
dif = 500 / 9
val = 0
# Default Sudoku Board.
grid =[
        [7, 8, 0, 4, 0, 0, 1, 2, 0],
        [6, 0, 0, 0, 7, 5, 0, 0, 9],
        [0, 0, 0, 6, 0, 1, 0, 7, 8],
        [0, 0, 7, 0, 4, 0, 2, 6, 0],
        [0, 0, 1, 0, 5, 0, 9, 3, 0],
        [9, 0, 4, 0, 6, 0, 0, 0, 5],
        [0, 7, 0, 3, 0, 0, 0, 1, 2],
        [1, 2, 0, 0, 0, 7, 4, 0, 0],
        [0, 4, 9, 2, 0, 6, 0, 0, 7]
    ]
 
# Load test fonts for future use
font1 = pygame.font.SysFont("comicsans", 40)
font2 = pygame.font.SysFont("comicsans", 20)
def get_cord(pos):
    global x
    x = pos[0]//dif
    global y
    y = pos[1]//dif
 
# Highlight the cell selected
def draw_box():
    for i in range(2):
        pygame.draw.line(screen, (255, 0, 0), (x * dif-3, (y + i)*dif), (x * dif + dif + 3, (y + i)*dif), 7)
        pygame.draw.line(screen, (255, 0, 0), ( (x + i)* dif, y * dif ), ((x + i) * dif, y * dif + dif), 7)  
 
# Function to draw required lines for making Sudoku grid        
def draw():
    # Draw the lines
        
    for i in range (9):
        for j in range (9):
            if grid[i][j]!= 0:
 
                # Fill blue color in already numbered grid
                pygame.draw.rect(screen, (0, 153, 153), (i * dif, j * dif, dif + 1, dif + 1))
 
                # Fill grid with default numbers specified
                text1 = font1.render(str(grid[i][j]), 1, (0, 0, 0))
                screen.blit(text1, (i * dif + 15, j * dif + 15))
    # Draw lines horizontally and verticallyto form grid          
    for i in range(10):
        if i % 3 == 0 :
            thick = 7
        else:
            thick = 1
        pygame.draw.line(screen, (0, 0, 0), (0, i * dif), (500, i * dif), thick)
        pygame.draw.line(screen, (0, 0, 0), (i * dif, 0), (i * dif, 500), thick)     
 

    # Fill value entered in cell     
    def draw_val(val):
        text1 = font1.render(str(val), 1, (0, 0, 0))
        screen.blit(text1, (x * dif + 15, y * dif + 15))   
     
    # Raise error when wrong value entered
    def raise_error1():
        text1 = font1.render("WRONG !!!", 1, (0, 0, 0))
        screen.blit(text1, (20, 570)) 
    def raise_error2():
        text1 = font1.render("Wrong !!! Not a valid Key", 1, (0, 0, 0))
        screen.blit(text1, (20, 570)) 
     
    # Check if the value entered in board is valid
    def valid(m, i, j, val):
        for it in range(9):
            if m[i][it]== val:
                return False
            if m[it][j]== val:
                return False
        it = i//3
        jt = j//3
        for i in range(it * 3, it * 3 + 3):
            for j in range (jt * 3, jt * 3 + 3):
                if m[i][j]== val:
                    return False
        return True
     
    # Solves the sudoku board using Backtracking Algorithm
    def solve(grid, i, j):
         
        while grid[i][j]!= 0:
            if i<8:
                i+= 1
            elif i == 8 and j<8:
                i = 0
                j+= 1
            elif i == 8 and j == 8:
                return True
        pygame.event.pump()   
        for it in range(1, 10):
            if valid(grid, i, j, it)== True:
                grid[i][j]= it
                global x, y
                x = i
                y = j
                # white color background\
                screen.fill((255, 255, 255))
                draw()
                draw_box()
                pygame.display.update()
                pygame.time.delay(20)
                if solve(grid, i, j)== 1:
                    return True
                else:
                    grid[i][j]= 0
                # white color background\
                screen.fill((255, 255, 255))
             
                draw()
                draw_box()
                pygame.display.update()
                pygame.time.delay(50)   
        return False 
     
    # Display instruction for the game
    def instruction():
        text1 = font2.render("PRESS D TO RESET TO DEFAULT / R TO EMPTY", 1, (0, 0, 0))
        text2 = font2.render("ENTER VALUES AND PRESS ENTER TO VISUALIZE", 1, (0, 0, 0))
        screen.blit(text1, (20, 520))       
        screen.blit(text2, (20, 540))
     
    # Display options when solved
    def result():
        text1 = font1.render("FINISHED PRESS R or D", 1, (0, 0, 0))
        screen.blit(text1, (20, 570))   
    run = True
    flag1 = 0
    flag2 = 0
    rs = 0
    error = 0
    # The loop thats keep the window running
    while run:
         
        # White color background
        screen.fill((255, 255, 255))
        # Loop through the events stored in event.get()
        for event in pygame.event.get():
            # Quit the game window
            if event.type == pygame.QUIT:
                run = False 
            # Get the mouse position to insert number   
            if event.type == pygame.MOUSEBUTTONDOWN:
                flag1 = 1
                pos = pygame.mouse.get_pos()
                get_cord(pos)
            # Get the number to be inserted if key pressed   
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    x-= 1
                    flag1 = 1
                if event.key == pygame.K_RIGHT:
                    x+= 1
                    flag1 = 1
                if event.key == pygame.K_UP:
                    y-= 1
                    flag1 = 1
                if event.key == pygame.K_DOWN:
                    y+= 1
                    flag1 = 1   
                if event.key == pygame.K_1:
                    val = 1
                if event.key == pygame.K_2:
                    val = 2   
                if event.key == pygame.K_3:
                    val = 3
                if event.key == pygame.K_4:
                    val = 4
                if event.key == pygame.K_5:
                    val = 5
                if event.key == pygame.K_6:
                    val = 6
                if event.key == pygame.K_7:
                    val = 7
                if event.key == pygame.K_8:
                    val = 8
                if event.key == pygame.K_9:
                    val = 9 
                if event.key == pygame.K_RETURN:
                    flag2 = 1  
                # If R pressed clear the sudoku board
                if event.key == pygame.K_r:
                    rs = 0
                    error = 0
                    flag2 = 0
                    grid =[
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0]
                    ]
                # If D is pressed reset the board to default
                if event.key == pygame.K_d:
                    rs = 0
                    error = 0
                    flag2 = 0
                    grid =[
                        [7, 8, 0, 4, 0, 0, 1, 2, 0],
                        [6, 0, 0, 0, 7, 5, 0, 0, 9],
                        [0, 0, 0, 6, 0, 1, 0, 7, 8],
                        [0, 0, 7, 0, 4, 0, 2, 6, 0],
                        [0, 0, 1, 0, 5, 0, 9, 3, 0],
                        [9, 0, 4, 0, 6, 0, 0, 0, 5],
                        [0, 7, 0, 3, 0, 0, 0, 1, 2],
                        [1, 2, 0, 0, 0, 7, 4, 0, 0],
                        [0, 4, 9, 2, 0, 6, 0, 0, 7]
                    ]
        if flag2 == 1:
            if solve(grid, 0, 0)== False:
                error = 1
            else:
                rs = 1
            flag2 = 0   
        if val != 0:           
            draw_val(val)
            # print(x)
            # print(y)
            if valid(grid, int(x), int(y), val)== True:
                grid[int(x)][int(y)]= val
                flag1 = 0
            else:
                grid[int(x)][int(y)]= 0
                raise_error2()  
            val = 0   
           
        if error == 1:
            raise_error1() 
        if rs == 1:
            result()       
        draw() 
        if flag1 == 1:
            draw_box()      
        instruction()   
     
        # Update window
        pygame.display.update() 
     
    # Quit pygame window   
    pygame.quit()    

First, randomly create a completed sudoku solution.首先,随机创建一个完整的数独解决方案。 This part require to have a sudoku solver.这部分需要有一个数独求解器。

From the sudoku solution, constantly remove numbers at random locations.从数独解决方案中,不断删除随机位置的数字。 For each removal, check if the sudoku is still valid.对于每次删除,检查数独是否仍然有效。 That is, the sudoku has a unique solution.也就是说,数独有一个独特的解决方案。 This part needs to find out if there is more than one solution.这部分需要找出是否有多个解决方案。 It is another version of sudoku solver.它是数独求解器的另一个版本。

If not, we put back the number and try another location.如果没有,我们会放回号码并尝试另一个位置。 The process keeps going until all locations have tried.该过程继续进行,直到所有位置都尝试过。

import random
import numpy as np

def PossibleValueAtPosition(pz:[], row:int, col:int):
    r=row//3*3
    c=col//3*3
    return {1,2,3,4,5,6,7,8,9}.difference(set(pz[r:r+3,c:c+3].flat)).difference(set(pz[row,:])).difference(set(pz[:,col]))

def Solution_Count(pz:[], n:int, Nof_solution:int):
    if Nof_solution>1:
        return Nof_solution
    if n>=81:
        Nof_solution+=1
        return Nof_solution
    (row,col) = divmod(n,9)
    if pz[row][col]>0:
        Nof_solution = Solution_Count(pz, n+1, Nof_solution)
    else:
        l = PossibleValueAtPosition(pz, row,col)
        for v in l:
            pz[row][col] = v
            Nof_solution = Solution_Count(pz, n+1, Nof_solution)
            pz[row][col] = 0
    return Nof_solution 

def SudokuSolver(pz:[], n:int):
    if n==81:
        return True
    (row,col) = divmod(n,9)
    if pz[row][col]>0:
        if SudokuSolver(pz, n+1):
            return True
    else:
        l = list(PossibleValueAtPosition(pz, row,col))
        random.shuffle(l)
        for v in l:
            pz[row][col] = v
            if SudokuSolver(pz, n+1):
                return True
            pz[row][col] = 0
    return False

def DigHoles(pz:[], randomlist:[], n:int, nof_holes:int):
    if n>=81 or nof_holes>=64:
        return
    (row,col) = divmod(randomlist[n],9)
    if pz[row][col]>0:
        pz_check=pz.copy()
        pz_check[row][col]=0
        Nof_solution = Solution_Count(pz_check, 0, 0)
        if Nof_solution==1:
            pz[row][col]=0
            nof_holes+=1
            print(pz)
            print("{} zeros".format(nof_holes))
            print()
    DigHoles(pz, randomlist, n+1, nof_holes)

def main():
    puzzle = np.zeros((9,9), dtype=int)
    SudokuSolver(puzzle, 0)
    print(puzzle, "--------- Answer\n")
    randomlist = list(range(81))
    random.shuffle(randomlist)
    DigHoles(puzzle, randomlist, 0, 0)

if __name__ == "__main__":
    main()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM