Skip to content

Commit 52a7ea3

Browse files
committed
first commit
0 parents  commit 52a7ea3

21 files changed

+423
-0
lines changed

.idea/.gitignore

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/profiles_settings.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/sudokuSolver-main.iml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
1.39 KB
Binary file not shown.

__pycache__/bruteForce.cpython-38.pyc

1.12 KB
Binary file not shown.

__pycache__/main.cpython-38.pyc

1.83 KB
Binary file not shown.

__pycache__/methods.cpython-38.pyc

3.57 KB
Binary file not shown.

backtracking.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
class Backtracking:
2+
3+
def __init__(self, grid):
4+
self.grid = grid
5+
6+
@staticmethod
7+
def isEmpty(grid):
8+
for i in range(len(grid)):
9+
for j in range(len(grid[0])):
10+
if grid[i][j] == 0:
11+
return (i, j)
12+
13+
def isValid(self, grid, nb, pos):
14+
for i in range(len(grid)):
15+
if grid[i][pos[1]] == nb and pos[0] != i:
16+
return False
17+
for i in range(len(grid[0])):
18+
if grid[pos[0]][i] == nb and pos[1] != i:
19+
return False
20+
subgrid_col_index = pos[1] // 3
21+
subgrid_row_index = pos[0] // 3
22+
for i in range(subgrid_row_index * 3, subgrid_row_index * 3 + 3):
23+
for j in range(subgrid_col_index * 3, subgrid_col_index * 3 + 3):
24+
if grid[i][j] == nb and (i, j) != pos:
25+
return False
26+
return True
27+
28+
def solve(self, grid):
29+
empty = self.isEmpty(grid)
30+
if not empty:
31+
return True
32+
else:
33+
row, col = empty
34+
for i in range(1, 10):
35+
if self.isValid(grid, i, (row, col)):
36+
grid[row][col] = i
37+
if self.solve(grid):
38+
return True
39+
grid[row][col] = 0
40+
return False

benchmarking.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os, time
2+
import numpy as np
3+
from backtracking import Backtracking
4+
from bruteForce import BruteForce
5+
6+
def file_holder(file_name):
7+
with open(file_name, 'r') as f:
8+
file = f.read()
9+
file = file.replace(' ', '').replace('\n', '').replace('_', "0")
10+
return (np.array(list(file)).astype(int)).reshape((9, 9))
11+
def benchmark_all_puzzles():
12+
puzzle_directory = "./sudoku_puzzles"
13+
puzzle_files = os.listdir(puzzle_directory)
14+
brute_force_times = []
15+
backtracking_times = []
16+
17+
for puzzle_file in puzzle_files:
18+
puzzle_path = os.path.join(puzzle_directory, puzzle_file)
19+
20+
if os.path.isfile(puzzle_path):
21+
sudoku = file_holder(puzzle_path)
22+
back_or_brute = input("Select 1 for the Brute Force solution or 2 for the Backtrack solution: ")
23+
24+
if back_or_brute == '1':
25+
solver = BruteForce(sudoku)
26+
else:
27+
solver = Backtracking(sudoku)
28+
29+
start_time = time.time()
30+
if solver.solve(sudoku):
31+
end_time = time.time()
32+
execution_time = end_time - start_time
33+
34+
print(f"Solved {puzzle_file} in {execution_time} seconds")
35+
36+
if back_or_brute == '1':
37+
brute_force_times.append(execution_time)
38+
else:
39+
backtracking_times.append(execution_time)
40+
else:
41+
print(f"No solution found for {puzzle_file}")
42+
#calculate and print average execution times for all puzzles
43+
if brute_force_times:
44+
avg_brute_force_time = sum(brute_force_times) / len(brute_force_times)
45+
print(f"Average Brute Force execution time: {avg_brute_force_time} seconds")
46+
47+
if backtracking_times:
48+
avg_backtracking_time = sum(backtracking_times) / len(backtracking_times)
49+
print(f"Average Backtracking execution time: {avg_backtracking_time} seconds")
50+
51+
52+
benchmark_all_puzzles()

bruteForce.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
class BruteForce:
3+
def __init__(self, grid):
4+
self.grid = grid
5+
self.counter = 0
6+
7+
def solve(self, grid):
8+
for i in range(9):
9+
for j in range(9):
10+
if grid[i][j] == 0:
11+
for k in range(1, 10):
12+
if self.check(grid, i, j, k):
13+
grid[i][j] = k
14+
self.counter += 1
15+
if self.solve(grid):
16+
return True
17+
else:
18+
grid[i][j] = 0
19+
self.counter -= 1
20+
return False
21+
return True
22+
23+
def check(self, grid, row, column, num):
24+
if num in grid[row]:
25+
return False
26+
for i in range(9):
27+
if grid[i][column] == num:
28+
return False
29+
30+
x = (row - (row % 3))
31+
y = (column - (column % 3))
32+
33+
for i in range(3):
34+
for j in range(3):
35+
if (i, j) == (row, column):
36+
continue
37+
if grid[x + j][y + i] == num:
38+
return False
39+
return True

