簡體   English   中英

如何在 Python 中創建數獨謎題

[英]How to create a Sudoku puzzle in Python

目標是在 Python 中創建一個 9x9 數獨矩陣。

所以我走到了這一步。 但我似乎無法讓程序讓內部條件框正確。

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'

您可以生成一個隨機數獨解決方案板,在其中填寫所有數字,然后刪除其中一些以創建拼圖。 這將確保謎題始終有解決方案。 確保它只有一個解決方案更具挑戰性(提示:您必須為 9x9 數獨保留至少 17 個數字)

下面的算法將在 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]

然后,您可以從數獨解決方案中刪除一些數字來創建拼圖:

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

對於 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 ║
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝

[編輯]

以下是有關改組過程的一些其他信息...

洗牌行被分成 3 行一組。 可以將組作為一個整體進行交換,但我們不能在不破壞塊完整性的情況下跨組交換行。 (同樣的推理適用於列)

例如:

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)

我們可以通過同時移動所有 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)

我們可以在一個組的 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)

我們不能跨組交換行(例如 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)

請參見左上塊中的重復 8,下面的重復 7,等等。

單解謎題

為了生成只有一個解決方案的數獨游戲,您需要一個求解器函數,它可以告訴您是否有多個解決方案。 我建議的策略是從刪除 75%(或更多)的數字開始,然后檢查是否只有一個解決方案。 如果有多個解決方案,請放回一個數字並再次檢查。 您可以在隨機位置放回一個數字或選擇解決方案不同的位置(這將更快地收斂到單個解決方案難題)

首先編寫一個求解器,它將生成它找到的所有解決方案(理想情況下作為生成器,因為我們只需要前 2 個)。 這是一個簡單的:

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

從包含所有數字的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]

輸出:

╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗
║   │   │   ║   │   │ 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 │   ║
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝

請注意,對於 9x9 數獨板,這將在合理的時間內工作,但對於較大的板,您將需要更好/更快的求解器功能

另請注意,這通常會產生“簡單”的謎題,因為在某些情況下它會增加比絕對必要的數量更多的數字。 選擇最小的一組數字來添加回需要一個分層或回溯的方法,這會更復雜一些,運行起來也慢得多。 從更多的空白空間開始(例如 80% 或更多)將很有可能在合理的時間范圍內產生更難的謎題。

這應該有效。

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 '-------------------'

如果您的目標是創建9 x 9數獨,那么為什么不做一個更簡單的程序呢? 多時間在任何n^2 xn^2 (板)上工作。 要創建拼圖,您可能必須手動刪除元素。 保證一種解決方案需要一些回溯。 對於較大的 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.

沒有重復元素! 對於大於 9 x 9 的網格,請使用附加括號以提高可讀性。 例如。 [[01],[02],[03],....] 另外,請記住,您需要知道乘法才能輸出正確映射的 n^2 xn^2。 例如,對於如下輸入,25 x 25 應該是 5 x 5

對於 x,x = 5

對於 e,e = 25

對於 f,f = 5

另外,我有一個朋友幫我將我的算法轉換成這個 Python 代碼,用於我的業余數獨項目。 感謝那個 Reddit 用戶。

順便說一句,實際上是 O(m^2) 時間。 證明

感謝 Reddit 好友的幫助。

這是我的解決方案

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
    

希望大家喜歡。 它不會刪除數字,但這可以在您使用此函數隨機使用該函數后完成

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()    

首先,隨機創建一個完整的數獨解決方案。 這部分需要有一個數獨求解器。

從數獨解決方案中,不斷刪除隨機位置的數字。 對於每次刪除,檢查數獨是否仍然有效。 也就是說,數獨有一個獨特的解決方案。 這部分需要找出是否有多個解決方案。 它是數獨求解器的另一個版本。

如果沒有,我們會放回號碼並嘗試另一個位置。 該過程繼續進行,直到所有位置都嘗試過。

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