diff --git a/.github/workflows/cs102.yml b/.github/workflows/cs102.yml index 4509ac4..cb286a1 100644 --- a/.github/workflows/cs102.yml +++ b/.github/workflows/cs102.yml @@ -10,7 +10,7 @@ jobs: - name: Set up Python 3.8.6 uses: actions/setup-python@v2 with: - python-version: '3.8.6' + python-version: '3.10' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/homework02/sudoku.py b/homework02/sudoku.py index df78ab1..df02ea2 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -1,11 +1,12 @@ import pathlib +import random import typing as tp T = tp.TypeVar("T") def read_sudoku(path: tp.Union[str, pathlib.Path]) -> tp.List[tp.List[str]]: - """ Прочитать Судоку из указанного файла """ + """Прочитать Судоку из указанного файла""" path = pathlib.Path(path) with path.open() as f: puzzle = f.read() @@ -19,7 +20,7 @@ def create_grid(puzzle: str) -> tp.List[tp.List[str]]: def display(grid: tp.List[tp.List[str]]) -> None: - """Вывод Судоку """ + """Вывод Судоку""" width = 2 line = "+".join(["-" * (width * 3)] * 3) for row in range(9): @@ -38,11 +39,18 @@ def group(values: tp.List[T], n: int) -> tp.List[tp.List[T]]: Сгруппировать значения values в список, состоящий из списков по n элементов >>> group([1,2,3,4], 2) - [[1, 2], [3, 4]] + [[1, 2], [3, 4]]) >>> group([1,2,3,4,5,6,7,8,9], 3) [[1, 2, 3], [4, 5, 6], [7, 8, 9]] """ - pass + i = 0 + j = n + s = [] + while j <= len(values): + s.append(values[i:j]) + i = j + j += n + return s def get_row(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: @@ -55,7 +63,7 @@ def get_row(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str >>> get_row([['1', '2', '3'], ['4', '5', '6'], ['.', '8', '9']], (2, 0)) ['.', '8', '9'] """ - pass + return grid[pos[0]] def get_col(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: @@ -68,7 +76,10 @@ def get_col(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str >>> get_col([['1', '2', '3'], ['4', '5', '6'], ['.', '8', '9']], (0, 2)) ['3', '6', '9'] """ - pass + post = [] + for i in range(len(grid)): + post.append(grid[i][pos[1]]) + return post def get_block(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: @@ -82,10 +93,14 @@ def get_block(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[s >>> get_block(grid, (8, 8)) ['2', '8', '.', '.', '.', '5', '.', '7', '9'] """ - pass + square = [] + for i in range(pos[0] // 3 * 3, pos[0] // 3 * 3 + 3): + for j in range(pos[1] // 3 * 3, pos[1] // 3 * 3 + 3): + square.append(grid[i][j]) + return square -def find_empty_positions(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.Tuple[int, int]]: +def find_empty_positions(grid: tp.List[tp.List[str]]): """Найти первую свободную позицию в пазле >>> find_empty_positions([['1', '2', '.'], ['4', '5', '6'], ['7', '8', '9']]) @@ -95,7 +110,14 @@ def find_empty_positions(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.Tuple[in >>> find_empty_positions([['1', '2', '3'], ['4', '5', '6'], ['.', '8', '9']]) (2, 0) """ - pass + x = -1 + for i in range(len(grid)): + x += 1 + y = 0 + for j in range(len(grid[i])): + if grid[i][j] == ".": + return x, y + y += 1 def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.Set[str]: @@ -109,11 +131,13 @@ def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) - >>> values == {'2', '5', '9'} True """ - pass + full = set(get_row(grid, pos) + get_col(grid, pos) + get_block(grid, pos)) + possible_values = set("123456789") - full + return possible_values -def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: - """ Решение пазла, заданного в grid """ +def solve(grid: tp.List[tp.List[str]]): + """Решение пазла, заданного в grid""" """ Как решать Судоку? 1. Найти свободную позицию 2. Найти все возможные значения, которые могут находиться на этой позиции @@ -125,13 +149,33 @@ def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: >>> solve(grid) [['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', '5', '9', '7', '6', '1', '4', '2', '3'], ['4', '2', '6', '8', '5', '3', '7', '9', '1'], ['7', '1', '3', '9', '2', '4', '8', '5', '6'], ['9', '6', '1', '5', '3', '7', '2', '8', '4'], ['2', '8', '7', '4', '1', '9', '6', '3', '5'], ['3', '4', '5', '2', '8', '6', '1', '7', '9']] """ - pass + empty_positions = find_empty_positions(grid) + if empty_positions is None: + return grid + else: + possible_values = find_possible_values(grid, empty_positions) + for i in possible_values: + grid[empty_positions[0]][empty_positions[1]] = i + next = solve(grid) + if next: + return next + grid[empty_positions[0]][empty_positions[1]] = "." def check_solution(solution: tp.List[tp.List[str]]) -> bool: - """ Если решение solution верно, то вернуть True, в противном случае False """ + """Если решение solution верно, то вернуть True, в противном случае False""" # TODO: Add doctests with bad puzzles - pass + all = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] + for i in range(0, len(solution)): + for j in range(0, len(solution)): + pos = i, j + if ( + not sorted(get_row(solution, pos)) == all + or not sorted(get_col(solution, pos)) == all + or not sorted(get_block(solution, pos)) == all + ): + return False + return True def generate_sudoku(N: int) -> tp.List[tp.List[str]]: @@ -156,7 +200,24 @@ def generate_sudoku(N: int) -> tp.List[tp.List[str]]: >>> check_solution(solution) True """ - pass + grid = [["." for i in range(9)] for i in range(9)] + print(N) + if N > 81: + N = 81 + print(grid) + grid = solve(grid) + print(grid) + k = 0 + while k != 81 - N: + i = random.randint(0, 8) + j = random.randint(0, 8) + if grid[i][j] == ".": + k = k + else: + grid[i][j] = "." + k += 1 + print(grid) + return grid if __name__ == "__main__": @@ -168,3 +229,8 @@ def generate_sudoku(N: int) -> tp.List[tp.List[str]]: print(f"Puzzle {fname} can't be solved") else: display(solution) + if check_solution(solution): + print("Solution is correct") + else: + print("LoL") + display(generate_sudoku(1))