Skip to content
2 changes: 1 addition & 1 deletion .github/workflows/cs102.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
98 changes: 82 additions & 16 deletions homework02/sudoku.py
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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):
Expand All @@ -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]:
Expand All @@ -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]:
Expand All @@ -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]:
Expand All @@ -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']])
Expand All @@ -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]:
Expand All @@ -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. Найти все возможные значения, которые могут находиться на этой позиции
Expand All @@ -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]]:
Expand All @@ -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__":
Expand All @@ -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))
98 changes: 90 additions & 8 deletions homework03/life.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,45 +30,127 @@ def __init__(

def create_grid(self, randomize: bool = False) -> Grid:
# Copy from previous assignment
pass
if randomize:
return [[random.randint(0, 1) for i in range(self.rows)] for i in range(self.cols)]
else:
return [[0 for i in range(self.rows)] for i in range(self.cols)]

def get_neighbours(self, cell: Cell) -> Cells:
# Copy from previous assignment
pass
height, width = cell
cells = []
if height == 0 and width == 0:
cells.append(self.curr_generation[0][1])
cells.append(self.curr_generation[1][0])
cells.append(self.curr_generation[1][1])
elif height == 0 and width == self.cols - 1:
cells.append(self.curr_generation[0][width - 1])
cells.append(self.curr_generation[1][width])
cells.append(self.curr_generation[1][width - 1])
elif height == self.rows - 1 and width == 0:
cells.append(self.curr_generation[height - 1][0])
cells.append(self.curr_generation[height - 1][1])
cells.append(self.curr_generation[height][1])
elif height == self.rows - 1 and width == self.cols - 1:
cells.append(self.curr_generation[height - 1][width - 1])
cells.append(self.curr_generation[height][width - 1])
cells.append(self.curr_generation[height - 1][width])
elif height == 0:
cells.append(self.curr_generation[0][width - 1])
cells.append(self.curr_generation[0][width + 1])
cells.append(self.curr_generation[1][width])
cells.append(self.curr_generation[1][width - 1])
cells.append(self.curr_generation[1][width + 1])
elif width == 0:
cells.append(self.curr_generation[height - 1][0])
cells.append(self.curr_generation[height + 1][0])
cells.append(self.curr_generation[height][1])
cells.append(self.curr_generation[height - 1][1])
cells.append(self.curr_generation[height + 1][1])
elif height == self.rows - 1:
cells.append(self.curr_generation[height][width - 1])
cells.append(self.curr_generation[height][width + 1])
cells.append(self.curr_generation[height - 1][width])
cells.append(self.curr_generation[height - 1][width - 1])
cells.append(self.curr_generation[height - 1][width + 1])
elif width == self.cols - 1:
cells.append(self.curr_generation[height - 1][width])
cells.append(self.curr_generation[height + 1][width])
cells.append(self.curr_generation[height][width - 1])
cells.append(self.curr_generation[height - 1][width - 1])
cells.append(self.curr_generation[height + 1][width - 1])
else:
cells.append(self.curr_generation[height][width - 1])
cells.append(self.curr_generation[height][width + 1])
cells.append(self.curr_generation[height - 1][width])
cells.append(self.curr_generation[height + 1][width])
cells.append(self.curr_generation[height - 1][width - 1])
cells.append(self.curr_generation[height - 1][width + 1])
cells.append(self.curr_generation[height + 1][width + 1])
cells.append(self.curr_generation[height + 1][width - 1])
return cells

def get_next_generation(self) -> Grid:
# Copy from previous assignment
pass
next_grid = [[0 for i in range(self.cols)] for i in range(self.rows)]
for i in range(self.rows):
for j in range(self.cols):
if self.curr_generation[i][j] == 1 and (
sum(self.get_neighbours((i, j))) == 2 or sum(self.get_neighbours((i, j))) == 3
):
next_grid[i][j] = 1
elif self.curr_generation[i][j] == 0 and sum(self.get_neighbours((i, j))) == 3:
next_grid[i][j] = 1
else:
next_grid[i][j] = 0
return next_grid

def step(self) -> None:
"""
Выполнить один шаг игры.
"""
pass
self.prev_generation = self.curr_generation
self.curr_generation = self.get_next_generation()
self.generations += 1

@property
def is_max_generations_exceeded(self) -> bool:
"""
Не превысило ли текущее число поколений максимально допустимое.
"""
pass
if self.max_generations == self.generations:
return True
else:
return False

@property
def is_changing(self) -> bool:
"""
Изменилось ли состояние клеток с предыдущего шага.
"""
pass
if self.curr_generation == self.prev_generation:
return False
else:
return True

@staticmethod
def from_file(filename: pathlib.Path) -> "GameOfLife":
"""
Прочитать состояние клеток из указанного файла.
"""
pass
f = open(filename, "r").readlines()
grid = []
for i in range(len(f)):
r = list(map(int, f[i].split()))
grid.append(r)
life = GameOfLife(size=(len(grid), len(grid[0])), randomize=False)
life.curr_generation = grid
return life

def save(self, filename: pathlib.Path) -> None:
"""
Сохранить текущее состояние клеток в указанный файл.
"""
pass
with open(filename, "w") as f:
for i in self.curr_generation:
f.write("".join([str(j) for j in i]) + "\n")
28 changes: 23 additions & 5 deletions homework03/life_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,32 @@ def __init__(self, life: GameOfLife) -> None:
super().__init__(life)

def draw_borders(self, screen) -> None:
""" Отобразить рамку. """
pass
"""Отобразить рамку."""
screen.border("|", "|", "-", "-", "+", "+", "+", "+")

def draw_grid(self, screen) -> None:
""" Отобразить состояние клеток. """
pass
"""Отобразить состояние клеток."""
for i in range(self.life.rows):
for j in range(self.life.cols):
if self.life.curr_generation[i][j] == 0:
screen.addch(i + 1, j + 1, " ")
else:
screen.addch(i + 1, j + 1, "#")

def run(self) -> None:
screen = curses.initscr()
# PUT YOUR CODE HERE
curses.endwin()
while True:
self.draw_borders(screen)
self.draw_grid(screen)
screen.refresh()
self.life.step()
if screen.getch() == 32: # space
curses.endwin()
break


if __name__ == "__main__":
life = GameOfLife((100, 100), max_generations=50)
ui = Console(life)
ui.run()
Loading