From 1b7d323665c6f2ff06b8ebd132ae2a9e3af54b75 Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 16:35:38 +0300 Subject: [PATCH 1/5] s --- .idea/Chzh.iml | 14 ++++++++++++++ .idea/misc.xml | 4 ++++ .idea/workspace.xml | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 .idea/Chzh.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/Chzh.iml b/.idea/Chzh.iml new file mode 100644 index 0000000..8e5446a --- /dev/null +++ b/.idea/Chzh.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..cc2518d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..73eaf47 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + 1673962365428 + + + + \ No newline at end of file From c13c53878999b997c6d4390786b38049092180b5 Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 16:38:23 +0300 Subject: [PATCH 2/5] s --- .idea/.name | 1 + .idea/vcs.xml | 6 +++ homework02/sudoku.py | 93 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 .idea/.name create mode 100644 .idea/vcs.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..b15c5a4 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +sudoku.py \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/homework02/sudoku.py b/homework02/sudoku.py index df78ab1..87139b0 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): @@ -42,7 +43,16 @@ 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 + t: tp.List[T] = [] + res = [] + length = len(values) + for i in range(length): + if i % n == 0 and i != 0: + res.append(t) + t = [] + t.append(values[i]) + res.append(t) + return res def get_row(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: @@ -55,7 +65,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 +78,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 + res = [] + for arr in grid: + res.append(arr[pos[1]]) + return res def get_block(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: @@ -82,10 +95,16 @@ 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 + res = [] + r = pos[0] // 3 * 3 + c = pos[1] // 3 * 3 + for i in range(r, r + 3): + for j in range(c, c + 3): + res.append(grid[i][j]) + return res -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]]) -> tp.Tuple[int, int]: """Найти первую свободную позицию в пазле >>> find_empty_positions([['1', '2', '.'], ['4', '5', '6'], ['7', '8', '9']]) @@ -95,7 +114,13 @@ 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 + if grid != 0: + for i in range(len(grid)): + for j in range(len(grid[i])): + if not grid[i][j].isdigit(): + r, c = i, j + return r, c + return -1, -1 def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.Set[str]: @@ -109,11 +134,20 @@ def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) - >>> values == {'2', '5', '9'} True """ - pass + res = [] + for arr in range(1, 10): + s = str(arr) + if ( + s not in get_block(grid, pos) + and s not in get_col(grid, pos) + and s not in get_row(grid, pos) + ): + res.append(s) + return set(res) def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: - """ Решение пазла, заданного в grid """ + """Решение пазла, заданного в grid""" """ Как решать Судоку? 1. Найти свободную позицию 2. Найти все возможные значения, которые могут находиться на этой позиции @@ -125,13 +159,36 @@ 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 = find_empty_positions(grid) # type: tp.Tuple[int, int] + if empty[0] < 0: + return grid + possible = find_possible_values(grid, empty) # type: tp.Set[str] + if not possible: + return None + for i in possible: + grid[empty[0]][empty[1]] = i + if solve(grid): + return grid + grid[empty[0]][empty[1]] = "." + return None def check_solution(solution: tp.List[tp.List[str]]) -> bool: - """ Если решение solution верно, то вернуть True, в противном случае False """ + """Если решение solution верно, то вернуть True, в противном случае False""" # TODO: Add doctests with bad puzzles - pass + g = solution + for i in range(9): + if len(get_row(g, (i, 0))) != len(set(get_row(g, (i, 0)))) or len( + get_col(g, (0, i)) + ) != len(set(get_col(g, (0, i)))): + return False + if i == 0 or i == 3 or i == 6: + for j in range(0, 9, 3): + if len(get_block(g, (i, j))) != len(set(get_block(g, (i, j)))): + return False + if "." in g[i]: + return False + return True def generate_sudoku(N: int) -> tp.List[tp.List[str]]: @@ -156,7 +213,15 @@ def generate_sudoku(N: int) -> tp.List[tp.List[str]]: >>> check_solution(solution) True """ - pass + grid = [["."] * 9 for _ in range(9)] + solve(grid) + count, N = 0, min(81, N) + while count < 81 - N: + i, j = random.randint(0, 8), random.randint(0, 8) + if grid[i][j] != ".": + grid[i][j] = "." + count += 1 + return grid if __name__ == "__main__": From 182cf8424ddd47d7a3594c9b376f34416af9f3b7 Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 16:40:40 +0300 Subject: [PATCH 3/5] s --- .github/workflows/cs102.yml | 4 +-- .../inspectionProfiles/profiles_settings.xml | 6 ++++ .idea/workspace.xml | 34 +++++++++++++++++++ main.py | 16 +++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 main.py diff --git a/.github/workflows/cs102.yml b/.github/workflows/cs102.yml index 4509ac4..8e1e8be 100644 --- a/.github/workflows/cs102.yml +++ b/.github/workflows/cs102.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up Python 3.8.6 + - name: Set up Python 3.10.7 uses: actions/setup-python@v2 with: - python-version: '3.8.6' + python-version: '3.10.7' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 73eaf47..adbc062 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -22,6 +22,29 @@ + + + + @@ -33,4 +56,15 @@ + + + + + file://$PROJECT_DIR$/main.py + 8 + + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..5596b44 --- /dev/null +++ b/main.py @@ -0,0 +1,16 @@ +# This is a sample Python script. + +# Press Shift+F10 to execute it or replace it with your code. +# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. + + +def print_hi(name): + # Use a breakpoint in the code line below to debug your script. + print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint. + + +# Press the green button in the gutter to run the script. +if __name__ == '__main__': + print_hi('PyCharm') + +# See PyCharm help at https://www.jetbrains.com/help/pycharm/ From 72675ce86e5ea98bbc09077570949818fc1ee3c2 Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 16:43:53 +0300 Subject: [PATCH 4/5] s --- homework03/life.py | 97 +++++++++++++++++++++++++++----------- homework03/life_console.py | 6 ++- homework03/life_gui.py | 81 ++++++++++++++++++++++++++++--- homework03/life_proto.py | 85 +++++++++++++++++++++++++++++---- homework03/test.py | 0 5 files changed, 226 insertions(+), 43 deletions(-) create mode 100644 homework03/test.py diff --git a/homework03/life.py b/homework03/life.py index 7aef0b6..ce39855 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -15,7 +15,7 @@ def __init__( self, size: tp.Tuple[int, int], randomize: bool = True, - max_generations: tp.Optional[float] = float("inf"), + max_generations: float = float("inf"), ) -> None: # Размер клеточного поля self.rows, self.cols = size @@ -29,46 +29,89 @@ def __init__( self.generations = 1 def create_grid(self, randomize: bool = False) -> Grid: - # Copy from previous assignment - pass + grid: Grid = [] + for i in range(self.rows): + grid.append([]) + for j in range(self.cols): + grid[i].append(random.randint(0, int(randomize))) + + return grid def get_neighbours(self, cell: Cell) -> Cells: - # Copy from previous assignment - pass + row, col = cell + answer = [] + + positions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] + for i in positions: + newrow, newcol = row + i[0], col + i[1] + if newrow < 0 or newrow >= self.rows or newcol < 0 or newcol >= self.cols: + continue + answer.append(self.curr_generation[newrow][newcol]) + return answer def get_next_generation(self) -> Grid: - # Copy from previous assignment - pass + newgrid: Grid = [] + + for i in range(self.rows): + newgrid.append([]) + for j in range(self.cols): + neighbours = self.get_neighbours((i, j)) + count = 0 + for n in neighbours: + if n: + count += 1 + + if count == 2 and self.curr_generation[i][j]: + newgrid[i].append(1) + continue + + if count == 3: + newgrid[i].append(1) + continue + + newgrid[i].append(0) + + return newgrid def step(self) -> None: - """ - Выполнить один шаг игры. - """ - pass + self.prev_generation = [[j for j in i] for i in self.curr_generation] + self.curr_generation = self.get_next_generation() + self.generations += 1 @property def is_max_generations_exceeded(self) -> bool: - """ - Не превысило ли текущее число поколений максимально допустимое. - """ - pass + return self.generations >= self.max_generations @property def is_changing(self) -> bool: - """ - Изменилось ли состояние клеток с предыдущего шага. - """ - pass + return self.curr_generation != self.prev_generation @staticmethod def from_file(filename: pathlib.Path) -> "GameOfLife": - """ - Прочитать состояние клеток из указанного файла. - """ - pass + f = open(filename, "r") + + grid: Grid = [] + for i in f.readlines(): + grid.append([]) + for j in i: + grid[-1].append(int(j)) + + rows = len(grid) + cols = len(grid[0]) + + a = GameOfLife(size=(rows, cols), randomize=False) + a.curr_generation = grid + + f.close() + + return a def save(self, filename: pathlib.Path) -> None: - """ - Сохранить текущее состояние клеток в указанный файл. - """ - pass + f = open(filename, "w") + + for i in self.curr_generation: + for j in i: + f.write(str(j)) + f.write("\n") + + f.close() diff --git a/homework03/life_console.py b/homework03/life_console.py index ddeb9ef..e12d625 100644 --- a/homework03/life_console.py +++ b/homework03/life_console.py @@ -9,11 +9,13 @@ def __init__(self, life: GameOfLife) -> None: super().__init__(life) def draw_borders(self, screen) -> None: - """ Отобразить рамку. """ + + """Отобразить рамку.""" pass def draw_grid(self, screen) -> None: - """ Отобразить состояние клеток. """ + + """Отобразить состояние клеток.""" pass def run(self) -> None: diff --git a/homework03/life_gui.py b/homework03/life_gui.py index 1126b29..d0a3a8d 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -6,16 +6,85 @@ class GUI(UI): def __init__(self, life: GameOfLife, cell_size: int = 10, speed: int = 10) -> None: + self.speed = speed + self.cell_size = cell_size + self.width = life.cols * cell_size + self.height = life.rows * cell_size + self.screen = pygame.display.set_mode((self.width, self.height)) super().__init__(life) def draw_lines(self) -> None: - # Copy from previous assignment - pass + """Отрисовать сетку""" + for x in range(0, self.width, self.cell_size): + pygame.draw.line(self.screen, pygame.Color("black"), (x, 0), (x, self.height)) + for y in range(0, self.height, self.cell_size): + pygame.draw.line(self.screen, pygame.Color("black"), (0, y), (self.width, y)) def draw_grid(self) -> None: - # Copy from previous assignment - pass + for i in range(self.life.rows): + for j in range(self.life.cols): + if self.life.curr_generation[i][j]: + pygame.draw.rect( + self.screen, + pygame.Color("green"), + (j * self.cell_size, i * self.cell_size, self.cell_size, self.cell_size), + ) + else: + pygame.draw.rect( + self.screen, + pygame.Color("white"), + (j * self.cell_size, i * self.cell_size, self.cell_size, self.cell_size), + ) def run(self) -> None: - # Copy from previous assignment - pass + """Запустить игру""" + pygame.init() + clock = pygame.time.Clock() + self.screen = pygame.display.set_mode((self.width, self.height)) + pygame.display.set_caption("Game of Life") + self.screen.fill(pygame.Color("white")) + + self.draw_grid() + self.draw_lines() + + running = True + pause = False + while running: + for event in pygame.event.get(): + if event.type == pygame.constants.QUIT: + running = False + if event.type == pygame.MOUSEBUTTONUP: + if not pause: + continue + + y, x = pygame.mouse.get_pos() + x //= self.cell_size + y //= self.cell_size + + if self.life.curr_generation[x][y]: + self.life.curr_generation[x][y] = 0 + else: + self.life.curr_generation[x][y] = 1 + self.draw_grid() + self.draw_lines() + + pygame.display.flip() + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_SPACE: + pause = not pause + + if not pause: + if not self.life.is_max_generations_exceeded and self.life.is_changing: + self.life.step() + self.draw_grid() + self.draw_lines() + + pygame.display.flip() + clock.tick(self.speed) + pygame.quit() + + +proto = GameOfLife(size=(20, 20), randomize=False) +game = GUI(cell_size=40, life=proto) +game.run() diff --git a/homework03/life_proto.py b/homework03/life_proto.py index c6d6010..7e81147 100644 --- a/homework03/life_proto.py +++ b/homework03/life_proto.py @@ -30,27 +30,30 @@ def __init__( self.speed = speed def draw_lines(self) -> None: - """ Отрисовать сетку """ + + """Отрисовать сетку""" for x in range(0, self.width, self.cell_size): pygame.draw.line(self.screen, pygame.Color("black"), (x, 0), (x, self.height)) for y in range(0, self.height, self.cell_size): pygame.draw.line(self.screen, pygame.Color("black"), (0, y), (self.width, y)) def run(self) -> None: - """ Запустить игру """ + + """Запустить игру""" pygame.init() clock = pygame.time.Clock() pygame.display.set_caption("Game of Life") self.screen.fill(pygame.Color("white")) # Создание списка клеток - # PUT YOUR CODE HERE + self.grid = self.create_grid(randomize=True) running = True while running: for event in pygame.event.get(): - if event.type == QUIT: + if event.type == pygame.QUIT: running = False + self.draw_grid() self.draw_lines() # Отрисовка списка клеток @@ -79,13 +82,30 @@ def create_grid(self, randomize: bool = False) -> Grid: out : Grid Матрица клеток размером `cell_height` х `cell_width`. """ - pass + return [ + [random.choice((0, 1)) if randomize else 0 for i in range(self.cell_height)] + for j in range(self.cell_width) + ] def draw_grid(self) -> None: """ Отрисовка списка клеток с закрашиванием их в соответствующе цвета. """ - pass + for i in range(self.cell_width): + for j in range(self.cell_height): + if self.grid[i][j] == 1: + pygame.draw.rect( + self.screen, + pygame.Color("green"), + (i * self.cell_size, j * self.cell_size, self.cell_size, self.cell_size), + ) + else: + pygame.draw.rect( + self.screen, + pygame.Color("white"), + (i * self.cell_size, j * self.cell_size, self.cell_size, self.cell_size), + ) + return def get_neighbours(self, cell: Cell) -> Cells: """ @@ -105,7 +125,36 @@ def get_neighbours(self, cell: Cell) -> Cells: out : Cells Список соседних клеток. """ - pass + g = self.grid + a = cell[0] + b = cell[1] + if cell[0] == 0 and cell[1] == 0: + return [g[a][b + 1], g[a + 1][b + 1], g[a + 1][b]] + elif cell[0] == 0 and cell[1] == self.cell_width - 1: + return [g[a][b - 1], g[a + 1][b - 1], g[a + 1][b]] + elif cell[0] == self.cell_height - 1 and cell[1] == 0: + return [g[a - 1][b], g[a - 1][b + 1], g[a][b + 1]] + elif cell[0] == self.cell_height - 1 and cell[1] == self.cell_width - 1: + return [g[a - 1][b], g[a - 1][b - 1], g[a][b - 1]] + elif cell[0] == 0 and cell[1] != 0 and cell[1] != self.cell_width - 1: + return [g[a][b - 1], g[a + 1][b - 1], g[a + 1][b], g[a + 1][b + 1], g[a][b + 1]] + elif cell[0] != 0 and cell[0] != self.cell_height - 1 and cell[1] == 0: + return [g[a - 1][b], g[a - 1][b + 1], g[a][b + 1], g[a + 1][b + 1], g[a + 1][b]] + elif cell[0] == self.cell_height - 1 and cell[1] != 0 and cell[1] != self.cell_width - 1: + return [g[a][b - 1], g[a - 1][b - 1], g[a - 1][b], g[a - 1][b + 1], g[a][b + 1]] + elif cell[0] != 0 and cell[0] != self.cell_height and cell[1] == self.cell_width - 1: + return [g[a - 1][b], g[a - 1][b - 1], g[a][b - 1], g[a + 1][b - 1], g[a + 1][b]] + else: + return [ + g[a - 1][b - 1], + g[a - 1][b], + g[a - 1][b + 1], + g[a][b + 1], + g[a + 1][b + 1], + g[a + 1][b], + g[a + 1][b - 1], + g[a][b - 1], + ] def get_next_generation(self) -> Grid: """ @@ -116,4 +165,24 @@ def get_next_generation(self) -> Grid: out : Grid Новое поколение клеток. """ - pass + + res = [] + for i in range(self.cell_height): + row = [] + for j in range(self.cell_width): + row.append(self.grid[i][j]) + res.append(row) + for i in range(self.cell_height): + for j in range(self.cell_width): + if ( + sum(self.get_neighbours((i, j))) != 2 and sum(self.get_neighbours((i, j))) != 3 + ) and self.grid[i][j] == 1: + res[i][j] = 0 + elif sum(self.get_neighbours((i, j))) == 3 and self.grid[i][j] == 0: + res[i][j] = 1 + + return res + + +# game = GameOfLife(cell_size=40) +# game.run() diff --git a/homework03/test.py b/homework03/test.py new file mode 100644 index 0000000..e69de29 From bdb4b69b824a20884fc0c5577a09b7c7ec51d9d0 Mon Sep 17 00:00:00 2001 From: MaJaneb Date: Tue, 17 Jan 2023 17:01:01 +0300 Subject: [PATCH 5/5] s --- homework03/life.py | 118 +++++++++++++++++-------------------- homework03/life_console.py | 61 ++++++++++++++++--- homework03/life_gui.py | 82 +++++++++++--------------- homework03/life_proto.py | 108 +++++++++++++-------------------- 4 files changed, 182 insertions(+), 187 deletions(-) diff --git a/homework03/life.py b/homework03/life.py index ce39855..0ef18e3 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -2,9 +2,6 @@ import random import typing as tp -import pygame -from pygame.locals import * - Cell = tp.Tuple[int, int] Cells = tp.List[int] Grid = tp.List[Cells] @@ -29,89 +26,80 @@ def __init__( self.generations = 1 def create_grid(self, randomize: bool = False) -> Grid: - grid: Grid = [] - for i in range(self.rows): - grid.append([]) - for j in range(self.cols): - grid[i].append(random.randint(0, int(randomize))) + if randomize: + grid = [[random.randint(0, 1) for _ in range(self.cols)] for _ in range(self.rows)] + else: + grid = [[0 for _ in range(self.cols)] for _ in range(self.rows)] return grid def get_neighbours(self, cell: Cell) -> Cells: - row, col = cell - answer = [] - - positions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] - for i in positions: - newrow, newcol = row + i[0], col + i[1] - if newrow < 0 or newrow >= self.rows or newcol < 0 or newcol >= self.cols: - continue - answer.append(self.curr_generation[newrow][newcol]) - return answer + neighbours = [] + y_borders = [max(0, cell[0] - 1), min(self.rows, cell[0] + 2)] + x_borders = [max(0, cell[1] - 1), min(self.cols, cell[1] + 2)] + for i in range(*y_borders): + neighbours += self.curr_generation[i][x_borders[0] : x_borders[1]] + neighbours.remove(self.curr_generation[cell[0]][cell[1]]) + return neighbours def get_next_generation(self) -> Grid: - newgrid: Grid = [] - + new_generation = self.create_grid(randomize=False) for i in range(self.rows): - newgrid.append([]) for j in range(self.cols): - neighbours = self.get_neighbours((i, j)) - count = 0 - for n in neighbours: - if n: - count += 1 + n_neighbours = sum(self.get_neighbours((i, j))) + if self.curr_generation[i][j] and 2 <= n_neighbours <= 3: + new_generation[i][j] = 1 + elif not self.curr_generation[i][j] and n_neighbours == 3: + new_generation[i][j] = 1 - if count == 2 and self.curr_generation[i][j]: - newgrid[i].append(1) - continue - - if count == 3: - newgrid[i].append(1) - continue - - newgrid[i].append(0) - - return newgrid + return new_generation def step(self) -> None: - self.prev_generation = [[j for j in i] for i in self.curr_generation] - self.curr_generation = self.get_next_generation() + """ + Выполнить один шаг игры. + """ self.generations += 1 + self.prev_generation = self.curr_generation + self.curr_generation = self.get_next_generation() @property def is_max_generations_exceeded(self) -> bool: + """ + Не превысило ли текущее число поколений максимально допустимое. + """ return self.generations >= self.max_generations @property def is_changing(self) -> bool: - return self.curr_generation != self.prev_generation + """ + Изменилось ли состояние клеток с предыдущего шага. + """ + return self.prev_generation != self.curr_generation or self.generations == 1 @staticmethod def from_file(filename: pathlib.Path) -> "GameOfLife": - f = open(filename, "r") - - grid: Grid = [] - for i in f.readlines(): - grid.append([]) - for j in i: - grid[-1].append(int(j)) - - rows = len(grid) - cols = len(grid[0]) - - a = GameOfLife(size=(rows, cols), randomize=False) - a.curr_generation = grid - - f.close() - - return a + """ + Прочитать состояние клеток из указанного файла. + """ + try: + with filename.open() as f: + grid = list(map(lambda x: list(map(int, list(x))), f.read().split())) + loaded_game = GameOfLife((len(grid), len(grid[0])), randomize=False) + loaded_game.curr_generation = grid + + except (ValueError, IndexError): + print("Something wrong with file") + return GameOfLife((13, 13)) + except Exception as error: + print(error) + return GameOfLife((13, 13)) + else: + return loaded_game def save(self, filename: pathlib.Path) -> None: - f = open(filename, "w") - - for i in self.curr_generation: - for j in i: - f.write(str(j)) - f.write("\n") - - f.close() + """ + Сохранить текущее состояние клеток в указанный файл. + """ + with filename.open("w") as f: + for cells in self.curr_generation: + f.write("".join(map(str, cells)) + "\n") diff --git a/homework03/life_console.py b/homework03/life_console.py index e12d625..79ab954 100644 --- a/homework03/life_console.py +++ b/homework03/life_console.py @@ -1,24 +1,71 @@ import curses +import pygame from life import GameOfLife from ui import UI class Console(UI): - def __init__(self, life: GameOfLife) -> None: + def __init__(self, life: GameOfLife, speed=10) -> None: super().__init__(life) + self.speed = speed def draw_borders(self, screen) -> None: - """Отобразить рамку.""" - pass + screen.addstr(f"+{'-' * self.life.cols}+\n") + for i in range(self.life.rows): + screen.addstr("|") + screen.addstr(i + 1, self.life.cols + 1, "|\n") + screen.addstr(f"+{'-' * self.life.cols}+\n") def draw_grid(self, screen) -> None: - """Отобразить состояние клеток.""" - pass + for row in range(self.life.rows): + for col in range(self.life.cols): + if self.life.curr_generation[row][col]: + screen.move(row + 1, col + 1) + screen.addstr("*") def run(self) -> None: + clock = pygame.time.Clock() + screen = curses.initscr() - # PUT YOUR CODE HERE - curses.endwin() + curses.noecho() + curses.cbreak() + screen.nodelay(True) + curses.curs_set(False) + try: + running = True + pause = False + while running: + key = screen.getch() + while key != -1: + if key == 113: + running = False + elif key == 32: + pause = not pause + key = screen.getch() + screen.clear() + self.draw_borders(screen) + self.draw_grid(screen) + if not pause: + self.life.step() + screen.refresh() + clock.tick(self.speed) + + if not self.life.is_changing or self.life.is_max_generations_exceeded: + running = False + curses.echo() + curses.nocbreak() + curses.endwin() + + except Exception as err: + print(f"{err}\n{type(err)}") + curses.echo() + curses.nocbreak() + curses.endwin() + + +if __name__ == "__main__": + life = Console(GameOfLife((32, 32))) + life.run() diff --git a/homework03/life_gui.py b/homework03/life_gui.py index d0a3a8d..c7d054d 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -6,12 +6,20 @@ class GUI(UI): def __init__(self, life: GameOfLife, cell_size: int = 10, speed: int = 10) -> None: - self.speed = speed + super().__init__(life) self.cell_size = cell_size - self.width = life.cols * cell_size - self.height = life.rows * cell_size + self.width = self.cell_size * self.life.cols + self.height = self.cell_size * self.life.rows + + # Создание нового окна self.screen = pygame.display.set_mode((self.width, self.height)) - super().__init__(life) + + # Вычисляем количество ячеек по вертикали и горизонтали + self.cell_width = self.width // self.cell_size + self.cell_height = self.height // self.cell_size + + # Скорость протекания игры + self.speed = speed def draw_lines(self) -> None: """Отрисовать сетку""" @@ -21,70 +29,50 @@ def draw_lines(self) -> None: pygame.draw.line(self.screen, pygame.Color("black"), (0, y), (self.width, y)) def draw_grid(self) -> None: + """ + Отрисовка списка клеток с закрашиванием их в соответствующе цвета. + """ + color = (0, 255, 0) for i in range(self.life.rows): for j in range(self.life.cols): if self.life.curr_generation[i][j]: pygame.draw.rect( self.screen, - pygame.Color("green"), - (j * self.cell_size, i * self.cell_size, self.cell_size, self.cell_size), - ) - else: - pygame.draw.rect( - self.screen, - pygame.Color("white"), - (j * self.cell_size, i * self.cell_size, self.cell_size, self.cell_size), + color, + pygame.Rect( + self.cell_size * j, + self.cell_size * i, + self.cell_size, + self.cell_size, + ), ) def run(self) -> None: """Запустить игру""" pygame.init() clock = pygame.time.Clock() - self.screen = pygame.display.set_mode((self.width, self.height)) pygame.display.set_caption("Game of Life") self.screen.fill(pygame.Color("white")) - self.draw_grid() - self.draw_lines() - running = True - pause = False while running: for event in pygame.event.get(): - if event.type == pygame.constants.QUIT: + if event.type == pygame.QUIT: running = False - if event.type == pygame.MOUSEBUTTONUP: - if not pause: - continue - - y, x = pygame.mouse.get_pos() - x //= self.cell_size - y //= self.cell_size - - if self.life.curr_generation[x][y]: - self.life.curr_generation[x][y] = 0 - else: - self.life.curr_generation[x][y] = 1 - self.draw_grid() - self.draw_lines() - - pygame.display.flip() + self.screen.fill(pygame.Color("white")) - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_SPACE: - pause = not pause + self.draw_grid() + self.draw_lines() + self.life.step() - if not pause: - if not self.life.is_max_generations_exceeded and self.life.is_changing: - self.life.step() - self.draw_grid() - self.draw_lines() + pygame.display.flip() + clock.tick(self.speed) - pygame.display.flip() - clock.tick(self.speed) + if not self.life.is_changing or self.life.is_max_generations_exceeded: + running = False pygame.quit() -proto = GameOfLife(size=(20, 20), randomize=False) -game = GUI(cell_size=40, life=proto) -game.run() +if __name__ == "__main__": + life = GUI(GameOfLife((32, 32))) + life.run() diff --git a/homework03/life_proto.py b/homework03/life_proto.py index 7e81147..7ad76d8 100644 --- a/homework03/life_proto.py +++ b/homework03/life_proto.py @@ -30,7 +30,6 @@ def __init__( self.speed = speed def draw_lines(self) -> None: - """Отрисовать сетку""" for x in range(0, self.width, self.cell_size): pygame.draw.line(self.screen, pygame.Color("black"), (x, 0), (x, self.height)) @@ -38,7 +37,6 @@ def draw_lines(self) -> None: pygame.draw.line(self.screen, pygame.Color("black"), (0, y), (self.width, y)) def run(self) -> None: - """Запустить игру""" pygame.init() clock = pygame.time.Clock() @@ -53,12 +51,11 @@ def run(self) -> None: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False + self.screen.fill(pygame.Color("white")) + self.draw_grid() self.draw_lines() - - # Отрисовка списка клеток - # Выполнение одного шага игры (обновление состояния ячеек) - # PUT YOUR CODE HERE + self.grid = self.get_next_generation() pygame.display.flip() clock.tick(self.speed) @@ -82,30 +79,34 @@ def create_grid(self, randomize: bool = False) -> Grid: out : Grid Матрица клеток размером `cell_height` х `cell_width`. """ - return [ - [random.choice((0, 1)) if randomize else 0 for i in range(self.cell_height)] - for j in range(self.cell_width) - ] + if randomize: + grid = [ + [random.randint(0, 1) for _ in range(self.cell_width)] + for _ in range(self.cell_height) + ] + else: + grid = [[0 for _ in range(self.cell_width)] for _ in range(self.cell_height)] + + return grid def draw_grid(self) -> None: """ Отрисовка списка клеток с закрашиванием их в соответствующе цвета. """ - for i in range(self.cell_width): - for j in range(self.cell_height): - if self.grid[i][j] == 1: - pygame.draw.rect( - self.screen, - pygame.Color("green"), - (i * self.cell_size, j * self.cell_size, self.cell_size, self.cell_size), - ) - else: + color = (0, 255, 0) + for i in range(self.cell_height): + for j in range(self.cell_width): + if self.grid[i][j]: pygame.draw.rect( self.screen, - pygame.Color("white"), - (i * self.cell_size, j * self.cell_size, self.cell_size, self.cell_size), + color, + pygame.Rect( + self.cell_size * j, + self.cell_size * i, + self.cell_size, + self.cell_size, + ), ) - return def get_neighbours(self, cell: Cell) -> Cells: """ @@ -125,36 +126,13 @@ def get_neighbours(self, cell: Cell) -> Cells: out : Cells Список соседних клеток. """ - g = self.grid - a = cell[0] - b = cell[1] - if cell[0] == 0 and cell[1] == 0: - return [g[a][b + 1], g[a + 1][b + 1], g[a + 1][b]] - elif cell[0] == 0 and cell[1] == self.cell_width - 1: - return [g[a][b - 1], g[a + 1][b - 1], g[a + 1][b]] - elif cell[0] == self.cell_height - 1 and cell[1] == 0: - return [g[a - 1][b], g[a - 1][b + 1], g[a][b + 1]] - elif cell[0] == self.cell_height - 1 and cell[1] == self.cell_width - 1: - return [g[a - 1][b], g[a - 1][b - 1], g[a][b - 1]] - elif cell[0] == 0 and cell[1] != 0 and cell[1] != self.cell_width - 1: - return [g[a][b - 1], g[a + 1][b - 1], g[a + 1][b], g[a + 1][b + 1], g[a][b + 1]] - elif cell[0] != 0 and cell[0] != self.cell_height - 1 and cell[1] == 0: - return [g[a - 1][b], g[a - 1][b + 1], g[a][b + 1], g[a + 1][b + 1], g[a + 1][b]] - elif cell[0] == self.cell_height - 1 and cell[1] != 0 and cell[1] != self.cell_width - 1: - return [g[a][b - 1], g[a - 1][b - 1], g[a - 1][b], g[a - 1][b + 1], g[a][b + 1]] - elif cell[0] != 0 and cell[0] != self.cell_height and cell[1] == self.cell_width - 1: - return [g[a - 1][b], g[a - 1][b - 1], g[a][b - 1], g[a + 1][b - 1], g[a + 1][b]] - else: - return [ - g[a - 1][b - 1], - g[a - 1][b], - g[a - 1][b + 1], - g[a][b + 1], - g[a + 1][b + 1], - g[a + 1][b], - g[a + 1][b - 1], - g[a][b - 1], - ] + neighbours = [] + y_borders = [max(0, cell[0] - 1), min(self.cell_height, cell[0] + 2)] + x_borders = [max(0, cell[1] - 1), min(self.cell_width, cell[1] + 2)] + for i in range(*y_borders): + neighbours += self.grid[i][x_borders[0] : x_borders[1]] + neighbours.remove(self.grid[cell[0]][cell[1]]) + return neighbours def get_next_generation(self) -> Grid: """ @@ -165,24 +143,18 @@ def get_next_generation(self) -> Grid: out : Grid Новое поколение клеток. """ - - res = [] - for i in range(self.cell_height): - row = [] - for j in range(self.cell_width): - row.append(self.grid[i][j]) - res.append(row) + new_generation = self.create_grid(randomize=False) for i in range(self.cell_height): for j in range(self.cell_width): - if ( - sum(self.get_neighbours((i, j))) != 2 and sum(self.get_neighbours((i, j))) != 3 - ) and self.grid[i][j] == 1: - res[i][j] = 0 - elif sum(self.get_neighbours((i, j))) == 3 and self.grid[i][j] == 0: - res[i][j] = 1 + n_neighbours = sum(self.get_neighbours((i, j))) + if self.grid[i][j] and 2 <= n_neighbours <= 3: + new_generation[i][j] = 1 + elif not self.grid[i][j] and n_neighbours == 3: + new_generation[i][j] = 1 - return res + return new_generation -# game = GameOfLife(cell_size=40) -# game.run() +if __name__ == "__main__": + life = GameOfLife() + life.run()