def test_is_valid_row_number_included(self): from sudoku_board import SudokuBoard board = SudokuBoard() for a in range(1, 9): board.add_answer(1, a, a) for a in range(1, 9): self.assertFalse(board.is_valid_row(1, a))
def test_valid_column_number_not_included(self): from sudoku_board import SudokuBoard board = SudokuBoard() for a in range(1, 9): board.add_answer(a, 1, a) for a in range(1, 9): self.assertFalse(board.is_valid_column(1, a))
def test_sudoku_boxes_hold_thier_location(self): from sudoku_board import SudokuBoard board = SudokuBoard() for row in range(1, 10): for column in range(1, 10): self.assertEqual(board.get_sudoku_box(row, column).row, row) self.assertEqual( board.get_sudoku_box(row, column).column, column)
def test_is_valid_nonet_number_not_included(self): counter = 0 from sudoku_board import SudokuBoard board = SudokuBoard() for i in range(1, 4): for j in range(1, 4): if counter != 0: board.add_answer(i, j, counter % 9 + 1) counter += 1 for a in range(2, 9): self.assertFalse(board.is_valid_nonnet(1, 1, a))
def test_number_is_possible_number_included(self): from sudoku_board import SudokuBoard sudokuboard = [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 6], [0, 0, 0, 0, 0, 0, 0, 0, 5], [0, 0, 0, 0, 0, 0, 0, 0, 4], [0, 0, 0, 0, 0, 0, 7, 8, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 2, 3, 0, 0, 0, 0, 0, 0]] board = SudokuBoard(sudokuboard) self.assertTrue(board.is_valid_number(9, 9, 9))
def test_is_valid_nonet_number_included(self): from sudoku_board import SudokuBoard sudoku_board = [[5, 3, 4, 6, 7, 8, 9, 1, 2], [6, 7, 2, 1, 9, 5, 3, 4, 8], [1, 9, 8, 3, 4, 2, 5, 6, 7], [8, 0, 0, 0, 6, 0, 4, 2, 3], [4, 0, 0, 8, 0, 3, 7, 9, 1], [7, 0, 0, 0, 2, 0, 8, 5, 6], [0, 6, 0, 0, 0, 0, 2, 8, 4], [0, 0, 0, 4, 1, 9, 6, 3, 5], [0, 0, 0, 0, 8, 0, 1, 7, 9]] board = SudokuBoard(sudoku_board) self.assertTrue(board.is_valid_nonnet(4, 1, 5))
class FileReader: def __init__(self): self.difficulty = "eazy" self.file = json.load(open("puzzels/"+self.difficulty+"/"+self.random()+"/incomplete.json", "r")) self.used_puzzles = [] self.board = SudokuBoard(self.file) self.impossible = False def random(self): number = random.randint(0,3) if number == 1: return "one" elif number == 2: return "two" else: return "three" def load_newpuzzle(self): if self.impossible: print("loading new impossible") self.file = json.load(open("puzzels/"+self.difficulty+"/"+self.random()+"/compleete.json", "r")) self.board = SudokuBoard(self.file) x = random.randint(30, 81) while x>0: print("running forever in progress") r=random.randint(0,8) c=random.randint(0,8) if self.board.get_point(r,c) !=0: self.board.set_point(r,c,0) x=x-1 else: self.file = json.load(open("puzzels/"+self.difficulty+"/"+self.random()+"/incomplete.json", "r")) self.board = SudokuBoard(self.file) def get_puzzle(self): return self.board def load_beginner(self): self.difficulty = "eazy" self.impossible = False def load_intermediate(self): self.difficulty = "meedium" self.impossible = False def load_advanced(self): self.difficulty = "harrrrrrrrrrrd" self.impossible = False def load_impossible(self): num = random.randint(0,3) if num ==1: self.difficulty = "eazy" elif num ==2: self.difficulty = "meedium" else: self.difficulty = "harrrrrrrrrrrd" self.impossible = True
def test_number_is_possible_not_included(self): from sudoku_board import SudokuBoard sudokuboard = [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 6], [0, 0, 0, 0, 0, 0, 0, 0, 5], [0, 0, 0, 0, 0, 0, 0, 0, 4], [0, 0, 0, 0, 0, 0, 7, 8, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 2, 3, 0, 0, 0, 0, 0, 0]] board = SudokuBoard(sudokuboard) for answer in range(1, 9): self.assertFalse(board.is_valid_number(9, 9, answer))
def test_find_emptyspot(self): from sudoku_board import SudokuBoard sudoku_board = [[5, 3, 0, 0, 7, 0, 0, 0, 0], [6, 0, 0, 1, 9, 5, 0, 0, 0], [0, 9, 8, 0, 0, 0, 0, 6, 0], [8, 0, 0, 0, 6, 0, 0, 0, 3], [4, 0, 0, 8, 0, 3, 0, 0, 1], [7, 0, 0, 0, 2, 0, 0, 0, 6], [0, 6, 0, 0, 0, 0, 2, 8, 0], [0, 0, 0, 4, 1, 9, 0, 0, 5], [0, 0, 0, 0, 8, 0, 0, 7, 9]] board = SudokuBoard(sudoku_board) self.assertTrue(board.find_emptyspot().column == 3) self.assertTrue(board.find_emptyspot().row == 1)
def load_newpuzzle(self): if self.impossible: print("loading new impossible") self.file = json.load(open("puzzels/"+self.difficulty+"/"+self.random()+"/compleete.json", "r")) self.board = SudokuBoard(self.file) x = random.randint(30, 81) while x>0: print("running forever in progress") r=random.randint(0,8) c=random.randint(0,8) if self.board.get_point(r,c) !=0: self.board.set_point(r,c,0) x=x-1 else: self.file = json.load(open("puzzels/"+self.difficulty+"/"+self.random()+"/incomplete.json", "r")) self.board = SudokuBoard(self.file)
def test_get_answer(self): from sudoku_board import SudokuBoard board = SudokuBoard() board.add_answer(1, 2, 4) self.assertEqual(board.get_answer(1, 2), 4) with self.assertRaises(IndexError): board.get_answer(0, 0)
def test_find_emptyspot_full(self): from sudoku_board import SudokuBoard sudoku_board = [ [1, 2, 3, 4, 5, 6, 7, 8, 9], # this sudoku board is not right but it [2, 3, 4, 5, 6, 7, 8, 9, 1], # doesn't matter we are testing if the board [3, 4, 5, 6, 7, 8, 9, 1, 2], # is correctly implemented in sudoku board [4, 5, 6, 7, 8, 9, 1, 2, 3], [5, 6, 7, 8, 9, 1, 2, 3, 4], [6, 7, 8, 9, 1, 2, 3, 4, 5], [7, 8, 9, 1, 2, 3, 4, 5, 6], [8, 9, 1, 2, 3, 4, 5, 6, 7], [9, 1, 2, 3, 4, 5, 6, 7, 8] ] board = SudokuBoard(sudoku_board) self.assertTrue(board.find_emptyspot() is None)
def test_add_answer_stored_in_right_location(self): from sudoku_board import SudokuBoard, SudokuBox board = SudokuBoard() board.add_answer(1, 2, 4) # board uses indexing starting at 1 instead of 0 self.assertEqual(board.get_sudoku_box(1, 2), SudokuBox(4)) with self.assertRaises(IndexError): board.add_answer(0, 0, 4)
def main(): board = SudokuBoard() board.add_element(0, 3, 3) board.add_element(0, 4, 4) board.add_element(0, 5, 6) board.add_element(0, 7, 5) board.add_element(0, 8, 9) ########### # board.add_element(0, 0, 5) # board.add_element(0, 1, 3) # board.add_element(0, 4, 7) # board.add_element(1, 0, 6) # board.add_element(1, 3, 1) # board.add_element(1, 4, 9) # board.add_element(1, 5, 5) # board.add_element(2, 1, 9) # board.add_element(2, 2, 8) # board.add_element(2, 7, 6) # board.add_element(3, 0, 8) # board.add_element(3, 4, 6) # board.add_element(3, 8, 3) # board.add_element(4, 0, 4) # board.add_element(4, 3, 8) # board.add_element(4, 5, 3) # board.add_element(4, 8, 1) # board.add_element(5, 0, 7) # board.add_element(5, 4, 2) # board.add_element(5, 8, 6) # board.add_element(6, 1, 6) # board.add_element(6, 6, 2) # board.add_element(6, 7, 8) # board.add_element(7, 3, 4) # board.add_element(7, 4, 1) # board.add_element(7, 5, 9) # board.add_element(7, 8, 5) # board.add_element(8, 4, 8) # board.add_element(8, 7, 7) # board.add_element(8, 8, 9) # board.build_board() solver = SudokuSolver(board) if solver.board.is_valid_full(): print("Board before solving: \n") print(board, "\n") if solver.backtracking(0, 0): print("Sudoku has been solved \n") print(board) if solver.board.is_valid_full(): print("Solution Exists!!") else: print("Board entered is not valid. Please enter a valid board.")
def __init__(self, display, board=SudokuBoard(9)): self.screen = display.screen self.board = board self.squares_pos = [] self.wrong = [] self.interactive = self.interactive_shell() self.time = pygame.time.Clock() self.font = pygame.font.Font('freesansbold.ttf', 30) self.active = False self.input = '' self.active_cell = None
def test_get_guesses_out_of_bounds(self): from sudoku_board import SudokuBoard board = SudokuBoard() with self.assertRaises(IndexError): board.get_guesses(0, 0) with self.assertRaises(IndexError): board.get_guesses(10, 10)
def test_sudoku_boxes_hold_thier_location_import_board(self): from sudoku_board import SudokuBoard sudoku_board = [ [1, 2, 3, 4, 5, 6, 7, 8, 9], # this sudoku board is not right but it [2, 3, 4, 5, 6, 7, 8, 9, 1], # doesn't matter we are testing if the board [3, 4, 5, 6, 7, 8, 9, 1, 2], # is correctly implemented in sudoku board [4, 5, 6, 7, 8, 9, 1, 2, 3], [5, 6, 7, 8, 9, 1, 2, 3, 4], [6, 7, 8, 9, 1, 2, 3, 4, 5], [7, 8, 9, 1, 2, 3, 4, 5, 6], [8, 9, 1, 2, 3, 4, 5, 6, 7], [9, 1, 2, 3, 4, 5, 6, 7, 8] ] board = SudokuBoard(sudoku_board) for row in range(1, 10): for column in range(1, 10): self.assertEqual(board.get_sudoku_box(row, column).row, row) self.assertEqual( board.get_sudoku_box(row, column).column, column)
def _on_check_click(self) -> None: """ Check the board's state """ pg.draw.rect(self.game_screen, self.BUTTON_COLOR, (20, 20, self.dimensions - 40, self.dimensions - 40)) text = '' if (self.board.check_win_condition()): text = "Puzzle Solved!" self.board = SudokuBoard() else: text = "Puzzle Not Solved." text_surface = self.STATUS_FONT.render(text, True, self.TEXT_COLOR) self.game_screen.blit( text_surface, text_surface.get_rect(center=(self.dimensions // 2, self.dimensions // 2))) pg.display.update() t.sleep(3) # Pause for 3 seconds
def __get_all_params(self): if self.self.__get_new_or_load() == 1: self.self.__game_type = self.self.__get_game_type() amount_of_filled_cells = self.self.__get_amount_of_filled_cells() self.self.__board = SudokuBoard(amount_of_filled_cells) return True else: val, saves = self.self.__get_save_name() if val == 0: return False with open(f'{saves[val - 1]}', 'rb') as f: self.self = pickle.load(f) return True
def test_create_board(self): from sudoku_board import SudokuBoard sudoku_board = [ [1, 2, 3, 4, 5, 6, 7, 8, 9], # this sudoku board is not right but it [2, 3, 4, 5, 6, 7, 8, 9, 1], # doesn't matter we are testing if the board [3, 4, 5, 6, 7, 8, 9, 1, 2], # is correctly implemented in sudoku board [4, 5, 6, 7, 8, 9, 1, 2, 3], [5, 6, 7, 8, 9, 1, 2, 3, 4], [6, 7, 8, 9, 1, 2, 3, 4, 5], [7, 8, 9, 1, 2, 3, 4, 5, 6], [8, 9, 1, 2, 3, 4, 5, 6, 7], [9, 1, 2, 3, 4, 5, 6, 7, 8] ] board = SudokuBoard(sudoku_board) for sudoku_row, board_row in zip(sudoku_board, board.board): for sudoku_number, board_number in zip(sudoku_row, board_row): self.assertEqual(sudoku_number, board_number.answer)
def __init__(self) -> None: """ params: Initialize the sudoku game. Calcualte and store positioning and spacing values for UI. """ pg.init() # Used to control the speed of game loop # Acts as a buffer for user input self.BUFFER_DELAY = (1 / 20) # 20 FPS # Color Palette self.BACKGROUND_COLOR = (0, 0, 0) self.MAJOR_LINE_COLOR = (200, 200, 200) self.MINOR_LINE_COLOR = (150, 150, 150) self.SELECTED_BOX_COLOR = (70, 70, 255) self.BUTTON_COLOR = (30, 30, 80) self.TEXT_COLOR = (250, 250, 250) self.NUMBER_ORIGINAL_COLOR = (255, 244, 176) self.NUMBER_PLACED_COLOR = (255, 255, 255) # Line Widths self.MAJOR_LINE_WIDTH = 6 self.MINOR_LINE_WIDTH = 1 # Fonts self.NUMBER_FONT = pg.font.SysFont('Consolas', 50) self.BUTTON_FONT = pg.font.SysFont('Consolas', 30) self.STATUS_FONT = pg.font.SysFont('Consolas', 65) # The dimensions of the window (window is dimensions x dimensions) self.dimensions = 700 # The dimensions of the buttons self.button_width = self.dimensions / 3 self.button_height = 70 self.button_padding = 10 self.button_y_pos = self.dimensions + self.button_padding self.check_x_pos = self.button_padding self.undo_x_pos = self.button_width + self.button_padding self.main_menu_x_pos = 2 * self.button_width + self.button_padding # The interval of major box in each direction self.major_box_x_interval = self.dimensions / 3 self.major_box_y_interval = self.dimensions / 3 # The interval of minor box inside the major box in each direction self.minor_box_x_interval = self.dimensions / 9 self.minor_box_y_interval = self.dimensions / 9 # Create the sudoku board model self.board = SudokuBoard() # Represents the current selected box self.curr_selected_row = 0 self.curr_selected_col = 0 # The number placement history self.move_history = []
def __init__(self): self.difficulty = "eazy" self.file = json.load(open("puzzels/"+self.difficulty+"/"+self.random()+"/incomplete.json", "r")) self.used_puzzles = [] self.board = SudokuBoard(self.file) self.impossible = False
def test_add_answer_number_not_included(self): from sudoku_board import SudokuBoard board = SudokuBoard() board.add_answer(1, 2, 4) self.assertTrue(board.add_answer(1, 3, 5))
def create_menu_1(self): if not self.dif_menu: pressed_button = None buttons = [] texts = ("Create New Board", "Solve Given Board", "Difficulty Settings") if self.interactive: texts = ("Create New Board", "Solve Given Board", "Difficulty Settings", "Resume") x, y = 0, 50 for i in range(len(texts)): buttons.append([pygame.Rect(x, y, 400, 50), i]) pygame.draw.rect(self.screen, (160, 160, 160), buttons[i][0]) pygame.draw.rect(self.screen, (0, 0, 0), buttons[i][0], 2) text = self.font.render(texts[i], True, (0, 0, 0)) self.screen.blit(text, (x + 30, y + 10)) y += 50 pygame.display.flip() if event.type == pygame.MOUSEBUTTONDOWN: for i in buttons: if i[0].collidepoint(event.pos): pygame.draw.rect(self.screen, (0, 0, 255), i[0], 2) pygame.display.flip() pressed_button = texts[i[1]] break if pressed_button == "Create New Board": self.board = Board(self.display, SudokuBoard(self.difficulty[0])) self.sudoku = self.board.board self.sudoku.reset() self.sudoku.fill_solve() self.sudoku.create_board() self.board.interactive = self.board.interactive_shell() self.interactive = self.board.interactive self.submit_mode = False self.active = False if pressed_button == "Solve Given Board": self.board = Board(self.display) self.sudoku = self.board.board self.sudoku.reset() self.board.interactive = self.board.interactive_shell() self.interactive = self.board.interactive self.submit_mode = True self.active = False if pressed_button == "Difficulty Settings": self.dif_menu = True self.background() if pressed_button == "Resume": self.active = False else: pressed_button = None buttons = [] dif_texts = ("Easy", "Normal", "Hard", "Really Hard", "Back") x, y = 500, 100 for i in range(len(dif_texts)): buttons.append([pygame.Rect(x, y, 300, 50), i]) pygame.draw.rect(self.screen, (160, 160, 160), buttons[i][0]) if self.difficulty[1] == dif_texts[i]: pygame.draw.rect(self.screen, (0, 0, 255), buttons[i][0], 2) else: pygame.draw.rect(self.screen, (0, 0, 0), buttons[i][0], 2) text = self.font.render(dif_texts[i], True, (0, 0, 0)) self.screen.blit(text, (x + 30, y + 10)) y += 50 pygame.display.flip() if event.type == pygame.MOUSEBUTTONDOWN: for i in buttons: if i[0].collidepoint(event.pos): pressed_button = dif_texts[i[1]] break if pressed_button == "Easy": self.difficulty = 3, "Easy" if pressed_button == "Normal": self.difficulty = 4, "Normal" if pressed_button == "Hard": self.difficulty = 5, "Hard" if pressed_button == "Really Hard": self.difficulty = 6, "Really Hard" if pressed_button == "Back": self.dif_menu = False self.background()
def test_solve(self): from sudoku_board import SudokuBoard board = SudokuBoard() self.assertTrue(board.solve()) print(board)
def test_get_guesses(self): from sudoku_board import SudokuBoard board = SudokuBoard() board.add_guess(1, 1, 3) self.assertEqual(board.get_guesses(1, 1), [3])
def test_get_sudoku_box_number_not_between_one_through_nine(self): from sudoku_board import SudokuBoard board = SudokuBoard() with self.assertRaises(IndexError): board.get_sudoku_box(0, 0)
class SudokuGUI: """ The Main GUI Application for Sudoku. Runs the game based on user input. """ def __init__(self) -> None: """ params: Initialize the sudoku game. Calcualte and store positioning and spacing values for UI. """ pg.init() # Used to control the speed of game loop # Acts as a buffer for user input self.BUFFER_DELAY = (1 / 20) # 20 FPS # Color Palette self.BACKGROUND_COLOR = (0, 0, 0) self.MAJOR_LINE_COLOR = (200, 200, 200) self.MINOR_LINE_COLOR = (150, 150, 150) self.SELECTED_BOX_COLOR = (70, 70, 255) self.BUTTON_COLOR = (30, 30, 80) self.TEXT_COLOR = (250, 250, 250) self.NUMBER_ORIGINAL_COLOR = (255, 244, 176) self.NUMBER_PLACED_COLOR = (255, 255, 255) # Line Widths self.MAJOR_LINE_WIDTH = 6 self.MINOR_LINE_WIDTH = 1 # Fonts self.NUMBER_FONT = pg.font.SysFont('Consolas', 50) self.BUTTON_FONT = pg.font.SysFont('Consolas', 30) self.STATUS_FONT = pg.font.SysFont('Consolas', 65) # The dimensions of the window (window is dimensions x dimensions) self.dimensions = 700 # The dimensions of the buttons self.button_width = self.dimensions / 3 self.button_height = 70 self.button_padding = 10 self.button_y_pos = self.dimensions + self.button_padding self.check_x_pos = self.button_padding self.undo_x_pos = self.button_width + self.button_padding self.main_menu_x_pos = 2 * self.button_width + self.button_padding # The interval of major box in each direction self.major_box_x_interval = self.dimensions / 3 self.major_box_y_interval = self.dimensions / 3 # The interval of minor box inside the major box in each direction self.minor_box_x_interval = self.dimensions / 9 self.minor_box_y_interval = self.dimensions / 9 # Create the sudoku board model self.board = SudokuBoard() # Represents the current selected box self.curr_selected_row = 0 self.curr_selected_col = 0 # The number placement history self.move_history = [] def _render_sudoku_board(self) -> None: """ Render the sudoku board on the screen by drawing the grid lines, the numbers and the buttons. """ # Reset Screen self.game_screen.fill(self.BACKGROUND_COLOR) # Draw Grid Lines and Selected Box self._render_grid() # Draw Numbers self._render_numbers() # Draw Buttons self._render_buttons() def _render_grid(self) -> None: """ Draw thin lines to represnet smaller box. Draw thick lines to represent the nonets. Draw square to represent selected box. """ # Draw Minor Lines for i in range(3): for j in range(1, 3): # Vertical Lines pg.draw.line(self.game_screen, self.MINOR_LINE_COLOR, ((self.major_box_x_interval*i) + (self.minor_box_x_interval*j), 0), \ ((self.major_box_x_interval*i) + (self.minor_box_x_interval*j), self.dimensions), self.MINOR_LINE_WIDTH) # Horizontal Lines pg.draw.line(self.game_screen, self.MINOR_LINE_COLOR, (0, (self.major_box_y_interval*i) + (self.minor_box_y_interval*j)), \ (self.dimensions, (self.major_box_y_interval*i) + (self.minor_box_y_interval*j)), self.MINOR_LINE_WIDTH) # Draw Major Lines for i in range(1, 3): # Vertical Lines pg.draw.line(self.game_screen, self.MAJOR_LINE_COLOR, (self.major_box_x_interval*i, 0), \ (self.major_box_x_interval*i, self.dimensions), self.MAJOR_LINE_WIDTH) # Horizontal Lines pg.draw.line(self.game_screen, self.MAJOR_LINE_COLOR, (0, self.major_box_y_interval*i), \ (self.dimensions, self.major_box_y_interval*i), self.MAJOR_LINE_WIDTH) # Draw Outline Border of screen pg.draw.rect(self.game_screen, self.MAJOR_LINE_COLOR, (0, 0, self.dimensions, self.dimensions), 5) # Draw Selected Box pg.draw.rect(self.game_screen, self.SELECTED_BOX_COLOR, (self.minor_box_x_interval * self.curr_selected_col, self.minor_box_y_interval * self.curr_selected_row, \ self.minor_box_x_interval, self.minor_box_y_interval), self.MAJOR_LINE_WIDTH) def _render_numbers(self) -> None: """ Draw the numbers and place them inside the box. """ for row in range(self.board.DIMENSION): for col in range(self.board.DIMENSION): val, is_original = self.board.get_element(row, col) if val: value_string = str(val) if (is_original): text_surface = self.NUMBER_FONT.render( value_string, True, self.NUMBER_ORIGINAL_COLOR) else: text_surface = self.NUMBER_FONT.render( value_string, True, self.NUMBER_PLACED_COLOR) # Text position px = (self.minor_box_x_interval * col) + (self.minor_box_x_interval / 2) py = (self.minor_box_y_interval * row) + (self.minor_box_y_interval / 2) # Center the text text_rect = text_surface.get_rect(center=(px, py)) self.game_screen.blit(text_surface, text_rect) def _render_buttons(self) -> None: """ Draw the three button rectangles and place the button text inside the rectangles. """ # Pause Button pg.draw.rect(self.game_screen, self.BUTTON_COLOR, (self.check_x_pos, self.button_y_pos, \ self.button_width - 2*self.button_padding, self.button_height - 2*self.button_padding)) px = self.check_x_pos + ( (self.button_width - 2 * self.button_padding) // 2) py = self.button_y_pos + ( (self.button_height - 2 * self.button_padding) // 2) text_surface = self.BUTTON_FONT.render("CHECK", True, self.TEXT_COLOR) self.game_screen.blit(text_surface, text_surface.get_rect(center=(px, py))) # Clear Button pg.draw.rect(self.game_screen, self.BUTTON_COLOR, (self.undo_x_pos, self.button_y_pos, \ self.button_width - 2*self.button_padding, self.button_height - 2*self.button_padding)) px = self.undo_x_pos + ( (self.button_width - 2 * self.button_padding) // 2) py = self.button_y_pos + ( (self.button_height - 2 * self.button_padding) // 2) text_surface = self.BUTTON_FONT.render("UNDO", True, self.TEXT_COLOR) self.game_screen.blit(text_surface, text_surface.get_rect(center=(px, py))) # Main Menu Button pg.draw.rect(self.game_screen, self.BUTTON_COLOR, (self.main_menu_x_pos, self.button_y_pos, \ self.button_width - 2*self.button_padding, self.button_height - 2*self.button_padding)) px = self.main_menu_x_pos + ( (self.button_width - 2 * self.button_padding) // 2) py = self.button_y_pos + ( (self.button_height - 2 * self.button_padding) // 2) text_surface = self.BUTTON_FONT.render("MAIN MENU", True, self.TEXT_COLOR) self.game_screen.blit(text_surface, text_surface.get_rect(center=(px, py))) def _update_current_selected_box_pos(self, user_input, is_mouse) -> None: """ Parse user input and update position of the current selected box """ # user_input can be a keyboard direction # or it can be a mouse position if not is_mouse: dr = user_input[0] dc = user_input[1] # Update current selected box only if direction is valid self.curr_selected_row += dr * int( 0 <= self.curr_selected_row + dr <= 8) self.curr_selected_col += dc * int( 0 <= self.curr_selected_col + dc <= 8) else: mx = user_input[0] my = user_input[1] # Convert mouse position to array position row = int(my // self.minor_box_y_interval) col = int(mx // self.minor_box_x_interval) if 0 <= row <= 8 and 0 <= col <= 8: self.curr_selected_row = row self.curr_selected_col = col def _get_player_input(self) -> tuple: """ Get the user's directional input, number key input, and mouse position input and return it as a tuple """ # User keyboard input keys = pg.key.get_pressed() move_direction = [0, 0] # The direction the selected box is moving if keys[pg.K_UP] or keys[pg.K_w]: # Up Direction move_direction = [-1, 0] elif keys[pg.K_DOWN] or keys[pg.K_s]: # Down Direction move_direction = [1, 0] elif keys[pg.K_LEFT] or keys[pg.K_a]: # Left Direction move_direction = [0, -1] elif keys[pg.K_RIGHT] or keys[pg.K_d]: # Right Direction move_direction = [0, 1] placed_num = -1 # The number input from user if keys[pg.K_1]: placed_num = 1 elif keys[pg.K_2]: placed_num = 2 elif keys[pg.K_3]: placed_num = 3 elif keys[pg.K_4]: placed_num = 4 elif keys[pg.K_5]: placed_num = 5 elif keys[pg.K_6]: placed_num = 6 elif keys[pg.K_7]: placed_num = 7 elif keys[pg.K_8]: placed_num = 8 elif keys[pg.K_9]: placed_num = 9 elif keys[pg.K_0]: placed_num = 0 # Check Mouse Input mouse_pos = self._check_player_mouse() return move_direction, placed_num, mouse_pos def _check_player_mouse(self) -> tuple: """ Return mouse position as a tuple if left click pressed. Return None if left click not pressed. """ mouse_button_state = pg.mouse.get_pressed() # Left Click if mouse_button_state[0]: # Mouse xy position mx, my = pg.mouse.get_pos() # Check if mouse is in the y-region of the buttons if self.button_y_pos <= my <= self.button_y_pos + ( self.button_height - 2 * self.button_padding): # Check if mouse is in Check Button Rectangle if self.check_x_pos <= mx <= self.check_x_pos + ( self.button_width - 2 * self.button_padding): # Check Button pressed self._on_check_click() # Check if mouse is in Undo Button Rectangle elif self.undo_x_pos <= mx <= self.undo_x_pos + ( self.button_width - 2 * self.button_padding): # Undo Button pressed self._on_undo_click() # Check if mouse is in Main Menu Button Rectangle elif self.main_menu_x_pos <= mx <= self.main_menu_x_pos + ( self.button_width - 2 * self.button_padding): # Main Menu Button pressed self._on_main_menu_click() return (mx, my) def _on_check_click(self) -> None: """ Check the board's state """ pg.draw.rect(self.game_screen, self.BUTTON_COLOR, (20, 20, self.dimensions - 40, self.dimensions - 40)) text = '' if (self.board.check_win_condition()): text = "Puzzle Solved!" self.board = SudokuBoard() else: text = "Puzzle Not Solved." text_surface = self.STATUS_FONT.render(text, True, self.TEXT_COLOR) self.game_screen.blit( text_surface, text_surface.get_rect(center=(self.dimensions // 2, self.dimensions // 2))) pg.display.update() t.sleep(3) # Pause for 3 seconds def _on_undo_click(self) -> None: """ Undo the last move by clearing the board and playing all moves up until the last move (not inclusive). """ if (self.move_history): self.move_history.pop() self.board.clear_board() for move in self.move_history: row, col, val = move self.board.set_element(row, col, val) def _on_main_menu_click(self) -> None: """ Will be implemented by Greg and Aditya """ # TODO Implement this method print("MAIN MENU BUTTON PRESSED") def run_game(self) -> None: """ Initialize the game and run the main game loop """ # Initialize PyGame Screen self.game_screen = pg.display.set_mode( (self.dimensions, self.dimensions + self.button_height)) pg.display.set_caption('PyDoku Inc.') self.game_over = False self._render_sudoku_board() while not self.game_over: # Poll user input event = pg.event.poll() # User closed the window if event.type == pg.QUIT: return # Input for moving the selected box user_input, placed_num, mouse_pos = self._get_player_input() board_changed = False # If user inputs a number other than 0 if placed_num != -1: self.board.set_element(self.curr_selected_row, self.curr_selected_col, placed_num) move = (self.curr_selected_row, self.curr_selected_col, placed_num) # Check for repeating moves if (not self.move_history): self.move_history.append(move) elif (self.move_history[-1] != move): self.move_history.append(move) board_changed = True # If user moves the selected box in a direction other than (0, 0) if any(user_input): self._update_current_selected_box_pos(user_input, False) board_changed = True # If the user clicked on the screen if mouse_pos is not None: self._update_current_selected_box_pos(mouse_pos, True) board_changed = True # If the state of the board has changed if board_changed: self._render_sudoku_board() pg.display.update() # Delay the game loop to act as a buffer t.sleep(self.BUFFER_DELAY)
def __init__(self, puzzle: List[List[int]] = None): self.guide: int = -1 self.board: SudokuBoard = SudokuBoard(puzzle) self.squares: SudokuSquares = SudokuSquares() self.init_squares()
def test_get_sudoku_box_number_not_between(self): from sudoku_board import SudokuBoard, SudokuBox board = SudokuBoard() board.add_answer(1, 1, 1) self.assertEqual(board.get_sudoku_box(1, 1), SudokuBox(1))