main.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import os
2+
import numpy as np
3+
from backtracking import Backtracking
4+
from bruteForce import BruteForce
5+
6+
7+
def file_holder(file_name):
8+
with open(file_name, 'r') as f:
9+
file = f.read()
10+
file = file.replace(' ', '').replace('\n', '').replace('_', "0")
11+
return (np.array(list(file)).astype(int)).reshape((9, 9))
12+
13+
14+
def get_coordinates(grid):
15+
coordinates = []
16+
for i in range(len(grid)):
17+
for j in range(len(grid[i])):
18+
if grid[i][j] > 0:
19+
coordinates.append((i, j))
20+
return coordinates
21+
22+
23+
def print_grid(grid, coordinates):
24+
hsep = "\033[34m" + " _______________________" + "\033[0m"
25+
vsep = "\033[34m" + "|" + "\033[0m"
26+
print(hsep)
27+
for i in range(len(grid)):
28+
print(vsep, end=" ")
29+
for j in range(len(grid[i])):
30+
current = str(grid[i][j])
31+
if (i, j) in coordinates:
32+
current = f"\033[32m{current}\033[0m" # Set text color to green
33+
if j % 3 == 0 and j != 0:
34+
print(vsep, end=" ")
35+
print(current, end=" ")
36+
print(vsep, end=" ")
37+
print("")
38+
if i != 8 and i % 3 == 2:
39+
print(hsep)
40+
return hsep
41+
42+
43+
def execute():
44+
while True:
45+
file_name = input("Please enter a valid file (.txt): ")
46+
if os.path.exists(file_name):
47+
sudoku = file_holder(file_name)
48+
coordinates = get_coordinates(sudoku)
49+
# Use Brute Force or Backtracking solver
50+
back_or_brute = input("Select 1 for the Brute Force solution or 2 for the Backtrack solution: ")
51+
if back_or_brute == 1:
52+
solver = BruteForce(sudoku)
53+
else:
54+
solver = Backtracking(sudoku)
55+
if solver.solve(sudoku):
56+
print(print_grid(solver.grid, coordinates))
57+
else:
58+
print("No solution found.")
59+
choice = input("Do you want to solve another one? (y/n) ")
60+
if choice.lower() not in ['y', 'yes']:
61+
print("See ya!")
62+
break
63+
64+
execute()
65+
66+

pygame_sudoku.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import pygame
2+
import os
3+
import numpy as np
4+
from backtracking import Backtracking
5+
from bruteForce import BruteForce
6+
import tkinter as tk
7+
from tkinter import filedialog, simpledialog
8+
9+
def file_holder(file_name):
10+
with open(file_name, 'r') as f:
11+
file = f.read()
12+
file = file.replace(' ', '').replace('\n', '').replace('_', "0")
13+
return (np.array(list(file)).astype(int)).reshape((9, 9))
14+
15+
16+
def get_coordinates(grid):
17+
coordinates = []
18+
for i in range(len(grid)):
19+
for j in range(len(grid[i])):
20+
if grid[i][j] > 0:
21+
coordinates.append((i, j))
22+
return coordinates
23+
24+
## initialize Pygame
25+
pygame.init()
26+
WIDTH, HEIGHT = 600, 600
27+
GRID_SIZE = 9
28+
CELL_SIZE = WIDTH // GRID_SIZE
29+
FPS = 60
30+
WHITE = (255, 255, 255)
31+
FONT_SIZE = 36
32+
33+
#create a Pygame window
34+
window = pygame.display.set_mode((WIDTH, HEIGHT))
35+
pygame.display.set_caption("Sudoku Solver")
36+
font = pygame.font.Font(None, FONT_SIZE)
37+
38+
def draw_grid(grid, coordinates):
39+
window.fill(WHITE)
40+
for i in range(GRID_SIZE):
41+
for j in range(GRID_SIZE):
42+
x = j * CELL_SIZE
43+
y = i * CELL_SIZE
44+
pygame.draw.rect(window, (0, 0, 0), (x, y, CELL_SIZE, CELL_SIZE), 1)
45+
46+
if grid[i][j] != 0:
47+
text = font.render(str(grid[i][j]), True, (0, 0, 0))
48+
text_rect = text.get_rect(center=(x + CELL_SIZE // 2, y + CELL_SIZE // 2))
49+
window.blit(text, text_rect)
50+
51+
# Highlight the given coordinates
52+
for coord in coordinates:
53+
i, j = coord
54+
pygame.draw.rect(window, (0, 255, 0), (j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE), 2)
55+
56+
pygame.display.flip()
57+
58+
def brute_or_backtrack():
59+
root = tk.Tk()
60+
root.withdraw()
61+
choice = simpledialog.askstring("Solving Method", "Enter 1 for the Brute Force solution or 2 for the Backtrack solution:")
62+
try:
63+
choice = int(choice)
64+
if choice == 1 or choice == 2:
65+
return choice
66+
else:
67+
raise ValueError("Invalid choice")
68+
except (ValueError, TypeError):
69+
return None
70+
71+
def execute():
72+
# Create a Tkinter root window (it will be hidden)
73+
root = tk.Tk()
74+
root.withdraw() # Hide the main root window
75+
76+
# Use a file dialog to select a file
77+
file_name = filedialog.askopenfilename(filetypes=[("Text Files", "*.txt")])
78+
79+
if file_name:
80+
if os.path.exists(file_name):
81+
sudoku = file_holder(file_name)
82+
coordinates = get_coordinates(sudoku)
83+
84+
solver_choice = brute_or_backtrack()
85+
86+
if solver_choice == 1:
87+
solver = BruteForce(sudoku)
88+
else:
89+
solver = Backtracking(sudoku)
90+
91+
running = True
92+
while running:
93+
for event in pygame.event.get():
94+
if event.type == pygame.QUIT:
95+
running = False
96+
if solver.solve(sudoku):
97+
draw_grid(solver.grid, coordinates)
98+
99+
if __name__ == "__main__":
100+
pygame.init()
101+
execute()
102+
pygame.quit()

sudoku1.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
_729___3_
2+
__1__6_8_
3+
____4__6_
4+
96___41_8
5+
_487_5_96
6+
__56_8__3
7+
___4_2_1_
8+
85__6_327
9+
1__85____

0 commit comments

Comments
 (0)