Esempio n. 1
0
class Board(GUIBase):
    """Screen Board

    :param board: Sudoku board represent as two dimensional array
    :type board: list
    :param size: screen dimensions (pixels) (width, height)
    :type size: tuple
    :param screen: pygame screen
    :type screen: pygame.Surface 
    """
    def __init__(self, size: tuple, board: list, screen: pygame.Surface):
        super().__init__((size[1], size[1], size[0] - size[1]), screen)
        self.__board = board
        self.__solver = Solver(self)
        # create squares list
        self.__squares = [[
            Square(
                self.__board[c][r],
                (r, c),
                (self.size[0], self.size[2]),
                self.screen,
                True if self.__board[c][r] == 0 else False,
            ) for r in range(9)
        ] for c in range(9)]
        self.__selected = None
        self.__wrong = None

    @property
    def wrong(self):
        """wrong property (getter)"""
        return self.__wrong

    @property
    def squares(self) -> list:
        """squares property (getter)"""
        return self.__squares

    def update_squares(self):
        """squares property (updatter)"""
        # iterate over all squares
        for r in range(9):
            for c in range(9):
                # update values
                self.__squares[r][c].value = self.__board[r][c]
                self.__squares[r][c].pencil = 0

    @property
    def board(self) -> list:
        """board property (getter)"""
        return self.__board

    @board.setter
    def board(self, board: list):
        """board property (setter) & update squares
        
        :param board: Sudoku board represent as two dimensional array
        :type board: list
        """
        # set new board
        self.__board = board
        # reinit squares
        self.__squares = [[
            Square(
                self.__board[c][r],
                (r, c),
                (self.size[0], self.size[2]),
                self.screen,
                True if self.__board[c][r] == 0 else False,
            ) for r in range(9)
        ] for c in range(9)]

    @property
    def selected(self) -> tuple:
        """selected property (getter)"""
        return self.__selected

    @selected.setter
    def selected(self, pos: tuple):
        """selected property (setter) & refresh squares
        
        :param pos: selected square position (row, column)
        :type pos: tuple
        """
        if not self.__wrong:
            # clear previous selection
            if self.__selected != None:
                self.__squares[self.__selected[0]][
                    self.__selected[1]].selected = False
            if pos:
                # select new square
                self.__selected = pos
                self.__squares[self.__selected[0]][
                    self.__selected[1]].selected = True
            else:
                # set selected to None if pos out of board
                self.__selected = None

    @property
    def get_pencil(self) -> int:
        """selected square pencil (getter)"""
        # get selected square
        r, c = self.__selected
        return self.__squares[r][c].pencil

    def set_pencil(self, value: int):
        """set pencil value
        
        :param value: pencil value
        :type value: int
        """
        # get selected square
        r, c = self.__selected
        if self.__squares[r][c].value == 0:
            self.__squares[r][c].pencil = value

    @property
    def get_value(self) -> int:
        """selected square value (getter)"""
        # get selected square
        r, c = self.__selected
        return self.__squares[r][c].value

    def set_value(self) -> str:
        """set square value 

        :returns: board state ('s' -> success, 'w' -> wrong, 'c' -> unsolvable board)
        :rtype: str
        """
        # get selected square
        r, c = self.__selected
        if self.get_value == 0:
            # chock for non-0 pencil value
            pencil = self.get_pencil
            if pencil != 0:
                # check the number match Sudoku rules
                w = self.__solver.exists(self.__board, pencil, (r, c))
                if w:
                    # change squares state to wrong (red color)
                    self.__squares[r][c].wrong = True
                    self.__squares[w[0]][w[1]].wrong = True
                    self.__squares[r][c].value = pencil
                    self.__board[r][c] = pencil
                    self.__wrong = w
                    return "w"
                else:
                    # change set square value and return true
                    self.__squares[r][c].value = pencil
                    self.__board[r][c] = pencil
                    # copy board
                    # init copy as two dimensional array with 9 rows
                    copy = [[] for r in range(9)]
                    # iterate over all rows
                    for r in range(9):
                        # iterate over all columns
                        for c in range(9):
                            # append the num
                            copy[r].append(self.__board[r][c])
                    # check if the board unsolvable
                    if not self.__solver.solve(copy):
                        return "c"
                    return "s"

    @property
    def clear(self):
        """clear selected square value"""
        # get selected square
        r, c = self.__selected
        # clear square value and pencil
        self.__squares[r][c].value = 0
        self.__squares[r][c].pencil = 0
        self.__board[r][c] = 0
        # change wrong state
        if self.__wrong:
            self.__squares[r][c].wrong = False
            self.__squares[self.__wrong[0]][self.__wrong[1]].wrong = False
            self.__wrong = None

    @property
    def isfinished(self):
        """return true if there's no more empty squares else false
        
        :returns: true if there's no more empty squares else false
        :rtype: bool
        """
        return not self.__solver.nextpos(self.board)

    def set_sq_value(self, value: int, pos: tuple):
        """change square value by position
        
        :param value: new square value
        :type value: int
        :param pos: square position
        :type pos: tuple
        """
        self.__squares[pos[0]][pos[1]].value = value

    def draw(self):
        """Draw the board on the screen"""
        # Draw squares
        # iterate over all rows
        for r in range(9):
            # iterate over all columns
            for c in range(9):
                # draw square value
                self.__squares[c][r].draw()
        # Draw grid
        # set space between squares
        space = self.size[0] // 9
        # drow 10 lines HvV
        for r in range(10):
            # set line weight (bold at the end of 3*3 area)
            w = 4 if r % 3 == 0 and r != 0 else 1
            # draw horizontal line (screen, (color), (start_pos), (end_pos), width)
            pygame.draw.line(
                self.screen,
                (72, 234, 54),
                (self.size[2], r * space),
                (self.size[0] + self.size[2], r * space),
                w,
            )
            # draw vertical line (screen, (color), (start_pos), (end_pos), width)
            pygame.draw.line(
                self.screen,
                (72, 234, 54),
                (r * space + self.size[2], 0),
                (r * space + self.size[2], self.size[1]),
                w,
            )