-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
homework02 К24 Юров Кирилл 1.3 #272
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,33 @@ | ||
"""Sudoku solving""" | ||
|
||
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() | ||
return create_grid(puzzle) | ||
|
||
|
||
def create_grid(puzzle: str) -> tp.List[tp.List[str]]: | ||
"""Creates grid for puzzle""" | ||
digits = [c for c in puzzle if c in "123456789."] | ||
grid = group(digits, 9) | ||
return grid | ||
|
||
|
||
def display(grid: tp.List[tp.List[str]]) -> None: | ||
"""Вывод Судоку """ | ||
"""Вывод Судоку""" | ||
width = 2 | ||
line = "+".join(["-" * (width * 3)] * 3) | ||
for row in range(9): | ||
print( | ||
"".join( | ||
grid[row][col].center(width) + ("|" if str(col) in "25" else "") for col in range(9) | ||
) | ||
) | ||
print("".join(grid[row][col].center(width) + ("|" if str(col) in "25" else "") for col in range(9))) | ||
if str(row) in "25": | ||
print(line) | ||
print() | ||
|
@@ -41,7 +41,7 @@ def group(values: tp.List[T], n: int) -> tp.List[tp.List[T]]: | |
>>> group([1,2,3,4,5,6,7,8,9], 3) | ||
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] | ||
""" | ||
pass | ||
return [values[i * n : i * n + n] for i in range(len(values) // n)] | ||
|
||
|
||
def get_row(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: | ||
|
@@ -53,7 +53,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]: | ||
|
@@ -65,7 +65,7 @@ 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 | ||
return [i[pos[1]] for i in grid] | ||
|
||
|
||
def get_block(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: | ||
|
@@ -78,7 +78,12 @@ 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 | ||
array = [ | ||
grid[i][j] | ||
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) | ||
] | ||
return array | ||
|
||
|
||
def find_empty_positions(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.Tuple[int, int]]: | ||
|
@@ -90,7 +95,11 @@ 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 | ||
for i, char in enumerate(grid): | ||
for j, char1 in enumerate(char): | ||
if char1 == ".": | ||
return (i, j) | ||
return None | ||
|
||
|
||
def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.Set[str]: | ||
|
@@ -103,31 +112,63 @@ def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) - | |
>>> values == {'2', '5', '9'} | ||
True | ||
""" | ||
pass | ||
set1 = set("123456789") | ||
set2 = set(get_block(grid, pos)).union(get_row(grid, pos), get_col(grid, pos)) | ||
return set1 - set2 | ||
|
||
|
||
def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: | ||
""" Решение пазла, заданного в grid """ | ||
""" Как решать Судоку? | ||
1. Найти свободную позицию | ||
2. Найти все возможные значения, которые могут находиться на этой позиции | ||
3. Для каждого возможного значения: | ||
3.1. Поместить это значение на эту позицию | ||
3.2. Продолжить решать оставшуюся часть пазла | ||
"""Решение пазла, заданного в grid""" | ||
""" | ||
>>> grid = read_sudoku('puzzle1.txt') | ||
>>> 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 | ||
spc = find_empty_positions(grid) | ||
if not spc: | ||
return grid | ||
res_grid = [i.copy() for i in grid] | ||
for i in find_possible_values(grid, spc): | ||
res_grid[spc[0]][spc[1]] = i | ||
next1 = solve(res_grid) | ||
if next1: | ||
return next1 | ||
return None | ||
|
||
|
||
def check_solution(solution: tp.List[tp.List[str]]) -> bool: | ||
""" Если решение solution верно, то вернуть True, в противном случае False """ | ||
# TODO: Add doctests with bad puzzles | ||
pass | ||
|
||
|
||
def generate_sudoku(N: int) -> tp.List[tp.List[str]]: | ||
"""Если решение solution верно, то вернуть True, в противном случае False""" | ||
flag = True | ||
for i in range(0, 9): | ||
arr = get_row(solution, (i, 0)) | ||
used = [0] * 10 | ||
for j, val in enumerate(arr): | ||
used[ord(val) - ord("0")] += 1 | ||
if used[ord(val) - ord("0")] > 1: | ||
flag = False | ||
break | ||
for i in range(0, 9): | ||
arr = get_col(solution, (i, 0)) | ||
used = [0] * 10 | ||
for j, row in enumerate(arr): | ||
for z, val in enumerate(row): | ||
used[ord(arr[j]) - ord("0")] += 1 | ||
if used[ord(arr[j][z]) - ord("0")] > 1: | ||
flag = False | ||
break | ||
for i in range(0, 9, 3): | ||
for j in range(0, 9, 3): | ||
arr = get_block(solution, (i, j)) | ||
used = [0] * 10 | ||
for z, val in enumerate(arr): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Немного усложнил перебор массива с лишним enumerate, тк z не использовалась |
||
used[ord(val) - ord("0")] += 1 | ||
if used[ord(val) - ord("0")] > 1: | ||
flag = False | ||
break | ||
return flag | ||
|
||
|
||
def generate_sudoku(n: int) -> tp.List[tp.List[str]]: | ||
"""Генерация судоку заполненного на N элементов | ||
>>> grid = generate_sudoku(40) | ||
>>> sum(1 for row in grid for e in row if e == '.') | ||
|
@@ -148,7 +189,26 @@ def generate_sudoku(N: int) -> tp.List[tp.List[str]]: | |
>>> check_solution(solution) | ||
True | ||
""" | ||
pass | ||
cor_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"], | ||
] | ||
empty_grid = [["." for i in range(9)] for i in range(9)] | ||
dot_list = [(i, j) for i in range(9) for j in range(9)] | ||
n = min(n, 81) | ||
for i in range(n): | ||
replace_pos = random.randint(0, len(dot_list) - 1) | ||
ch_pos = dot_list[replace_pos] | ||
empty_grid[ch_pos[0]][ch_pos[1]] = cor_grid[ch_pos[0]][ch_pos[1]] | ||
dot_list.pop(replace_pos) | ||
return empty_grid | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. мне кажется, что данный код не генерирует случайный массив, а только выбирает случайные точки из этого массива и, допустим, на n>=81 ответ будет один и тот же и массив не будет отличаться |
||
|
||
|
||
if __name__ == "__main__": | ||
|
@@ -159,4 +219,4 @@ def generate_sudoku(N: int) -> tp.List[tp.List[str]]: | |
if not solution: | ||
print(f"Puzzle {fname} can't be solved") | ||
else: | ||
display(solution) | ||
display(solution) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно упростить и объединить две строки (20 и 21) в grid = [int(char) for char in puzzle if char in '123456789.']