diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..13566b81b
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/Sudoku-Project.iml b/.idea/Sudoku-Project.iml
new file mode 100644
index 000000000..8a05c6ed5
--- /dev/null
+++ b/.idea/Sudoku-Project.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 000000000..ae089e89e
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 000000000..105ce2da2
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..e557d17a7
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..4de2d0149
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/board.py b/board.py
new file mode 100644
index 000000000..a4843342d
--- /dev/null
+++ b/board.py
@@ -0,0 +1,98 @@
+import pygame
+from pygame.examples.moveit import WIDTH
+from constants import *
+
+from cell import Cell
+
+class Board:
+ def __init__(self, width, height, screen, difficulty):
+ self.width = width
+ self.height = height
+ self.screen = screen
+ self.difficulty = difficulty
+ self.cells = [[Cell(0, i, j, screen) for j in range(9)] for i in range(9)]
+ self.current = None
+ self.original_values = [[cell.value for cell in row] for row in self.cells]
+
+ def draw(self):
+ for row in range(10):
+ thickness = 3 if row % 3 == 0 else 1
+ pygame.draw.line(self.screen, (0, 0, 0), (0, row * cell_size), (WIDTH, row * cell_size), thickness)
+ pygame.draw.line(self.screen, (0, 0, 0), (row * cell_size, 0), (row * cell_size, WIDTH), thickness)
+ for row in self.cells:
+ for cell in row:
+ cell.draw()
+
+ def select(self, row, col):
+ if self.current:
+ self.current.selected = False
+
+ if self.cells[row][col].editable:
+ self.cells[row][col].selected = True
+ self.current = self.cells[row][col]
+ else:
+ self.current = None
+
+ def click(self, x, y):
+ if x < self.width and y < self.height:
+ row = y // cell_size
+ col = x // cell_size
+ self.select(row, col)
+ return row, col
+ return None
+
+ def clear(self):
+ if self.current and self.current.value == 0:
+ self.current.set_sketched_value(0)
+
+ def sketch(self, value):
+ if self.current and self.current.value == 0:
+ self.current.set_sketched_value(value)
+
+ def place_number(self, value):
+ if self.current and self.current.value == 0:
+ self.current.set_cell_value(value)
+ self.current.set_sketched_value(0)
+
+ def reset_to_original(self):
+ for i in range(9):
+ for j in range(9):
+ if self.cells[i][j].editable:
+ self.cells[i][j].set_cell_value(self.original_values[i][j])
+ self.cells[i][j].set_sketched_value(0)
+
+ def is_full(self):
+ for row in self.cells:
+ for cell in row:
+ if cell.value == 0:
+ return False
+ return True
+
+ def update_board(self):
+ board = [[cell.value for cell in row] for row in self.cells]
+ return board
+
+ def find_empty(self):
+ for row in range(9):
+ for col in range(9):
+ if self.cells[row][col].value == 0:
+ return row, col
+ return None
+
+ def check_board(self):
+ for row in self.cells:
+ if not self._is_valid_group([cell.value for cell in row]):
+ return False
+ for col in range(9):
+ if not self._is_valid_group([self.cells[row][col].value for row in range(9)]):
+ return False
+ for i in range(0, 9, 3):
+ for j in range(0, 9, 3):
+ subgrid = [self.cells[x][y].value for x in range(i, i + 3) for y in range(j, j + 3)]
+ if not self._is_valid_group(subgrid):
+ return False
+ return True
+
+ def _is_valid_group(self, values):
+ nums = [v for v in values if v != 0]
+ return len(nums) == len(set(nums))
\ No newline at end of file
diff --git a/cell.py b/cell.py
new file mode 100644
index 000000000..132f797a4
--- /dev/null
+++ b/cell.py
@@ -0,0 +1,35 @@
+import pygame
+from constants import *
+
+class Cell:
+ def __init__(self, value, row, col, screen):
+ self.value = value
+ self.row = row
+ self.col = col
+ self.screen = screen
+ self.selected = False
+ self.sketched_val = None
+ self.editable = True
+
+ def set_cell_value(self, value):
+ self.value = value
+
+ def set_sketched_value(self, value):
+ self.sketched_value = value
+
+ def draw(self):
+ cell_size = 70
+ font_num = pygame.font.Font(None, 56)
+ if self.value != 0:
+ num_surface = font_num.render(str(self.value), True, BLACK)
+ num_rectangle = num_surface.get_rect(center=(self.col * cell_size + cell_size // 2, self.row * cell_size + cell_size // 2))
+ self.screen.blit(num_surface, num_rectangle)
+ if self.selected:
+ red_square = pygame.Rect(self.col * cell_size, self.row * cell_size, cell_size, cell_size)
+ pygame.draw.rect(self.screen, (255, 0, 0), red_square, 3) # Red outline with 3 px thickness
+ if self.sketched_val is not None:
+ font_sketch = pygame.font.Font(None, 28)
+ sketch_surface = font_sketch.render(str(self.sketched_val), True, BLACK)
+ sketch_rectangle = sketch_surface.get_rect(
+ center=(100 * self.col + 50, 100 * self.row + 50))
+ self.screen.blit(sketch_surface, sketch_rectangle)
\ No newline at end of file
diff --git a/constants.py b/constants.py
new file mode 100644
index 000000000..5c3f1e758
--- /dev/null
+++ b/constants.py
@@ -0,0 +1,15 @@
+#All of these values are subject to change
+
+WIDTH = 630
+HEIGHT = 750
+LIGHT_BLUE = (0, 47, 255) #Light Blue
+PURPLE = (200, 0, 255) #Purple
+GREEN = (125, 255, 125) # Green
+RED = (250, 52, 52) # Red
+BLACK = (0, 0, 0) #Black
+PINK = (255, 10, 200) #Pink
+WHITE = (255, 255, 255)
+EASY = 30
+MEDIUM = 40
+HARD = 50
+cell_size = 70
\ No newline at end of file
diff --git a/sudoku.py b/sudoku.py
new file mode 100644
index 000000000..ff0fe0e2e
--- /dev/null
+++ b/sudoku.py
@@ -0,0 +1,372 @@
+import pygame
+import sys
+from board import Board
+from constants import *
+from sudoku_generator import SudokuGenerator
+
+
+def game_start(screen):
+ #Initialize Font
+ start_title_font = pygame.font.Font(None, 85)
+ subtitle_font = pygame.font.Font(None, 65)
+ button_font = pygame.font.Font(None, 63)
+
+ title_board = SudokuGenerator(9,35)
+ title_board.fill_values()
+ title_board.remove_cells()
+ disp = title_board.get_board()
+
+ display_board = Board(70, 70, screen, 0)
+ for i in range(9):
+ for j in range(9):
+ display_board.cells[i][j].value = disp[i][j]
+ if disp[i][j] != 0:
+ display_board.cells[i][j].editable = False
+
+ #Main title text
+ screen.fill(PINK)
+ display_board.draw()
+ main_title_surface = start_title_font.render("Welcome to Sudoku", True, PURPLE)
+ main_title_rectangle = main_title_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 100))
+ screen.blit(main_title_surface, main_title_rectangle)
+
+ #Subtitle text
+ subtitle_surface = subtitle_font.render("Select Game Mode", True, PURPLE)
+ subtitle_rectangle = subtitle_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 200))
+ screen.blit(subtitle_surface, subtitle_rectangle)
+
+ #Outline setup
+ main_title_outline = start_title_font.render("Welcome to Sudoku", 0, WHITE)
+ for i in range(-4, 5):
+ for j in range(-4, 5):
+ screen.blit(main_title_outline, main_title_surface.get_rect(center=(WIDTH // 2 + i, HEIGHT // 2 - 100 + j)))
+ screen.blit(main_title_surface, main_title_rectangle)
+ subtitle_outline = subtitle_font.render("Select Game Mode", 0, WHITE)
+ for i in range(-4, 5):
+ for j in range(-4, 5):
+ screen.blit(subtitle_outline, subtitle_surface.get_rect(center=(WIDTH // 2 + i, HEIGHT // 2 + 200 + j)))
+ screen.blit(subtitle_surface, subtitle_rectangle)
+
+ #Main menu buttons
+ button_data = [("Easy", WIDTH // 2+200), ("Medium", WIDTH // 2), ("Hard", WIDTH // 2 - 200)]
+ buttons = []
+
+ for text, x_pos in button_data:
+ button_text = button_font.render(text, True, WHITE)
+ button_surface = pygame.Surface((button_text.get_width() + 14, button_text.get_height() + 14))
+ button_surface.fill(PURPLE)
+ button_surface.blit(button_text, (10, 10))
+ button_rectangle = button_surface.get_rect(center=(x_pos, HEIGHT // 2 + 300))
+ buttons.append((button_surface, button_rectangle, text.lower()))
+ button_ot_surface = pygame.Surface((button_text.get_width() + 21, button_text.get_height() + 21))
+ button_ot_surface.fill(BLACK)
+ screen.blit(button_ot_surface, button_rectangle)
+ screen.blit(button_surface, button_rectangle)
+
+ while True:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ sys.exit()
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ for _, button_rect, difficulty in buttons:
+ if button_rect.collidepoint(event.pos):
+ return difficulty
+ pygame.display.update()
+
+def during_game(screen, display_board, difficulty):
+
+ #Font
+ bottom_button_font = pygame.font.Font(None, 50)
+ small_msg_font = pygame.font.Font(None, 40)
+
+ #Difficulty text
+ difficulty = difficulty.upper()
+ small_msg_surface = small_msg_font.render(difficulty, 0, BLACK)
+ small_msg_rectangle = small_msg_surface.get_rect(center=(WIDTH // 2 - 370, HEIGHT // 2 + 420))
+
+ #Init text
+ reset_text = bottom_button_font.render("RESET", 0, BLACK)
+ restart_text = bottom_button_font.render("RESTART", 0, BLACK)
+ exit_text = bottom_button_font.render("EXIT", 0, BLACK)
+
+ #Init reset button
+ reset_surface = pygame.Surface((reset_text.get_size()[0] + 20, reset_text.get_size()[1] + 20))
+ reset_surface.fill(PINK)
+ reset_surface.blit(reset_text, (10, 10))
+
+ #Init restart button
+ restart_surface = pygame.Surface((restart_text.get_size()[0] + 20, restart_text.get_size()[1] + 20))
+ restart_surface.fill(PINK)
+ restart_surface.blit(restart_text, (10, 10))
+
+ #Init exit button
+ exit_surface = pygame.Surface((exit_text.get_size()[0] + 20, exit_text.get_size()[1] + 20))
+ exit_surface.fill(PINK)
+ exit_surface.blit(exit_text, (10, 10))
+
+ #Set button locations
+ reset_rectangle = reset_surface.get_rect(center=(WIDTH // 2 + 200, HEIGHT - 50))
+ restart_rectangle = restart_surface.get_rect(center=(WIDTH // 2, HEIGHT - 50))
+ exit_rectangle = exit_surface.get_rect(center=(WIDTH // 2 - 200, HEIGHT - 50))
+
+ #Outline setup
+ reset_ot_surface = pygame.Surface((reset_text.get_size()[0] + 30, reset_text.get_size()[1] + 30))
+ reset_ot_surface.fill(BLACK)
+ restart_ot_surface = pygame.Surface((restart_text.get_size()[0] + 30, restart_text.get_size()[1] + 30))
+ restart_ot_surface.fill(BLACK)
+ exit_ot_surface = pygame.Surface((exit_text.get_size()[0] + 30, exit_text.get_size()[1] + 30))
+ exit_ot_surface.fill(BLACK)
+ clicked_row, clicked_col = -1, -1
+
+ while True:
+ screen.fill(PURPLE)
+ display_board.draw()
+
+ if clicked_row != -1 and clicked_col != -1:
+ display_board.select(clicked_row, clicked_col)
+ screen.blit(reset_ot_surface, reset_rectangle)
+ screen.blit(restart_ot_surface, restart_rectangle)
+ screen.blit(exit_ot_surface, exit_rectangle)
+ screen.blit(reset_surface, reset_rectangle)
+ screen.blit(restart_surface, restart_rectangle)
+ screen.blit(exit_surface, exit_rectangle)
+ screen.blit(small_msg_surface, small_msg_rectangle)
+
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ sys.exit()
+
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ if reset_rectangle.collidepoint(event.pos):
+ display_board.reset_to_original()
+ display_board.draw()
+ elif restart_rectangle.collidepoint(event.pos):
+ main()
+ elif exit_rectangle.collidepoint(event.pos):
+ sys.exit()
+
+ if 0 <= event.pos[0] < WIDTH and 0 <= event.pos[1] < WIDTH:
+ row, col = display_board.click(event.pos[0], event.pos[1])
+ if display_board.cells[row][col].editable:
+ clicked_row, clicked_col = row, col
+ else:
+ clicked_row, clicked_col = -1, -1
+
+ if event.type == pygame.KEYDOWN:
+ if clicked_row != -1 and clicked_col != -1:
+ if event.key in (pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4, pygame.K_5,
+ pygame.K_6, pygame.K_7, pygame.K_8, pygame.K_9):
+ value = int(event.unicode)
+ if display_board.cells[clicked_row][clicked_col].editable:
+ display_board.cells[clicked_row][clicked_col].value = value
+ elif event.key == pygame.K_UP:
+ clicked_row = (clicked_row - 1) % 9
+ elif event.key == pygame.K_DOWN:
+ clicked_row = (clicked_row + 1) % 9
+ elif event.key == pygame.K_LEFT:
+ clicked_col = (clicked_col - 1) % 9
+ elif event.key == pygame.K_RIGHT:
+ clicked_col = (clicked_col + 1) % 9
+ while not display_board.cells[clicked_row][clicked_col].editable:
+ if event.key in (pygame.K_UP, pygame.K_DOWN):
+ clicked_row = (clicked_row - 1) % 9 if event.key == pygame.K_UP else (clicked_row + 1) % 9
+ if event.key in (pygame.K_LEFT, pygame.K_RIGHT):
+ clicked_col = (clicked_col - 1) % 9 if event.key == pygame.K_LEFT else (clicked_col + 1) % 9
+ if display_board.is_full():
+ if display_board.check_board():
+ draw_game_win(screen)
+ else:
+ draw_game_lose(screen)
+ pygame.display.update()
+
+def draw_game_win(screen):
+ win_font = pygame.font.Font(None, 200)
+ try_font = pygame.font.Font(None, 70)
+ button_font = pygame.font.Font(None, 80)
+ screen.fill(BLACK)
+ color = WHITE
+ b_color = WHITE
+ b_color2 = b_color
+ x_pos1 = 200
+ x_pos2 = 70
+ b_size = 50
+
+ for i in range(5):
+ if i == 0:
+ color = GREEN
+ b_color = WHITE
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ elif i == 1:
+ color = PURPLE
+ b_color = WHITE
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ b_size -= 10
+ elif i == 2:
+ color = PINK
+ b_color = BLACK
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ elif i == 3:
+ color = RED
+ b_color = BLACK
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ b_size -= 10
+ elif i == 4:
+ b_color = RED
+ b_color2 = GREEN
+ color = WHITE
+ x_pos1 += 5
+ x_pos2 += 5
+ b_size -= 10
+
+ win_surface = win_font.render("YOU", True, color)
+ win_rectangle = win_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2 - x_pos1))
+ screen.blit(win_surface, win_rectangle)
+
+ yw_surface = win_font.render("WIN!", 0, color)
+ yw_rectangle = yw_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2 - x_pos2))
+ screen.blit(yw_surface, yw_rectangle)
+
+ try_surface = try_font.render("PLAY AGAIN?", True, GREEN)
+ try_rectangle = try_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 100))
+ screen.blit(try_surface, try_rectangle)
+
+ restart_text = button_font.render("YES", True, BLACK)
+ restart_surface = pygame.Surface((restart_text.get_width() + b_size, restart_text.get_height() + b_size))
+ restart_surface.fill(b_color2)
+ restart_surface.blit(restart_text, (10, 10))
+ restart_rectangle = restart_surface.get_rect(center=(WIDTH // 2 - 100, HEIGHT // 2 + 200))
+ exit_text = button_font.render("NO", True, BLACK)
+ exit_surface = pygame.Surface((exit_text.get_width() + b_size, exit_text.get_height() + b_size))
+ exit_surface.fill(b_color)
+ exit_surface.blit(exit_text, (10, 10))
+ exit_rectangle = exit_surface.get_rect(center=(WIDTH // 2 + 100, HEIGHT // 2 + 200))
+ screen.blit(restart_surface, restart_rectangle)
+ screen.blit(exit_surface, exit_rectangle)
+
+ while True:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ sys.exit()
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ if restart_rectangle.collidepoint(event.pos):
+ main()
+ elif exit_rectangle.collidepoint(event.pos):
+ sys.exit()
+ pygame.display.update()
+
+
+def draw_game_lose(screen):
+ lose_font = pygame.font.Font(None, 200)
+ try_font = pygame.font.Font(None, 70)
+ button_font = pygame.font.Font(None, 80)
+ screen.fill(BLACK)
+ color = WHITE
+ b_color = WHITE
+ b_color2 = b_color
+ x_pos1 = 200
+ x_pos2 = 70
+ b_size = 50
+
+ for i in range(5):
+ if i == 0:
+ color = GREEN
+ b_color = WHITE
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ elif i == 1:
+ color = PURPLE
+ b_color = WHITE
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ b_size -= 10
+ elif i == 2:
+ color = PINK
+ b_color = BLACK
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ elif i == 3:
+ color = RED
+ b_color = BLACK
+ b_color2 = b_color
+ x_pos1 += 5
+ x_pos2 += 5
+ b_size -= 10
+ elif i == 4:
+ b_color = RED
+ b_color2 = GREEN
+ color = WHITE
+ x_pos1 += 5
+ x_pos2 += 5
+ b_size -= 10
+
+ lose_surface = lose_font.render("YOU", True, color)
+ lose_rectangle = lose_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2 - x_pos1))
+ screen.blit(lose_surface, lose_rectangle)
+
+ yl_surface = lose_font.render("LOSE!", 0, color)
+ yl_rectangle = yl_surface.get_rect(center=(WIDTH // 2, HEIGHT // 2 - x_pos2))
+ screen.blit(yl_surface, yl_rectangle)
+
+ try_surface = try_font.render("TRY AGAIN?", True, RED)
+ try_rectangle = try_surface.get_rect(center = (WIDTH // 2, HEIGHT // 2 + 100))
+ screen.blit(try_surface, try_rectangle)
+
+ restart_text = button_font.render("YES", True, BLACK)
+ restart_surface = pygame.Surface((restart_text.get_width() + b_size, restart_text.get_height() + b_size))
+ restart_surface.fill(b_color2)
+ restart_surface.blit(restart_text, (10, 10))
+ restart_rectangle = restart_surface.get_rect(center=(WIDTH // 2 - 100, HEIGHT // 2 + 200))
+ exit_text = button_font.render("NO", True, BLACK)
+ exit_surface = pygame.Surface((exit_text.get_width() + b_size, exit_text.get_height() + b_size))
+ exit_surface.fill(b_color)
+ exit_surface.blit(exit_text, (10, 10))
+ exit_rectangle = exit_surface.get_rect(center=(WIDTH // 2 + 100, HEIGHT // 2 + 200))
+ screen.blit(restart_surface, restart_rectangle)
+ screen.blit(exit_surface, exit_rectangle)
+
+ while True:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ sys.exit()
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ if restart_rectangle.collidepoint(event.pos):
+ main()
+ elif exit_rectangle.collidepoint(event.pos):
+ sys.exit()
+ pygame.display.update()
+
+def main():
+ pygame.init()
+ screen = pygame.display.set_mode((WIDTH, HEIGHT))
+ pygame.display.set_caption("Sudoku")
+ difficulty = game_start(screen)
+
+ removed_cells = {"easy": 30, "medium": 40, "hard": 50}
+ sudoku = SudokuGenerator(9, removed_cells[difficulty])
+ sudoku.fill_values()
+ sudoku.remove_cells()
+ board_state = sudoku.get_board()
+
+ display_board = Board(WIDTH, WIDTH, screen, removed_cells[difficulty])
+ for i in range(9):
+ for j in range(9):
+ display_board.cells[i][j].value = board_state[i][j]
+ if board_state[i][j] != 0:
+ display_board.cells[i][j].editable = False
+
+ display_board.draw()
+ during_game(screen, display_board, difficulty)
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/sudoku_generator.py b/sudoku_generator.py
index 7ec18a597..168fa2896 100644
--- a/sudoku_generator.py
+++ b/sudoku_generator.py
@@ -1,10 +1,5 @@
-import math,random
+import random
-"""
-This was adapted from a GeeksforGeeks article "Program for Sudoku Generator" by Aarti_Rathi and Ankur Trisal
-https://www.geeksforgeeks.org/program-sudoku-generator/
-
-"""
class SudokuGenerator:
'''
@@ -14,7 +9,6 @@ class SudokuGenerator:
self.removed_cells - the total number of cells to be removed
self.board - a 2D list of ints to represent the board
self.box_length - the square root of row_length
-
Parameters:
row_length is the number of rows/columns of the board (always 9 for this project)
removed_cells is an integer value - the number of cells to be removed
@@ -22,17 +16,22 @@ class SudokuGenerator:
Return:
None
'''
+
def __init__(self, row_length, removed_cells):
- pass
+ self.row_length = row_length
+ self.removed_cells = removed_cells
+ self.board = [[0 for row in range(row_length)] for col in range(row_length)]
+ self.box_length = int(row_length ** 0.5)
'''
- Returns a 2D python list of numbers which represents the board
+ Returns a 2D python list of numbers which represents the board
Parameters: None
Return: list[list]
'''
+
def get_board(self):
- pass
+ return self.board
'''
Displays the board to the console
@@ -41,8 +40,12 @@ def get_board(self):
Parameters: None
Return: None
'''
+
def print_board(self):
- pass
+ for row in self.board:
+ for col in row:
+ print(col, end=" ")
+ print()
'''
Determines if num is contained in the specified row (horizontal) of the board
@@ -51,11 +54,14 @@ def print_board(self):
Parameters:
row is the index of the row we are checking
num is the value we are looking for in the row
-
+
Return: boolean
'''
+
def valid_in_row(self, row, num):
- pass
+ if num in self.board[row]:
+ return False
+ return True
'''
Determines if num is contained in the specified column (vertical) of the board
@@ -64,11 +70,15 @@ def valid_in_row(self, row, num):
Parameters:
col is the index of the column we are checking
num is the value we are looking for in the column
-
+
Return: boolean
'''
+
def valid_in_col(self, col, num):
- pass
+ for row in self.board:
+ if row[col] == num:
+ return False
+ return True
'''
Determines if num is contained in the 3x3 box specified on the board
@@ -82,9 +92,14 @@ def valid_in_col(self, col, num):
Return: boolean
'''
+
def valid_in_box(self, row_start, col_start, num):
- pass
-
+ for row in range(row_start, row_start + 3):
+ for col in range(col_start, col_start + 3):
+ if self.board[row][col] == num:
+ return False
+ return True
+
'''
Determines if it is valid to enter num at (row, col) in the board
This is done by checking that num is unused in the appropriate, row, column, and box
@@ -95,8 +110,16 @@ def valid_in_box(self, row_start, col_start, num):
Return: boolean
'''
+
def is_valid(self, row, col, num):
- pass
+ row_start = (row // 3) * 3
+ col_start = (col // 3) * 3
+
+ if self.valid_in_box(row_start, col_start, num):
+ if self.valid_in_row(row, num):
+ if self.valid_in_col(col, num):
+ return True
+ return False
'''
Fills the specified 3x3 box with values
@@ -108,9 +131,21 @@ def is_valid(self, row, col, num):
Return: None
'''
+
def fill_box(self, row_start, col_start):
- pass
-
+ nums_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ random.shuffle(nums_list)
+
+ for row in range(row_start, row_start + 3):
+ for col in range(col_start, col_start + 3):
+ for num in nums_list:
+ if self.valid_in_box(row_start, col_start, num) and self.valid_in_col(col,
+ num) and self.valid_in_row(
+ row, num):
+ self.board[row][col] = num
+ nums_list.remove(num)
+ break
+
'''
Fills the three boxes along the main diagonal of the board
These are the boxes which start at (0,0), (3,3), and (6,6)
@@ -118,21 +153,24 @@ def fill_box(self, row_start, col_start):
Parameters: None
Return: None
'''
+
def fill_diagonal(self):
- pass
+ for start in range(0, self.row_length, 3):
+ self.fill_box(start, start)
'''
DO NOT CHANGE
Provided for students
Fills the remaining cells of the board
Should be called after the diagonal boxes have been filled
-
+
Parameters:
row, col specify the coordinates of the first empty (0) cell
Return:
boolean (whether or not we could solve the board)
'''
+
def fill_remaining(self, row, col):
if (col >= self.row_length and row < self.row_length - 1):
row += 1
@@ -151,7 +189,7 @@ def fill_remaining(self, row, col):
col = 0
if row >= self.row_length:
return True
-
+
for num in range(1, self.row_length + 1):
if self.is_valid(row, col, num):
self.board[row][col] = num
@@ -168,6 +206,7 @@ def fill_remaining(self, row, col):
Parameters: None
Return: None
'''
+
def fill_values(self):
self.fill_diagonal()
self.fill_remaining(0, self.box_length)
@@ -177,15 +216,26 @@ def fill_values(self):
This is done by setting some values to 0
Should be called after the entire solution has been constructed
i.e. after fill_values has been called
-
+
NOTE: Be careful not to 'remove' the same cell multiple times
i.e. if a cell is already 0, it cannot be removed again
Parameters: None
Return: None
'''
+
def remove_cells(self):
- pass
+ removed = 0
+ while removed < self.removed_cells:
+ row = random.randint(0, self.row_length - 1)
+ col = random.randint(0, self.row_length - 1)
+
+ if self.board[row][col] == 0:
+ continue
+
+ self.board[row][col] = 0
+ removed += 1
+
'''
DO NOT CHANGE
@@ -202,10 +252,12 @@ def remove_cells(self):
Return: list[list] (a 2D Python list to represent the board)
'''
+
+
def generate_sudoku(size, removed):
sudoku = SudokuGenerator(size, removed)
sudoku.fill_values()
board = sudoku.get_board()
sudoku.remove_cells()
board = sudoku.get_board()
- return board
+ return board
\ No newline at end of file