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