-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
class Backtracking: | ||
|
||
def __init__(self, grid): | ||
self.grid = grid | ||
|
||
@staticmethod | ||
def isEmpty(grid): | ||
for i in range(len(grid)): | ||
for j in range(len(grid[0])): | ||
if grid[i][j] == 0: | ||
return (i, j) | ||
|
||
def isValid(self, grid, nb, pos): | ||
for i in range(len(grid)): | ||
if grid[i][pos[1]] == nb and pos[0] != i: | ||
return False | ||
for i in range(len(grid[0])): | ||
if grid[pos[0]][i] == nb and pos[1] != i: | ||
return False | ||
subgrid_col_index = pos[1] // 3 | ||
subgrid_row_index = pos[0] // 3 | ||
for i in range(subgrid_row_index * 3, subgrid_row_index * 3 + 3): | ||
for j in range(subgrid_col_index * 3, subgrid_col_index * 3 + 3): | ||
if grid[i][j] == nb and (i, j) != pos: | ||
return False | ||
return True | ||
|
||
def solve(self, grid): | ||
empty = self.isEmpty(grid) | ||
if not empty: | ||
return True | ||
else: | ||
row, col = empty | ||
for i in range(1, 10): | ||
if self.isValid(grid, i, (row, col)): | ||
grid[row][col] = i | ||
if self.solve(grid): | ||
return True | ||
grid[row][col] = 0 | ||
return False |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import os, time | ||
import numpy as np | ||
from backtracking import Backtracking | ||
from bruteForce import BruteForce | ||
|
||
def file_holder(file_name): | ||
with open(file_name, 'r') as f: | ||
file = f.read() | ||
file = file.replace(' ', '').replace('\n', '').replace('_', "0") | ||
return (np.array(list(file)).astype(int)).reshape((9, 9)) | ||
def benchmark_all_puzzles(): | ||
puzzle_directory = "./sudoku_puzzles" | ||
puzzle_files = os.listdir(puzzle_directory) | ||
brute_force_times = [] | ||
backtracking_times = [] | ||
|
||
for puzzle_file in puzzle_files: | ||
puzzle_path = os.path.join(puzzle_directory, puzzle_file) | ||
|
||
if os.path.isfile(puzzle_path): | ||
sudoku = file_holder(puzzle_path) | ||
back_or_brute = input("Select 1 for the Brute Force solution or 2 for the Backtrack solution: ") | ||
|
||
if back_or_brute == '1': | ||
solver = BruteForce(sudoku) | ||
else: | ||
solver = Backtracking(sudoku) | ||
|
||
start_time = time.time() | ||
if solver.solve(sudoku): | ||
end_time = time.time() | ||
execution_time = end_time - start_time | ||
|
||
print(f"Solved {puzzle_file} in {execution_time} seconds") | ||
|
||
if back_or_brute == '1': | ||
brute_force_times.append(execution_time) | ||
else: | ||
backtracking_times.append(execution_time) | ||
else: | ||
print(f"No solution found for {puzzle_file}") | ||
#calculate and print average execution times for all puzzles | ||
if brute_force_times: | ||
avg_brute_force_time = sum(brute_force_times) / len(brute_force_times) | ||
print(f"Average Brute Force execution time: {avg_brute_force_time} seconds") | ||
|
||
if backtracking_times: | ||
avg_backtracking_time = sum(backtracking_times) / len(backtracking_times) | ||
print(f"Average Backtracking execution time: {avg_backtracking_time} seconds") | ||
|
||
|
||
benchmark_all_puzzles() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
|
||
class BruteForce: | ||
def __init__(self, grid): | ||
self.grid = grid | ||
self.counter = 0 | ||
|
||
def solve(self, grid): | ||
for i in range(9): | ||
for j in range(9): | ||
if grid[i][j] == 0: | ||
for k in range(1, 10): | ||
if self.check(grid, i, j, k): | ||
grid[i][j] = k | ||
self.counter += 1 | ||
if self.solve(grid): | ||
return True | ||
else: | ||
grid[i][j] = 0 | ||
self.counter -= 1 | ||
return False | ||
return True | ||
|
||
def check(self, grid, row, column, num): | ||
if num in grid[row]: | ||
return False | ||
for i in range(9): | ||
if grid[i][column] == num: | ||
return False | ||
|
||
x = (row - (row % 3)) | ||
y = (column - (column % 3)) | ||
|
||
for i in range(3): | ||
for j in range(3): | ||
if (i, j) == (row, column): | ||
continue | ||
if grid[x + j][y + i] == num: | ||
return False | ||
return True |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import os | ||
import numpy as np | ||
from backtracking import Backtracking | ||
from bruteForce import BruteForce | ||
|
||
|
||
def file_holder(file_name): | ||
with open(file_name, 'r') as f: | ||
file = f.read() | ||
file = file.replace(' ', '').replace('\n', '').replace('_', "0") | ||
return (np.array(list(file)).astype(int)).reshape((9, 9)) | ||
|
||
|
||
def get_coordinates(grid): | ||
coordinates = [] | ||
for i in range(len(grid)): | ||
for j in range(len(grid[i])): | ||
if grid[i][j] > 0: | ||
coordinates.append((i, j)) | ||
return coordinates | ||
|
||
|
||
def print_grid(grid, coordinates): | ||
hsep = "\033[34m" + " _______________________" + "\033[0m" | ||
vsep = "\033[34m" + "|" + "\033[0m" | ||
print(hsep) | ||
for i in range(len(grid)): | ||
print(vsep, end=" ") | ||
for j in range(len(grid[i])): | ||
current = str(grid[i][j]) | ||
if (i, j) in coordinates: | ||
current = f"\033[32m{current}\033[0m" # Set text color to green | ||
if j % 3 == 0 and j != 0: | ||
print(vsep, end=" ") | ||
print(current, end=" ") | ||
print(vsep, end=" ") | ||
print("") | ||
if i != 8 and i % 3 == 2: | ||
print(hsep) | ||
return hsep | ||
|
||
|
||
def execute(): | ||
while True: | ||
file_name = input("Please enter a valid file (.txt): ") | ||
if os.path.exists(file_name): | ||
sudoku = file_holder(file_name) | ||
coordinates = get_coordinates(sudoku) | ||
# Use Brute Force or Backtracking solver | ||
back_or_brute = input("Select 1 for the Brute Force solution or 2 for the Backtrack solution: ") | ||
if back_or_brute == 1: | ||
solver = BruteForce(sudoku) | ||
else: | ||
solver = Backtracking(sudoku) | ||
if solver.solve(sudoku): | ||
print(print_grid(solver.grid, coordinates)) | ||
else: | ||
print("No solution found.") | ||
choice = input("Do you want to solve another one? (y/n) ") | ||
if choice.lower() not in ['y', 'yes']: | ||
print("See ya!") | ||
break | ||
|
||
execute() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import pygame | ||
import os | ||
import numpy as np | ||
from backtracking import Backtracking | ||
from bruteForce import BruteForce | ||
import tkinter as tk | ||
from tkinter import filedialog, simpledialog | ||
|
||
def file_holder(file_name): | ||
with open(file_name, 'r') as f: | ||
file = f.read() | ||
file = file.replace(' ', '').replace('\n', '').replace('_', "0") | ||
return (np.array(list(file)).astype(int)).reshape((9, 9)) | ||
|
||
|
||
def get_coordinates(grid): | ||
coordinates = [] | ||
for i in range(len(grid)): | ||
for j in range(len(grid[i])): | ||
if grid[i][j] > 0: | ||
coordinates.append((i, j)) | ||
return coordinates | ||
|
||
## initialize Pygame | ||
pygame.init() | ||
WIDTH, HEIGHT = 600, 600 | ||
GRID_SIZE = 9 | ||
CELL_SIZE = WIDTH // GRID_SIZE | ||
FPS = 60 | ||
WHITE = (255, 255, 255) | ||
FONT_SIZE = 36 | ||
|
||
#create a Pygame window | ||
window = pygame.display.set_mode((WIDTH, HEIGHT)) | ||
pygame.display.set_caption("Sudoku Solver") | ||
font = pygame.font.Font(None, FONT_SIZE) | ||
|
||
def draw_grid(grid, coordinates): | ||
window.fill(WHITE) | ||
for i in range(GRID_SIZE): | ||
for j in range(GRID_SIZE): | ||
x = j * CELL_SIZE | ||
y = i * CELL_SIZE | ||
pygame.draw.rect(window, (0, 0, 0), (x, y, CELL_SIZE, CELL_SIZE), 1) | ||
|
||
if grid[i][j] != 0: | ||
text = font.render(str(grid[i][j]), True, (0, 0, 0)) | ||
text_rect = text.get_rect(center=(x + CELL_SIZE // 2, y + CELL_SIZE // 2)) | ||
window.blit(text, text_rect) | ||
|
||
# Highlight the given coordinates | ||
for coord in coordinates: | ||
i, j = coord | ||
pygame.draw.rect(window, (0, 255, 0), (j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE), 2) | ||
|
||
pygame.display.flip() | ||
|
||
def brute_or_backtrack(): | ||
root = tk.Tk() | ||
root.withdraw() | ||
choice = simpledialog.askstring("Solving Method", "Enter 1 for the Brute Force solution or 2 for the Backtrack solution:") | ||
try: | ||
choice = int(choice) | ||
if choice == 1 or choice == 2: | ||
return choice | ||
else: | ||
raise ValueError("Invalid choice") | ||
except (ValueError, TypeError): | ||
return None | ||
|
||
def execute(): | ||
# Create a Tkinter root window (it will be hidden) | ||
root = tk.Tk() | ||
root.withdraw() # Hide the main root window | ||
|
||
# Use a file dialog to select a file | ||
file_name = filedialog.askopenfilename(filetypes=[("Text Files", "*.txt")]) | ||
|
||
if file_name: | ||
if os.path.exists(file_name): | ||
sudoku = file_holder(file_name) | ||
coordinates = get_coordinates(sudoku) | ||
|
||
solver_choice = brute_or_backtrack() | ||
|
||
if solver_choice == 1: | ||
solver = BruteForce(sudoku) | ||
else: | ||
solver = Backtracking(sudoku) | ||
|
||
running = True | ||
while running: | ||
for event in pygame.event.get(): | ||
if event.type == pygame.QUIT: | ||
running = False | ||
if solver.solve(sudoku): | ||
draw_grid(solver.grid, coordinates) | ||
|
||
if __name__ == "__main__": | ||
pygame.init() | ||
execute() | ||
pygame.quit() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
_729___3_ | ||
__1__6_8_ | ||
____4__6_ | ||
96___41_8 | ||
_487_5_96 | ||
__56_8__3 | ||
___4_2_1_ | ||
85__6_327 | ||
1__85____ |