#!/usr/bin/env python
# coding: utf-8
# In[1]:
# sections of code borrowed from "You will Never Want to Play Sudoku Again" by Srini Devadas
# https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-s095-programming-for-the-puzzled-january-iap-2018/puzzle-8-you-wont-want-to-play-sudoku-again/sudoku.py
# Whatever license Srini chose applies to the code he or his grad students wrote. Standard MIT license applies to anything else that I wrote.
# In[2]:
import numpy as np
import copy
# In[3]:
# global to keep track of the number of backtracks necessary
limit = 10000
backtracks = 0
# In[5]:
def findNextCellToFill(grid):
# Takes a 9 by 9 grid and looks row by column for the first zero.
# Returns either x, y coordinates of first '0' or -1, -1
for x in range(0, 9):
for y in range(0, 9):
if grid[x][y] == 0:
return x, y
return -1, -1
# In[6]:
def isValid(grid, i, j, e):
# takes a 9 by 9 grid; i,j coordinates; and a number 'e' then looks row-wise, column-wise, and sector-wise for duplicates of the number 'e''
# returns True if passes row/column/sector checks.
# returns False otherwise
rowOk = all([e != grid[i][x] for x in range(9)])
if rowOk:
columnOk = all([e != grid[x][j] for x in range(9)])
if columnOk:
# find the top left x, y coordinate of section containing the i,j cell
secTopX, secTopY = 3*(i//3), 3*(j//3)
for x in range(secTopX, secTopX+3):
for y in range(secTopY, secTopY+3):
if grid[x][y] == e:
return False
return True
return False
# In[7]:
def makeRandomSudoku(number_of_randoms):
# generates a random board with no constraints
# board has number_of_randoms filled in values with all other values as zero
grid_list = [0]*81
locations = np.random.choice(81, number_of_randoms, replace=False)
for location in locations:
grid_list[location] = int(np.random.randint(1,high=10))
i = 0
grid = []
while i < len(grid_list):
grid.append(grid_list[i:i+9])
i+=9
return grid
# In[8]:
def solveSudoku(grid, i=0, j=0):
# fills in missing squares of Sudoku puzzle using rules with a brute-force guess/check
# takes a grid and starts looking in the top left corner i,j = 0,0
global limit
global backtracks
if backtracks < limit:
i, j = findNextCellToFill(grid)
if i == -1:
# we solved it or there's nothing left to solve
return True
for e in range (1, 10):
# try different values in i, j location
if isValid(grid, i, j, e):
grid[i][j] = e
if solveSudoku(grid, i, j):
return True
# undo current cell for backtracking
if backtracks < limit:
backtracks += 1
grid[i][j] = 0
else:
# loop fell through without filling a number
return False
else:
return False
# In[9]:
def printSudoku(grid):
numrow = 0
for row in grid:
if numrow % 3 == 0 and numrow != 0:
print(' ')
print(row[0:3], ' ', row[3:6], ' ', row[6:9])
numrow += 1
return
# In[10]:
def process_input(grid):
# converts a grid into a flat list with one-hot encoding
onehot_encoded = list()
flat_grid = [item for sublist in grid for item in sublist]
for value in flat_grid:
temp = [0 for _ in range(10)]
temp[value - 1] = 1
onehot_encoded.append(temp)
#flat_onehot_grid = [item for sublist in onehot_encoded for item in sublist]
return onehot_encoded
# In[11]:
def build_dataset(number_of_datapoints, number_of_randoms):
# takes an int, number_of_datapoints, and builds a dataset of solvable Sudoku grids
# each test_grid will have number_of_randoms constraints
test_grid
test_grid = makeRandomSudoku(number_of_randoms)
# In[12]:
def generate_pair():
global backtracks
# generates an unsolved and solved grid
backtracks = 0
solved = makeRandomSudoku(20)
unsolved = copy.deepcopy(solved)
if solveSudoku(solved):
return unsolved, solved
else:
backtracks = 0
return generate_pair()
# In[ ]:
import time
data = []
start = time.time()
for pair in range(5):
data1, data2 = generate_pair()
data.append([data1, data2])
end = time.time()
print(end-start)
# In[ ]:
printSudoku(data[0][0])
print('\n')
printSudoku(data[0][1])
# In[ ]:
def save_data(data):
with open("Sudoku_data.txt", 'w') as f:
for pair in data:
for grid in pair:
for row in grid:
for column in row:
f.write("%i\n" % column)
Comments