how to create an impossible sudoku detector? - python-3.x

I'm making a sudoku solver on pygame, using backtracking. It's working, both the backtracking and the gui, however, when the user tries to solve an impossible puzzle, the game freezes.
I want to make a modification to my algorithm that, when the puzzle is impossible, the algorithm detects it, warning the user and reseting the board.
For now, this is the main code for the board class.
class Board:
solvedBoard = [[0 for i in range(9)] for j in range(9)]
gameRunning = True
def __init__(self):
self.tabuleiro = [[0 for i in range(9)] for j in range(9)]
def reset(self):
self.tabuleiro = [[0 for i in range(9)] for j in range(9)]
self.gameRunning = True
def show(self):
for i in self.tabuleiro:
for j in i:
print(j, end = ' ')
print('')
def setCell(self, linha, coluna, valor):
self.tabuleiro[linha][coluna] = valor
def isFull(self):
for i in range(9):
for j in range(9):
if self.tabuleiro[i][j] == 0:
return False
return True
def numberPossibilities(self, linha, coluna):
possible = [1 for i in range(10)]
#horizontal check
for col in range(9):
if self.tabuleiro[linha][col]:
possible[self.tabuleiro[linha][col]] = 0
#vertical check
for line in range(9):
if self.tabuleiro[line][coluna]:
possible[self.tabuleiro[line][coluna]] = 0
#mini square check
linhaSquare = (linha // 3) * 3
colunaSquare = (coluna // 3) * 3
for l in range(linhaSquare, linhaSquare + 3):
for c in range(colunaSquare, colunaSquare + 3):
if self.tabuleiro[l][c]:
possible[self.tabuleiro[l][c]] = 0
toTry = []
for k in range(1, 10):
if possible[k]:
toTry.append(k)
return toTry
def solve(self):
self.show()
print('')
if self.isFull():
for i in range(9):
for j in range(9):
self.solvedBoard[i][j] = self.tabuleiro[i][j]
self.gameRunning = False
self.tabuleiro = self.solvedBoard
elif self.gameRunning:
found = False
linha = 0
coluna = 0
for i in range(9):
for j in range(9):
if (not self.tabuleiro[i][j]):
linha = i
coluna = j
found = True
break
if found:
break
numbers = self.numberPossibilities(linha, coluna)
print(*numbers)
print('')
tamanho = len(numbers)
for k in range(tamanho):
if (not self.gameRunning):
self.tabuleiro = self.solvedBoard
break
self.tabuleiro[linha][coluna] = numbers[k]
self.solve()
if (self.gameRunning):
self.tabuleiro[linha][coluna] = 0
Note that the "show" function is only for debugging. I have an actual drawGrid function for handling the GUI.
My idea is to detect the first cell that needs to be changed, and, when the backtracking eventually comes back to It, with no other options to continue, It resets the board and then turns on a flag called "Impossible". Next iteration, the board detects that "Impossible" is on and resets the board, besides breaking the recursion.
However, that's seems too complicated, and I'm not sure how to do It without damaging code readability
What do you think? And also, Thanks in advance! This community is helping me a lot of this project :)

Related

Backtracking problem in Knights tour algorithm

**Edit: I managed to find solution for my problem so i put the correct code in place. Everything works perfect now.
I'm trying to complete the last stage of a project in Jetbrains Academy but I can't make my approach to the problem work:
It gives the user the solution for the knights tour in chess if it exists by taking their inputs. I know that there are working solutions out there, but I would like to make it work with the approach I have taken.
I can't make it work. Although, it works fine when the player plays with and I had cut the extra lines.
Example:
Input: A 4x3 board, and 1 3 (column/row)
My implementation gives 2 possible moves [(3, 2), (2, 1)]. It then continues for the possible values of 2 1 and it goes all the way down until it has no other solutions. Then it stops. Those inputs works because they dont require further search in the inside possible valid move. It doesn't check multiple paths that exist at the deeper level.
I don't understand why this happens. Where is the problem in the code?
Initialization of the board (there are extra variables because of the previous steps):
# Write your code here
from copy import deepcopy
import sys
sys.setrecursionlimit(2000)
# With that recursion limit you can test boards up to 40x40 instantly.
# Without this you can test easily up to 30 x 30 any value
# and up to 35x35 any value except edge columns / rows ex 1 4 / 4 1.
class ChessBoard:
def __init__(self, dimensions: str):
self.game_over = False
self.valid_grid = True
self.dimensions = self.board_validation(dimensions.strip().split())
if self.valid_grid:
self.x = self.dimensions[0]
self.y = self.dimensions[1]
self.cell_length = len(str(self.x * self.y)) # Cell format helper for spaces
self.board = [[(x, y) for x in range(1, self.x + 1)] for y in range(self.y, 0, -1)]
self.visual = [["_" * self.cell_length for _ in range(1, self.x + 1)] for _ in range(self.y, 0, -1)]
self.moves_visit = {key: False for i in range(len(self.board)) for key in self.board[i]}
# We need duplicates to reverse the state of the board if we want:
# to continue the game - try other initial move for the valid movements pool
self.board_duplicate = deepcopy(self.board)
self.visual_duplicate = deepcopy(self.visual)
self.moves_visit_duplicate = deepcopy(self.moves_visit)
self.x_boundaries = {i for i in range(1, self.x + 1)}
self.y_boundaries = {i for i in range(1, self.y + 1)}
self.valid_moves = [] # Helper for getting instant valid_moves for the player.
self.is_first_move = True # With that we make it easier to differentiate the first move from other inputs.
self.movements_made = 0 # Move counter direct connection with the AI solution finder and the AI move
self.board_dimensions = self.x * self.y
self.has_solution = False
self.initial_move_for_ai = None
self.first_valid_move = set({})
self.moves_placed = set({}) # Fast comparison to check if the ai_play function will return from recursion
self.total_moves = 0
self.find_solution_mode = False
def set_first(self):
if self.is_first_move:
self.is_first_move = False
def board_validation(self, board_dimensions):
if len(board_dimensions) != 2:
self.valid_grid = False
return False
try:
x = int(board_dimensions[0])
y = int(board_dimensions[1])
if x <= 0 or y <= 0:
self.valid_grid = False
return False
self.valid_grid = True
return x, y
except ValueError:
self.valid_grid = False
return False
def move_validation(self, move_to_validate):
if isinstance(move_to_validate, tuple) and move_to_validate[0] in self.x_boundaries and \
move_to_validate[1] in self.y_boundaries:
return move_to_validate
values = move_to_validate.split()
if len(values) != 2:
return False
try:
column = int(values[0])
row = int(values[1])
movement = (column, row)
if column not in self.x_boundaries or row not in self.y_boundaries:
return False
if not self.is_first_move and self.moves_visit[movement]:
return False
if not self.is_first_move and movement not in self.valid_moves:
return False
return movement
except ValueError:
return False
# Knight movement. X for player - Movement number for AI
def knight_move(self, movement, solution_mode=False):
if self.movements_made == 0:
self.set_first()
movement = self.move_validation(movement)
for m in range(len(self.board)):
if movement in self.board[m]:
index_of_move = self.board[m].index(movement)
if not self.moves_visit[movement]:
if not solution_mode:
self.visual[m][index_of_move] = " " * (self.cell_length - 1) + "X"
else:
move = "" * (self.cell_length - 3) + str(self.movements_made + 1)
self.visual[m][index_of_move] = move.rjust(self.cell_length)
self.possible_moves(movement)
self.movements_made += 1
self.moves_visit[movement] = True
return movement
return False
# Calculating exact moves to chose from. We return index based values based on visual repr
# and also player based values (2, 1), (3, 2) etc.. columns / rows
def move_calculation(self, movement_to_calc):
movement = movement_to_calc
value_to_process = [(-2, -1), (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (-1, -2), (1, -2)]
possible_calculations = [(movement[0] + value_to_process[i][0], movement[1] +
value_to_process[i][1]) for i in range(len(value_to_process))]
final_possible_indexes = [(r, self.board[r].index(j)) for r in range(len(self.board))
for j in possible_calculations if j in self.board[r] and not self.moves_visit[j]]
final_possible_indexes_translation = [self.board[i][j] for i, j in final_possible_indexes]
return final_possible_indexes, final_possible_indexes_translation
# Function that is used to calculate how many moves we have for the valid placements on board
def depth_calc(self, valid_move):
x, y = self.move_calculation(valid_move)
return len(x)
# Similar to move_calculation but this one is used to register primary valid moves for player on board
# And also to check the state of players game and outputting the depth values for the valid moves.
def possible_moves(self, main_move):
try:
moves, moves_translation = self.move_calculation(main_move)
combined_index_translation = set(zip(moves, moves_translation))
except TypeError:
return False
if not self.find_solution_mode:
for i, j in combined_index_translation:
col, row = i[0], i[1]
if not self.moves_visit[j]:
self.visual[col][row] = f'{" " * (self.cell_length - 1)}{self.depth_calc(j) - 1}'
self.valid_moves = moves_translation
print(self.__str__())
self.board_updater()
self.check_end()
return moves_translation
# Board update based on the moves_visit state True/False (if we visited or not)
def board_updater(self):
for i in range(len(self.board)):
for j in range(len(self.board[i])):
move_to_check = self.board[i][j]
if self.moves_visit[move_to_check]:
self.visual[i][j] = f'{" " * (self.cell_length - 1)}*'
else:
self.visual[i][j] = f'{" " * (self.cell_length - 1)}_'
# Getting all primary valid moves to work with
def get_valid_moves(self, from_move):
valid = self.possible_moves(from_move)
if valid:
return valid
else:
return False
# Key function for the ai_play function. It gives us the valid move with the minimum depth.
def lower_move(self, valid_values):
lengths = {i: self.depth_calc(i) for i in valid_values}
min_depth = min(lengths, key=lengths.get)
return min_depth
# Recursive function that is giving us the solution for the inputted move
def ai_play(self, move, pool, n):
if not self.moves_placed:
self.moves_placed.add(move)
if n == 1:
self.first_valid_move.add(self.lower_move(pool))
if len(self.moves_placed) == self.board_dimensions:
self.has_solution = True
return True
if self.total_moves >= self.board_dimensions ** 4:
return False
self.total_moves += 1
if pool:
best_move = self.lower_move(pool)
ai_move = self.knight_move(best_move, solution_mode=True)
self.moves_placed.add(ai_move)
if ai_move:
possible_moves = self.get_valid_moves(ai_move)
if self.ai_play(ai_move, possible_moves, n + 1):
return True
else:
self.ai_play_reset()
first_move = self.knight_reset()
self.moves_placed.add(first_move)
first_pool = self.get_valid_moves(first_move)
self.first_valid_move.add(first_pool[0])
new_pool = set(first_pool).difference(self.first_valid_move)
try:
self.ai_play(first_move, list(new_pool), 1)
except RecursionError:
self.ai_play_reset()
return False
except IndexError:
print("We tested all initial valid moves and failed.\nChoose one cell higher column and try again")
return False
# Resets the important variables of the board. Crucial if ai_play doesn't find a solution with first try.
def ai_play_reset(self):
self.moves_placed.clear()
self.board = deepcopy(self.board_duplicate)
self.visual = deepcopy(self.visual_duplicate)
self.moves_visit = deepcopy(self.moves_visit_duplicate)
self.movements_made = 0
self.has_solution = False
self.first_valid_move.clear()
# Checks the state of the player's game.
def check_end(self):
if not self.valid_moves:
self.game_over = True
if self.movements_made + 1 == self.board_dimensions:
self.has_solution = True
print("What a great tour! Congratulations!")
return False
else:
print(f"No more possible moves!\nYour knight visited {self.movements_made + 1} squares!")
return False
# Resets the Knight position to the initial for the ai_play function
def knight_reset(self):
main_move = self.knight_move(self.initial_move_for_ai, solution_mode=True)
return main_move
# Helper function that manages the outputs of the ai_play function regarding if player want a solution or not
def ai_solution_test(self, move, for_player=False):
if self.board_dimensions in {16, 4, 9}:
print("No solution exists!")
return False
self.initial_move_for_ai = move
self.find_solution_mode = True
movement = self.knight_move(move, solution_mode=True)
self.set_first()
self.initial_move_for_ai = movement
valid_moves = self.get_valid_moves(movement)
while True:
self.ai_play(movement, valid_moves, 1)
if self.has_solution:
if not for_player:
print("\nHere's the solution!")
print(self.__str__())
break
else:
break
self.find_solution_mode = False
self.ai_play_reset()
return True
def __str__(self):
visualization = [" ".rjust(self.cell_length - 1) + "-" * (self.x * (self.cell_length + 1) + 3)]
for i in range(len(self.visual)):
main_chess = " ".join([str(len(self.visual) - i).rjust(self.cell_length - 1) + "|", *self.visual[i], "|"])
visualization.append(main_chess)
visualization.append(" " * (self.cell_length - 1) + "-" * (self.x * (self.cell_length + 1) + 3))
visualization.append(
" ".rjust(self.cell_length + 2) + " ".join([f"{i}".center(self.cell_length) for i in range(1, self.x + 1)]))
return "\n".join(visualization)
def main():
while True:
board = ChessBoard(input("Enter your board's dimensions: "))
if not board.valid_grid:
print("Invalid dimensions!", end=" ")
continue
while True:
knight_move = board.move_validation(input("Enter the knight's starting position: "))
if not knight_move:
print("Invalid move!", end=" ")
continue
else:
break
while True:
puzzle_try = input("Do you want to try the puzzle? (y/n): ")
if puzzle_try not in {"y", "n"}:
print("Invalid input!", end=" ")
continue
else:
break
if puzzle_try == "y":
check_if_solution = board.ai_solution_test(knight_move, for_player=True)
if check_if_solution:
board.knight_move(knight_move)
while not board.game_over:
next_move = board.knight_move(input("Enter your next move: "))
if next_move:
board.board_updater()
else:
print("Invalid move!", end=" ")
continue
if board.game_over:
break
else:
break
else:
board.ai_solution_test(knight_move)
break
if __name__ == "__main__":
main()

Python returning none instead of an array

I made some code to solve a sudoku puzzle recursively, however when i go to return the output of the puzzle it returns none instead of the output, however printing the result at the end shows the correct solution
def sudoku(puzzle):
for y in range(9):
for x in range(9):
if(puzzle[y][x] == 0):
for n in range(1,10):
if possible(puzzle,x,y,n):
puzzle[y][x] = n
sudoku(puzzle)
puzzle[y][x] = 0
return
print(puzzle)
return puzzle
def possible(puzzle,x,y,n):
for i in range(9):
if (puzzle[y][i] == n):
return False
for i in range(9):
if (puzzle[i][x] == n):
return False
x0 = (x//3)*3
y0 = (y//3)*3
for i in range(3):
for j in range(3):
if(puzzle[y0 + j][x0 + i] == n):
return False
return True
When I run this code, the correct solution gets printed to the console but the returned output is none. I suspect that this is caused by the line return, but I don't know how to go back to the previous level of recursion without this.

Python Multiprocessing module did not work properly

I'm writing right now on a code which counts all Primenumbers up to the highest integer. The problem is that it would take way too long (about 35 days). So the solution was in our opinion the multiprocessing module, to count all Primenumbers in 4 seperated threads (process 1: starts with 1 steps +8, process 2: starts with 3 steps +8, process 3: starts with 5 steps +8, process 4: starts with 7 steps +8). That would test every odd number if it is a Primenumber.
But the following code don't work and we cant find the Problem.
Maybe one of you can help me?
from multiprocessing import Process
import time
import math
timeStart = 0
timeEnd = 0
def istTeilbar (x, y):
if x%y == 0:
return True
return False
def istPrimzahl (x):
for i in range(2, int(math.sqrt(x))+1):
if istTeilbar(x, i):
return False
return True
def schreibePrimzahlBis1 (x):
counter1 = 0
print ("x")
for i in range(1, x, 8):
if istPrimzahl(i):
counter1 = counter1 +1
print (f'Das waren {counter1} Primzahlen\n')
def schreibePrimzahlBis2 (x):
counter2 = 0
for i in range(3, x, 8):
if istPrimzahl(i):
counter2 = counter2 +1
print (f'Das waren {counter2} Primzahlen\n')
def schreibePrimzahlBis3 (x):
counter3 = 0
for i in range(5, x, 8):
if istPrimzahl(i):
counter3 = counter3 +1
print (f'Das waren {counter3} Primzahlen\n')
def schreibePrimzahlBis4 (x):
counter4 = 0
print ('counter4')
for i in range(7, x, 8):
if istPrimzahl(i):
counter4 = counter4 +1
print (f'Das waren {counter4} Primzahlen\n')
grenze = input("Die letzte zu testende Zahl:\n")
timeStart = time.time()
p1 = Process(target=schreibePrimzahlBis1, args=[int(grenze)])
p2 = Process(target=schreibePrimzahlBis2, args=[int(grenze)])
p3 = Process(target=schreibePrimzahlBis3, args=[int(grenze)])
p4 = Process(target=schreibePrimzahlBis4, args=[int(grenze)])
p1.start()
p2.start()
p3.start()
p4.start()
p1.join()
p2.join()
p3.join()
p4.join()
timeEnd = time.time()
print (f'Das hat ca. {timeEnd-timeStart} sekunden gedauert.')
The original code which would take way to long if you are intersted in:
import time
import math
timeStart = 0
timeEnd = 0
def istTeilbar (x, y):
if x%y == 0:
return True
return False
def istPrimzahl (x):
for i in range(2, int(math.sqrt(x))+1):
if istTeilbar(x, i):
return False
return True
def schreibePrimzahlBis (x):
counter = 0
for i in range(2, x):
if istPrimzahl(i):
if counter == 10000:
print (10000)
elif counter == 100000:
print (100000)
elif counter == 1000000:
print (1000000)
elif counter == 10000000:
print (10000000)
elif counter == 100000000:
print (100000000)
counter = counter +1
print ('Das waren')
print (counter)
print ('Primzahlen.\n')
grenze = input("Die letzte zu testende Zahl:\n")
timeStart = time.time()
schreibePrimzahlBis(int(grenze))
timeEnd = time.time()
print ('Das hat ca.')
print (timeEnd-timeStart)
print ('sekunden gedauert')
ยดยดยด
One way to parallelize tasks is to use concurrent.futures. I personally like this solution. Maybe you want to try this :
import concurrent.futures
function_A(arg1, arg2):
...
return val_A
function_B(arg1, arg2, arg3):
...
return val_B
if __name__ == '__main__':
arg1, arg2, arg3 = ...
with concurrent.futures.ThreadPoolExecutor() as executor:
future_A = executor.submit(function_A, arg1, arg2)
future_B = executor.submit(function_B, arg1, arg2, arg3)
print(future_A.result())
print(future_B.result())

How to write a function using for and if

I'm trying to write a code for calculating the number of factors for an arbitrary integer number but unfortunately when I run that I receive a false answer
I have tried for loop without defining function in this case and I got the result.Contrary to that, when I define a function I can't see the proper result
r = 0
def factor(a):
global r
for i in range(1, a + 1):
if a % i == 0:
r += 1
return r
a = int(input())
factor(a)
for example 18 has 6 factors but I receive just 1.
Use print to check your code. Indentation in Python matters. Also, global is not needed.
def factor(a):
r = 0
for i in range(1, a + 1):
if a % i == 0:
print('i', i)
r += 1
return r
a = int(input())
print(factor(a))
It was an indentation problem: the function should only return after the loop has finished iterating.
r = 0
def factor(a):
global r
for i in range(1, a + 1):
if a % i == 0:
r += 1
return r
a = 18 # int(input())
factor(a)
output:
6

What is wrong with my elif statement? Invalid Syntax

if option == "1":
with open("sample.txt","r") as f:
print(f.read())
numbers = []
with open("sample2.txt","r") as f:
for i in range(9):
numbers.append(f.readline().strip())
print(numbers)
from random import randint
for i in range(9):
print(numbers[randint(0,8)])
from tkinter import *
def mhello():
mtext = ment.get()
mLabel2 = Label(test, text=mtext).pack()
return
test = Tk()
ment = StringVar()
test.geometry('450x450+500+10')
test.title('Test')
mlabel = Label(test, text='Time to guess').pack()
mbutton = Button(test, text='Click', command = mhello).pack()
mEntry = Entry(test, textvariable=ment).pack()
test.mainloop()
from tkinter import *
def mhello():
my_word = 'HELLO'
mtext = ment.get()
if my_word == mtext:
mLabel2 = Label(test, text='Correct').pack()
else:
mLabel2 = Label(test, text='Incorrect').pack()
return
test = Tk()
ment = StringVar()
test.geometry('450x450+500+300')
test.title('Test')
def label_1():
label_1 = Label(test, text='Hello. Welcome to my game.').pack()
def label_2():
label_2 = Label(test, text='What word am I thinking of?').pack()
button_1 = Button(test, text='Click', command = mhello).pack()
entry_1 = Entry(test, textvariable=ment).pack()
label_1()
test.after(5000, label_2)
test.mainloop()
from tkinter import *
from random import shuffle
game = Tk()
game.geometry('200x200')
game.grid()
game.title("My Game")
board = [1, 2, 3, 4, 5, 6, 7, 8, 9]
def board_1():
board1 = []
k = 0
for i in range(3):
for j in range(3):
board1.append(Label(game, text = board[k]))
board1[k].grid(row = i, column = j)
k +=1
def board_2():
shuffle(board)
board2 = []
k = 0
for i in range(3):
for j in range(3):
board2.append(Label(game, text = board[k]))
board2[k].grid(row = i, column = j)
k +=1
board_1()
game.after(5000, board_2)
game.mainloop()
#2nd Option
elif option == "2":
print ("You have chosen option 2. Well Done, You Can Type! XD")
The bit that has the Syntax Error is the 1st elif statement (2nd Option). Ignore all the code prior to this if necessary (it is there for context). Whenever I run the code it says that there is a syntax error and just positions the typing line (I don't know what it's called) at the end of the word elif.
This is a simple fix, with if else statements you need to have a closing ELSE and in this case there is not so when your program runs it sees that theres a lonely if without its else :)
if option == "1":
elif option == "2":
else:
'do something else in the program if any other value was recieved'
also a switch statement can be used here so it does not keep checking each condition and just goes straight to the correct case :D
The problem is that your block is separated from the first if-statement, where it actually belongs to. As it is, it follows the game.mainloop() statement, and adds an unexpected indentation. Try to rearrange your code like so:
if option == "1":
with open("sample.txt","r") as f:
print(f.read())
numbers = []
with open("sample2.txt","r") as f:
for i in range(9):
numbers.append(f.readline().strip())
print(numbers)
from random import randint
for i in range(9):
print(numbers[randint(0,8)])
elif option == "2":
print ("You have chosen option 2. Well Done, You Can Type! XD")
[ Rest of the code ]

Resources