diff --git a/board.py b/board.py new file mode 100644 index 00000000..a005fae4 --- /dev/null +++ b/board.py @@ -0,0 +1,195 @@ +import pygame +from constants import * +from cell import Cell +from sudoku_generator import SudokuGenerator + + +class Board: + def __init__(self, width, height, screen, difficulty): + self.board = [[0 for _ in range(9)] for _ in range(9)] + self.width = 750 + self.height = 900 + self.screen = pygame.display.set_mode((width, height)) + self.difficulty = difficulty + self.board_rows = 9 + self.board_cols = 9 + self.board_line_width = 5 + self.cell_size = 82 + + + + # difficulty_levels = {"easy": 30, "medium": 40, "hard": 50} + # if self.difficulty in difficulty_levels: + # self.empty_cells = difficulty_levels[self.difficulty] + # else: + # raise ValueError("Invalid difficulty level") + + self.original = [row[:] for row in self.board] + self.cells = [[Cell(self.board[row][col], row, col, self.screen) + for col in range(self.board_cols)] + for row in range(self.board_rows)] + self.selected = (0, 0) + def draw(self): + # Calculate the width and height of the board + BOARD_WIDTH = 9 * CELL_SIZE + BOARD_HEIGHT = 9 * CELL_SIZE + + # Calculate the starting position + board_start_x = (WIDTH - BOARD_WIDTH) // 2 + board_start_y = (HEIGHT - BOARD_HEIGHT) // 2 - 70 # move board up ~ 70 px + + # Draw the cells + for row in self.cells: + for cell in row: + cell_rect = pygame.Rect(board_start_x + cell.col * CELL_SIZE, board_start_y + cell.row * CELL_SIZE, + CELL_SIZE, CELL_SIZE) + pygame.draw.rect(self.screen, SCREEN_COLOR, cell_rect) + cell.draw(self.screen) + + # Draw the squares for the board + for i in range(0, 4): + pygame.draw.line(self.screen, BLACK, + (board_start_x, board_start_y + (3 * i) * CELL_SIZE), + (board_start_x + BOARD_WIDTH, board_start_y + (3 * i) * CELL_SIZE), + BOARD_LINE_WIDTH) + + for i in range(0, 4): + pygame.draw.line(self.screen, BLACK, + (board_start_x + (3 * i) * CELL_SIZE, board_start_y), + (board_start_x + (3 * i) * CELL_SIZE, board_start_y + BOARD_HEIGHT), + BOARD_LINE_WIDTH) + + + + # Marks the cell at (row, col) in the board as the current selected cell + def select(self, row, col): + for i in range(self.board_rows): + for j in range(self.board_cols): + if i == row and j == col: + self.cells[i][j].selected = True + else: + self.cells[i][j].selected = False + + # returns a tuple of the (row, col) of the cell which was clicked + def click(self, x, y): + # Calculate the width and height of the board + BOARD_WIDTH = 9 * CELL_SIZE + BOARD_HEIGHT = 9 * CELL_SIZE + + # Calculate the starting position + board_start_x = (WIDTH - BOARD_WIDTH) // 2 + board_start_y = (HEIGHT - BOARD_HEIGHT) // 2 - 70 # move board up ~ 70 px + + # if coordinates inside the board (which calculated by adding board's width and height to the starting points) + if board_start_x <= x < board_start_x + BOARD_WIDTH and board_start_y <= y < board_start_y + BOARD_HEIGHT: + clicked_row = (y - board_start_y) // CELL_SIZE + clicked_col = (x - board_start_x) // CELL_SIZE + return clicked_row, clicked_col + + return None + + # allows user to remove the cell values and sketched value that are filled by themselves + def clear(self): + for i in range(self.board_rows): + for j in range(self.board_cols): + if self.cells[i][j].selected: + # check if it's an empty cell in the original board + if self.board[i][j] == 0: + self.cells[i][j].sketched_value = 0 + self.cells[i][j].value = 0 + + def sketch(self, value): + for row in range(self.board_rows): + for col in range(self.board_cols): + if self.cells[col][row].selected: + if self.board[col][row] == 0: + self.cells[col][row].set_sketched_value(value) + # sketched_val = str(value) + # font = pygame.font.SysFont('Times New Roman', 100) + # number_print = font.render(sketched_val, True, 'Grey') + # self.screen.blit(number_print, (selected_cell[0], selected_cell[1])) + # pygame.display.update() + + def place_number(self, value): + #self.value = value + #sketched_val = str(self.value) + for row in range(self.board_rows): + for col in range(self.board_cols): + if self.cells[col][row].selected: + if self.board[col][row] == 0: + self.cells[col][row].set_cell_value(value) + self.cells[col][row].set_sketched_value(0) + self.board[col][row] = value # Update the board + self.draw() # Refresh the board display + # x = selected_cell[0] * 82 + # y = selected_cell[1] * 82 + # font = pygame.font.SysFont('Times New Roman', 50) + # number_print = font.render(sketched_val, True, 'Black') + # for event in pygame.event.get(): + # if event.type == pygame.KEYDOWN: + # if event.key == pygame.K_RETURN: + # self.screen.blit(number_print, (x, y)) + + def reset_to_original(self): + + for row in range(self.board_rows): + for col in range(self.board_cols): + if self.original[row][col] == 0: # If the cell was empty originally + self.board[row][col] = 0 + + # Recreate the cells to reflect the reset + self.cells = [[Cell(self.board[row][col], row, col, self.screen) + for col in range(self.board_cols)] + for row in range(self.board_rows)] + + return self.cells + + def is_full(self): + # self.board = [[0 for _ in range(9)] for _ in range(9)] + for row in self.board: + for cell in row: + if cell == 0: + return False + return True + + def update_board(self): + # for row in self.board: + # for col in self.board: + # font = pygame.font.SysFont('Times New Roman', 50) + # number_print = font.render(str(self.value), True, 'Black') + # x = col * 82 + # y = row * 82 + # self.screen.blit(number_print, (x, y)) + # pygame.display.update() + for row in range(self.board_rows): + for col in range(self.board_cols): + if self.cells[col][row].sketched_value != 0: + self.cells[col][row].value = self.cells[col][row].sketched_value + + def find_empty(self): + # loop through cells and find empty, then return row and col as tuple + for row in self.board: + for col in self.board: + if self.board[row][col] == '0': + tup = (row, col) + return tup + + def check_board(self): + def valid(values): + values = [v for v in values if v != 0] + return sorted(values) == list(range(1, 10)) + + for row in range(self.board_rows): + if not valid(self.board[row]): + return False + for col in range(self.board_cols): + if not valid(self.board[col]): + return False + for box_row in range(0, 9, 3): + for box_col in range(0, 9, 3): + box = [self.board[row][col] + for row in range(box_row, box_row + 3) + for col in range(box_col, box_col + 3)] + if not valid(box): + return False + return True \ No newline at end of file diff --git a/cell.py b/cell.py new file mode 100644 index 00000000..8eb5278c --- /dev/null +++ b/cell.py @@ -0,0 +1,55 @@ +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.sketched_value = 0 + self.selected = False + + + def set_cell_value(self, value): + self.value = value + + def set_sketched_value(self, value): + self.sketched_value = value + + def draw(self, screen): + # Calculate the width and height of the Sudoku board + BOARD_WIDTH = 9 * CELL_SIZE + BOARD_HEIGHT = 9 * CELL_SIZE + + # Calculate the starting position to center the board + board_start_x = (WIDTH - BOARD_WIDTH) // 2 + board_start_y = (HEIGHT - BOARD_HEIGHT) // 2 - 70 # move board up ~ 70 px + + # Draw the cell rectangle + cell_rect = pygame.Rect(board_start_x + self.col * CELL_SIZE, board_start_y + self.row * CELL_SIZE, CELL_SIZE, + CELL_SIZE) + pygame.draw.rect(screen, BLACK, cell_rect, CELL_LINE_WIDTH) + + # Draw a thicker border for the cell if selected + if self.selected: + pygame.draw.rect(screen, BLUE, cell_rect, 6) + + # Draw the cell value (if not zero) + if self.value != 0 and self.sketched_value == 0: + cell_font = pygame.font.Font(None, 55) + cell_surf = cell_font.render(str(self.value), True, BLACK) + cell_rect = cell_surf.get_rect( + center=(board_start_x + self.col * CELL_SIZE + CELL_SIZE // 2, + board_start_y + self.row * CELL_SIZE + CELL_SIZE // 2)) + screen.blit(cell_surf, cell_rect) + # Draw the value using user input + if self.sketched_value != 0: + cell_font = pygame.font.Font(None, 50) + cell_surf = cell_font.render(str(self.sketched_value), True, USER_NUMBER_COLOR) + # draw the value in the upper left corner (not center) + cell_rect = cell_surf.get_rect( + center=((board_start_x + self.col * CELL_SIZE + CELL_SIZE // 2) - 12, + (board_start_y + self.row * CELL_SIZE + CELL_SIZE // 2) - 12)) + screen.blit(cell_surf, cell_rect) \ No newline at end of file diff --git a/constants.py b/constants.py new file mode 100644 index 00000000..dcfcae15 --- /dev/null +++ b/constants.py @@ -0,0 +1,20 @@ +WIDTH = 750 +HEIGHT = 900 +BOARD_ROWS = 4 +BOARD_COLS = 3 +BOARD_LINE_WIDTH = 5 +CELL_LINE_WIDTH = 1 +CELLS_IN_SQUARE = 3 +NUMBER_FONT = 100 +USER_NUMBER_COLOR = (105, 105, 105) +NUMBER_COLOR = (0, 0, 0) +MESSAGE_FONT = 50 +CELL_SIZE = 82 +RED = (255, 0, 0) +WHITE = (240, 248, 255) +BG_COLOR = (224, 255, 255) +BLUE = (0, 0, 255) +DARK_BLUE = (25, 25, 112) +DODGER_BLUE = (0, 90, 156) +SCREEN_COLOR = (173, 216, 230) +BLACK = (0, 0, 0) \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 00000000..885947dd --- /dev/null +++ b/main.py @@ -0,0 +1,357 @@ +from mimetypes import inited + +from board import * +import pygame +from constants import * +from sudoku_generator import * +import random +import copy + + +def draw_game_start(screen): + # Initialize fonts + title_font = pygame.font.Font(None, 100) + mode_font = pygame.font.Font(None, 70) + button_font = pygame.font.Font(None, 45) + + # Clear screen + screen.fill(SCREEN_COLOR) + + # Draw title + title_text = title_font.render("Welcome to Sudoku", True, BLACK) + title_rect = title_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 250)) + screen.blit(title_text, title_rect) + + # Draw mode selection text + mode_text = mode_font.render("Select Game Mode:", True, BLACK) + mode_rect = mode_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 50)) + screen.blit(mode_text, mode_rect) + + # Draw buttons + button_positions = [(-200, "EASY", 30), (0, "MEDIUM", 40), (200, "HARD", 50)] + buttons = {} + for offset, label, value in button_positions: + button_text = button_font.render(label, True, WHITE) + button_surface = pygame.Surface((button_text.get_width() + 20, button_text.get_height() + 20)) + button_surface.fill(DARK_BLUE) + button_surface.blit(button_text, (10, 10)) + button_rect = button_surface.get_rect(center=(WIDTH // 2 + offset, HEIGHT // 2 + 50)) + screen.blit(button_surface, button_rect) + buttons[label] = (button_rect, value) + + pygame.display.flip() + + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + exit() + elif event.type == pygame.MOUSEBUTTONDOWN: + for label, (rect, value) in buttons.items(): + if rect.collidepoint(event.pos): + return value + + +def generate_initial_board(num_cells): + screen = pygame.display.set_mode((WIDTH, HEIGHT)) + initial_board = Board(WIDTH, HEIGHT, screen, difficulty=0) + + # Function to check if the number placement is valid + def is_valid_move(board, row, col, num): + # Check row + if num in board[row]: + return False + # Check column + for r in range(9): + if board[r][col] == num: + return False + # Check 3x3 grid + start_row, start_col = 3 * (row // 3), 3 * (col // 3) + for i in range(start_row, start_row + 3): + for j in range(start_col, start_col + 3): + if board[i][j] == num: + return False + return True + def fill_board(): + for row in range(9): + for col in range(9): + if initial_board.board[row][col] == 0: + numbers = list(range(1, 10)) + random.shuffle(numbers) + for num in numbers: + if is_valid_move(initial_board.board, row, col, num): + initial_board.board[row][col] = num + initial_board.cells[row][col].set_cell_value(num) + if fill_board(): + return True + initial_board.board[row][col] = 0 + initial_board.cells[row][col].set_cell_value(0) + return False + return True + fill_board() + filled_cells = 81 - num_cells + cells_to_remove = 81 - filled_cells + while cells_to_remove > 0: + row, col = random.randint(0, 8), random.randint(0, 8) + if initial_board.board[row][col] != 0: + initial_board.board[row][col] = 0 + initial_board.cells[row][col].set_cell_value(0) + cells_to_remove -= 1 + initial_board.draw() + pygame.display.flip() + + return initial_board + + +def draw_other_buttons(screen): + # Initialize title font + buttons_font = pygame.font.Font(None, 50) + + # Initialize buttons + # Initialize text first + reset_text = buttons_font.render("RESET", 0, WHITE) + restart_text = buttons_font.render("RESTART", 0, WHITE) + exit_text = buttons_font.render("EXIT", 0, WHITE) + + # Initialize button background color and text + reset_surface = pygame.Surface((reset_text.get_size()[0] + 20, reset_text.get_size()[1] + 20)) + reset_surface.fill(DARK_BLUE) + reset_surface.blit(reset_text, (10, 10)) + + restart_surface = pygame.Surface((restart_text.get_size()[0] + 20, restart_text.get_size()[1] + 20)) + restart_surface.fill(DARK_BLUE) + restart_surface.blit(restart_text, (10, 10)) + + exit_surface = pygame.Surface((exit_text.get_size()[0] + 20, exit_text.get_size()[1] + 20)) + exit_surface.fill(DARK_BLUE) + exit_surface.blit(exit_text, (10, 10)) + + # Initialize button rectangle + reset_rectangle = reset_surface.get_rect( + center=(WIDTH // 2 - 200, HEIGHT // 2 + 375)) + restart_rectangle = restart_surface.get_rect( + center=(WIDTH // 2, HEIGHT // 2 + 375)) + exit_rectangle = exit_surface.get_rect( + center=(WIDTH // 2 + 200, HEIGHT // 2 + 375)) + + # Draw buttons + screen.blit(reset_surface, reset_rectangle) + screen.blit(restart_surface, restart_rectangle) + screen.blit(exit_surface, exit_rectangle) + + buttons = [(reset_rectangle, 'RESET'), (restart_rectangle, 'RESTART'), (exit_rectangle, 'EXIT')] + return buttons + + +def handle_button_click(event, buttons): + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + for rect, label in buttons: + if rect.collidepoint(event.pos): + return label + return None + + + +def error_message(error, screen): + message_font = pygame.font.Font(None, 30) + user_error_message = message_font.render(error, 0, DODGER_BLUE) + + # Initialize button background color and text + error_message_surface = pygame.Surface( + (user_error_message.get_size()[0] + 15, user_error_message.get_size()[1] + 15)) + error_message_surface.fill(BG_COLOR) + error_message_surface.blit(user_error_message, (10, 10)) + error_message_rectangle = error_message_surface.get_rect( + center=(WIDTH // 2, HEIGHT // 2 + 325)) + + # Write message on the screen + screen.blit(error_message_surface, error_message_rectangle) + + pygame.display.flip() # Update the display + + pygame.time.delay(800) # Display the message for 0.8 sec + + # Clear the error message from the screen + screen.fill(BG_COLOR) + pygame.display.flip() + + +def draw_game_over(screen, winner): + game_over_font = pygame.font.Font(None, 40) + screen.fill(BG_COLOR) + + if winner != 0: + text = 'Game Won!' + else: + text = "Game Over :(" + + game_over_surf = game_over_font.render(text, 0, DARK_BLUE) + game_over_rect = game_over_surf.get_rect( + center=(WIDTH // 2, HEIGHT // 2 - 100)) + screen.blit(game_over_surf, game_over_rect) + + restart_surf = game_over_font.render('RESTART', 0, DARK_BLUE) + restart_rect = restart_surf.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 100)) + screen.blit(restart_surf, restart_rect) + + menu_surf = game_over_font.render( + 'Press m to return to the main menu...', 0, DARK_BLUE) + menu_rect = menu_surf.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 150)) + screen.blit(menu_surf, menu_rect) + + pygame.display.update() + + while True: + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN and event.key == pygame.K_m: # Exit this function to restart the main loop + main() + +def handle_board_events(board, screen): + for event in pygame.event.get(): + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + pos = event.pos + clicked_cell = board.click(*pos) + if clicked_cell: + board.select(*clicked_cell) + elif event.type == pygame.KEYDOWN: + if pygame.K_1 <= event.key <= pygame.K_9: + board.sketch(event.key - pygame.K_0) + elif event.key == pygame.K_RETURN: + board.place_number() + board.draw() + return True + +def main(): + pygame.init() + screen = pygame.display.set_mode((WIDTH, HEIGHT)) + pygame.display.set_caption("Sudoku") + selected_row, selected_col = 0, 0 + + # Select difficulty and generate the board + difficulty = draw_game_start(screen) + board = generate_initial_board(difficulty) + board.original = copy.deepcopy(board.board) + print("Initial Board (self.board):") + for row in board.board: + print(row) + + print("Original Board (self.original):") + for row in board.original: + print(row) + screen.fill("white") + buttons = draw_other_buttons(screen) + + running = True + game_over = False + while running: + counter = 0 + + if game_over: + if board.check_board(): + winner = 1 + else: + winner = 0 + pygame.display.update() + draw_game_over(screen, winner) + pygame.display.flip() + running = False + + if not game_over: + if difficulty == 30: + for row in range(0,9): + for col in range(0,9): + entry = board.cells[row][col].value + if entry != 0 and counter < 51: + board.draw() + counter +=1 + elif difficulty == 40: + for row in range(0,9): + for col in range(0,9): + entry = board.cells[row][col].value + if entry != 0 and counter < 41: + board.draw() + counter +=1 + elif difficulty == 50: + for row in range(0,9): + for col in range(0,9): + entry = board.cells[row][col].value + if entry != 0 and counter < 31 : + board.draw() + counter +=1 + + # Main game loop + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + pos = pygame.mouse.get_pos() + clicked_cell = board.click(*pos) + other_clicked_cell = handle_button_click(event, buttons) + if clicked_cell: + row, col = clicked_cell + board.select(row, col) + if other_clicked_cell == "RESET": + print("Board before Reset") + for r in board.board: + print(r) + board.reset_to_original() + board.draw() + pygame.display.flip() + + # Debugging: Board state after resetting + print("Board After Reset:") + for row in board.board: + print(row) + elif other_clicked_cell == "RESTART": + main() + return + elif other_clicked_cell == "EXIT": + running = False + + elif event.type == pygame.KEYDOWN: + if pygame.K_1 <= event.key <= pygame.K_9: + board.sketch(event.key - pygame.K_0) + elif event.key == pygame.K_RETURN: + for row in range(board.board_rows): + for col in range(board.board_cols): + if board.cells[row][col].selected: + if board.cells[row][col].sketched_value != 0: + board.place_number(board.cells[row][col].sketched_value) + if board.is_full(): + game_over = True + + elif event.key == pygame.K_LEFT: + new_col = selected_col - 1 + if new_col < 0: + new_col = 8 + board.select(selected_row, new_col) + selected_col = new_col + elif event.key == pygame.K_RIGHT: + new_col = selected_col + 1 + if new_col > 8: + new_col = 0 + board.select(selected_row, new_col) + selected_col = new_col + elif event.key == pygame.K_UP: + new_row = selected_row - 1 + if new_row < 0: + new_row = 8 + board.select(new_row, selected_col) + selected_row = new_row + elif event.key == pygame.K_DOWN: + new_row = selected_row + 1 + if new_row > 8: + new_row = 0 + board.select(new_row, selected_col) + selected_row = new_row + elif event.key == pygame.K_RETURN and board.is_full(): + game_over = True + + board.draw() + pygame.display.flip() + pygame.quit() +if __name__ == '__main__': + main() + + + diff --git a/sudoku_generator.py b/sudoku_generator.py index 7ec18a59..a4eb74d9 100644 --- a/sudoku_generator.py +++ b/sudoku_generator.py @@ -1,157 +1,86 @@ -import math,random +import math, 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: - ''' - create a sudoku board - initialize class variables and set up the 2D board - This should initialize: - self.row_length - the length of each row - 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 - - Return: - None - ''' def __init__(self, row_length, removed_cells): - pass - - ''' - Returns a 2D python list of numbers which represents the board + self.row_length = row_length + self.removed_cells = removed_cells + self.board = [[0 for _ in range(row_length)] for _ in range(row_length)] - Parameters: None - Return: list[list] - ''' def get_board(self): - pass - - ''' - Displays the board to the console - This is not strictly required, but it may be useful for debugging purposes + return self.board - Parameters: None - Return: None - ''' def print_board(self): - pass - - ''' - Determines if num is contained in the specified row (horizontal) of the board - If num is already in the specified row, return False. Otherwise, return True - - 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 - - ''' - Determines if num is contained in the specified column (vertical) of the board - If num is already in the specified col, return False. Otherwise, return True - - 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: + print(" ".join(str(cell) + if cell != 0 else "0" for cell in row)) - ''' - Determines if num is contained in the 3x3 box specified on the board - If num is in the specified box starting at (row_start, col_start), return False. - Otherwise, return True + def valid_in_row(self, row, num): + return num not in self.board[row] - Parameters: - row_start and col_start are the starting indices of the box to check - i.e. the box is from (row_start, col_start) to (row_start+2, col_start+2) - num is the value we are looking for in the box + def valid_in_col(self, col, num): + for row in range(self.row_length): + if self.board[row][col] == num: + return False + return True - Return: boolean - ''' def valid_in_box(self, row_start, col_start, num): - pass - - ''' - 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 - - Parameters: - row and col are the row index and col index of the cell to check in the board - num is the value to test if it is safe to enter in this cell - - Return: boolean - ''' + 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 + def is_valid(self, row, col, num): - pass + if num in self.board[row]: + return False + + for i in range(9): + for col in range(self.row_length): + if self.board[i][col] == num: + return False - ''' - Fills the specified 3x3 box with values - For each position, generates a random digit which has not yet been used in the box + box_start_row = row - row % 3 + box_start_col = col - col % 3 + for i in range(box_start_row, box_start_row + 3): + for j in range(box_start_col, box_start_col + 3): + if self.board[i][j] == num: + return False - Parameters: - row_start and col_start are the starting indices of the box to check - i.e. the box is from (row_start, col_start) to (row_start+2, col_start+2) + return True - Return: None - ''' def fill_box(self, row_start, col_start): - pass - - ''' - 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) - - Parameters: None - Return: None - ''' + from random import shuffle + nums = list(range(1, 10)) + shuffle(nums) + idx = 0 + for i in range(row_start, row_start + 3): + for j in range(col_start, col_start + 3): + self.board[i][j] = nums[idx] + idx += 1 + def fill_diagonal(self): - pass - - ''' - 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) - ''' + for i in range(0, 9, 3): + self.fill_box(i, i) + def fill_remaining(self, row, col): - if (col >= self.row_length and row < self.row_length - 1): + if col >= self.row_length and row < self.row_length - 1: row += 1 col = 0 if row >= self.row_length and col >= self.row_length: return True - if row < self.box_length: - if col < self.box_length: - col = self.box_length - elif row < self.row_length - self.box_length: - if col == int(row // self.box_length * self.box_length): - col += self.box_length + if row < self.row_length: + if col < self.row_length: + col = self.row_length + elif row < self.row_length - self.row_length: + if col == int(row // self.row_length * self.row_length): + col += self.row_length else: - if col == self.row_length - self.box_length: + if col == self.row_length - self.row_length: row += 1 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 @@ -160,32 +89,20 @@ def fill_remaining(self, row, col): self.board[row][col] = 0 return False - ''' - DO NOT CHANGE - Provided for students - Constructs a solution by calling fill_diagonal and fill_remaining - - Parameters: None - Return: None - ''' def fill_values(self): self.fill_diagonal() - self.fill_remaining(0, self.box_length) - - ''' - Removes the appropriate number of cells from the board - 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 - ''' + self.fill_remaining(0, self.row_length) + def remove_cells(self): - pass + count = 0 + while count < self.removed_cells: + row = random.randint(0, 8) + col = random.randint(0, 8) + if self.board[row][col] != 0: + self.board[row][col] = 0 + count += 1 + + ''' DO NOT CHANGE @@ -202,10 +119,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