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/homework00/hello.py b/homework00/hello.py index efe8767..5802c29 100644 --- a/homework00/hello.py +++ b/homework00/hello.py @@ -1,5 +1,5 @@ def get_greeting(name: str) -> str: - pass + return f"Hello, {name}!" if __name__ == "__main__": diff --git a/homework01/caesar.py b/homework01/caesar.py index 09c3681..7b05368 100644 --- a/homework01/caesar.py +++ b/homework01/caesar.py @@ -1,46 +1,28 @@ import typing as tp +from string import ascii_lowercase as lower +from string import ascii_uppercase as upper def encrypt_caesar(plaintext: str, shift: int = 3) -> str: - """ - Encrypts plaintext using a Caesar cipher. - - >>> encrypt_caesar("PYTHON") - 'SBWKRQ' - >>> encrypt_caesar("python") - 'sbwkrq' - >>> encrypt_caesar("Python3.6") - 'Sbwkrq3.6' - >>> encrypt_caesar("") - '' - """ ciphertext = "" - # PUT YOUR CODE HERE + for char in plaintext: + if char.isalpha(): + if char.islower(): + char = lower[(lower.index(char) + shift) % 26] + else: + char = upper[(upper.index(char) + shift) % 26] + ciphertext += char return ciphertext def decrypt_caesar(ciphertext: str, shift: int = 3) -> str: - """ - Decrypts a ciphertext using a Caesar cipher. - - >>> decrypt_caesar("SBWKRQ") - 'PYTHON' - >>> decrypt_caesar("sbwkrq") - 'python' - >>> decrypt_caesar("Sbwkrq3.6") - 'Python3.6' - >>> decrypt_caesar("") - '' - """ - plaintext = "" - # PUT YOUR CODE HERE - return plaintext + return encrypt_caesar(ciphertext, -shift) def caesar_breaker_brute_force(ciphertext: str, dictionary: tp.Set[str]) -> int: - """ - Brute force breaking a Caesar cipher. - """ best_shift = 0 - # PUT YOUR CODE HERE + for shift in range(26): + decrypted_msg = decrypt_caesar(ciphertext, shift) + if decrypted_msg in dictionary: + best_shift = shift return best_shift diff --git a/homework01/rsa.py b/homework01/rsa.py index b777be5..1d56554 100644 --- a/homework01/rsa.py +++ b/homework01/rsa.py @@ -3,43 +3,32 @@ def is_prime(n: int) -> bool: - """ - Tests to see if a number is prime. - - >>> is_prime(2) - True - >>> is_prime(11) - True - >>> is_prime(8) - False - """ - # PUT YOUR CODE HERE - pass + for div in range(2, int(n**0.5) + 1): + if n % div == 0: + return False + return n != 1 def gcd(a: int, b: int) -> int: - """ - Euclid's algorithm for determining the greatest common divisor. + if b == 0: + return a + return gcd(b, a % b) - >>> gcd(12, 15) - 3 - >>> gcd(3, 7) - 1 - """ - # PUT YOUR CODE HERE - pass +def gcdex(a, b): + if b == 0: + return a, 1, 0 + else: + d, x, y = gcdex(b, a % b) + return d, y, x - y * (a // b) -def multiplicative_inverse(e: int, phi: int) -> int: - """ - Euclid's extended algorithm for finding the multiplicative - inverse of two numbers. - >>> multiplicative_inverse(7, 40) - 23 - """ - # PUT YOUR CODE HERE - pass +def multiplicative_inverse(a: int, n: int) -> int: + d, x, y = gcdex(a, n) + if d == 1: + return (x % n + n) % n + else: + return 0 def generate_keypair(p: int, q: int) -> tp.Tuple[tp.Tuple[int, int], tp.Tuple[int, int]]: @@ -49,10 +38,10 @@ def generate_keypair(p: int, q: int) -> tp.Tuple[tp.Tuple[int, int], tp.Tuple[in raise ValueError("p and q cannot be equal") # n = pq - # PUT YOUR CODE HERE + n = p * q # phi = (p-1)(q-1) - # PUT YOUR CODE HERE + phi = (p - 1) * (q - 1) # Choose an integer e such that e and phi(n) are coprime e = random.randrange(1, phi) @@ -85,7 +74,7 @@ def decrypt(pk: tp.Tuple[int, int], ciphertext: tp.List[int]) -> str: # Unpack the key into its components key, n = pk # Generate the plaintext based on the ciphertext and key using a^b mod m - plain = [chr((char ** key) % n) for char in ciphertext] + plain = [chr((char**key) % n) for char in ciphertext] # Return the array of bytes as a string return "".join(plain) diff --git a/homework01/vigenere.py b/homework01/vigenere.py index e51742e..1cbb09d 100644 --- a/homework01/vigenere.py +++ b/homework01/vigenere.py @@ -1,30 +1,18 @@ -def encrypt_vigenere(plaintext: str, keyword: str) -> str: - """ - Encrypts plaintext using a Vigenere cipher. +from caesar import decrypt_caesar, encrypt_caesar - >>> encrypt_vigenere("PYTHON", "A") - 'PYTHON' - >>> encrypt_vigenere("python", "a") - 'python' - >>> encrypt_vigenere("ATTACKATDAWN", "LEMON") - 'LXFOPVEFRNHR' - """ - ciphertext = "" - # PUT YOUR CODE HERE - return ciphertext +def encrypt_vigenere(plaintext: str, keyword: str, encode: bool = True) -> str: + ciphertext = [] + for index, char in enumerate(plaintext.lower()): + shift = ord(keyword[index % len(keyword)].lower()) - 97 + if encode: + ciphertext.append(encrypt_caesar(char, shift)) + else: + ciphertext.append(decrypt_caesar(char, shift)) + if plaintext[index].isupper(): + ciphertext[-1] = ciphertext[-1].upper() + return "".join(ciphertext) -def decrypt_vigenere(ciphertext: str, keyword: str) -> str: - """ - Decrypts a ciphertext using a Vigenere cipher. - >>> decrypt_vigenere("PYTHON", "A") - 'PYTHON' - >>> decrypt_vigenere("python", "a") - 'python' - >>> decrypt_vigenere("LXFOPVEFRNHR", "LEMON") - 'ATTACKATDAWN' - """ - plaintext = "" - # PUT YOUR CODE HERE - return plaintext +def decrypt_vigenere(ciphertext: str, keyword: str) -> str: + return encrypt_vigenere(ciphertext, keyword, encode=False) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index df78ab1..6de9720 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -1,11 +1,12 @@ import pathlib import typing as tp +from random import randint 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): @@ -34,129 +35,95 @@ def display(grid: tp.List[tp.List[str]]) -> None: 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]] - >>> group([1,2,3,4,5,6,7,8,9], 3) - [[1, 2, 3], [4, 5, 6], [7, 8, 9]] - """ - pass + """Сгруппировать значения values в список, состоящий из списков по n элементов""" + res = [] + tmp = [] + for value in values: + tmp.append(value) + if len(tmp) == n: + res.append(tmp) + tmp = [] + return res def get_row(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: - """Возвращает все значения для номера строки, указанной в pos - - >>> get_row([['1', '2', '.'], ['4', '5', '6'], ['7', '8', '9']], (0, 0)) - ['1', '2', '.'] - >>> get_row([['1', '2', '3'], ['4', '.', '6'], ['7', '8', '9']], (1, 0)) - ['4', '.', '6'] - >>> get_row([['1', '2', '3'], ['4', '5', '6'], ['.', '8', '9']], (2, 0)) - ['.', '8', '9'] - """ - pass + """Возвращает все значения для номера строки, указанной в pos""" + return grid[pos[0]] def get_col(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: - """Возвращает все значения для номера столбца, указанного в pos - - >>> get_col([['1', '2', '.'], ['4', '5', '6'], ['7', '8', '9']], (0, 0)) - ['1', '4', '7'] - >>> get_col([['1', '2', '3'], ['4', '.', '6'], ['7', '8', '9']], (0, 1)) - ['2', '.', '8'] - >>> get_col([['1', '2', '3'], ['4', '5', '6'], ['.', '8', '9']], (0, 2)) - ['3', '6', '9'] - """ - pass + """Возвращает все значения для номера столбца, указанного в pos""" + return [row[pos[1]] for row in grid] def get_block(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.List[str]: - """Возвращает все значения из квадрата, в который попадает позиция pos - - >>> grid = read_sudoku('puzzle1.txt') - >>> get_block(grid, (0, 1)) - ['5', '3', '.', '6', '.', '.', '.', '9', '8'] - >>> get_block(grid, (4, 7)) - ['.', '.', '3', '.', '.', '1', '.', '.', '6'] - >>> get_block(grid, (8, 8)) - ['2', '8', '.', '.', '.', '5', '.', '7', '9'] - """ - pass + """Возвращает все значения из квадрата, в который попадает позиция pos""" + res = [] + box_x = pos[1] // 3 + box_y = pos[0] // 3 + for i in range(box_y * 3, box_y * 3 + 3): + for j in range(box_x * 3, box_x * 3 + 3): + res.append(grid[i][j]) + return res def find_empty_positions(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.Tuple[int, int]]: - """Найти первую свободную позицию в пазле - - >>> find_empty_positions([['1', '2', '.'], ['4', '5', '6'], ['7', '8', '9']]) - (0, 2) - >>> find_empty_positions([['1', '2', '3'], ['4', '.', '6'], ['7', '8', '9']]) - (1, 1) - >>> find_empty_positions([['1', '2', '3'], ['4', '5', '6'], ['.', '8', '9']]) - (2, 0) - """ - pass + """Найти первую свободную позицию в пазле""" + for row in range(len(grid)): + for col in range(len(grid[0])): + if grid[row][col] == ".": + return row, col + return None def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.Set[str]: - """Вернуть множество возможных значения для указанной позиции - - >>> grid = read_sudoku('puzzle1.txt') - >>> values = find_possible_values(grid, (0,2)) - >>> values == {'1', '2', '4'} - True - >>> values = find_possible_values(grid, (4,7)) - >>> values == {'2', '5', '9'} - True - """ - pass + """Вернуть множество возможных значения для указанной позиции""" + row_values = set(get_row(grid, pos)) - {"."} + col_values = set(get_col(grid, pos)) - {"."} + block_values = set(get_block(grid, pos)) - {"."} + return set(map(str, range(1, len(grid) + 1))) - row_values - col_values - block_values def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: - """ Решение пазла, заданного в grid """ - """ Как решать Судоку? - 1. Найти свободную позицию - 2. Найти все возможные значения, которые могут находиться на этой позиции - 3. Для каждого возможного значения: - 3.1. Поместить это значение на эту позицию - 3.2. Продолжить решать оставшуюся часть пазла - - >>> 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 - - -def check_solution(solution: tp.List[tp.List[str]]) -> bool: - """ Если решение solution верно, то вернуть True, в противном случае False """ - # TODO: Add doctests with bad puzzles - pass + """Решение пазла, заданного в grid""" + pos = find_empty_positions(grid) + if not pos: + return grid + else: + for value in find_possible_values(grid, pos): + grid[pos[0]][pos[1]] = value + if solve(grid): + return grid + grid[pos[0]][pos[1]] = "." + return None + + +def check_solution(grid: tp.List[tp.List[str]]) -> bool: + """Если решение solution верно, то вернуть True, в противном случае False""" + length = len(grid) + flag1 = True + flag2 = True + flag3 = True + for i in range(length): + flag1 = flag1 and len(set(grid[i]) - {"."}) == length + flag2 = flag2 and len(set(grid[j][i] for j in range(length)) - {"."}) == length + for j in range(length): + flag3 = flag3 and len(set(get_block(grid, (i, j))) - {"."}) == length + return flag1 and flag2 and flag3 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 == '.') - 41 - >>> solution = solve(grid) - >>> check_solution(solution) - True - >>> grid = generate_sudoku(1000) - >>> sum(1 for row in grid for e in row if e == '.') - 0 - >>> solution = solve(grid) - >>> check_solution(solution) - True - >>> grid = generate_sudoku(0) - >>> sum(1 for row in grid for e in row if e == '.') - 81 - >>> solution = solve(grid) - >>> check_solution(solution) - True - """ - pass + """Генерация судоку заполненного на N элементов""" + grid = [["."] * 9 for _ in range(9)] + solve(grid) + for _ in range(81 - N): + while True: + row = randint(0, 8) + col = randint(0, 8) + if grid[row][col] != ".": + break + grid[row][col] = "." + return grid if __name__ == "__main__": diff --git a/homework03/life.py b/homework03/life.py index 7aef0b6..f44dbc8 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -1,9 +1,7 @@ import pathlib -import random +import random as r import typing as tp - -import pygame -from pygame.locals import * +from copy import deepcopy Cell = tp.Tuple[int, int] Cells = tp.List[int] @@ -15,7 +13,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 +27,82 @@ def __init__( self.generations = 1 def create_grid(self, randomize: bool = False) -> Grid: - # Copy from previous assignment - pass + if not randomize: + return [[0 for _ in range(self.cols)] for _ in range(self.rows)] + return [[r.randint(0, 1) for _ in range(self.cols)] for _ in range(self.rows)] def get_neighbours(self, cell: Cell) -> Cells: - # Copy from previous assignment - pass + row, col = cell + neighbours = [] + for dx in range(-1, 2): + for dy in range(-1, 2): + new_row = row + dx + new_col = col + dy + if 0 <= new_row < self.rows and 0 <= new_col < self.cols: + if new_row != row or new_col != col: + neighbours.append(self.curr_generation[new_row][new_col]) + return neighbours def get_next_generation(self) -> Grid: - # Copy from previous assignment - pass + new_grid = deepcopy(self.curr_generation) + for row in range(self.rows): + for col in range(self.cols): + neighbours_count = sum(self.get_neighbours((row, col))) + if neighbours_count == 3: + new_grid[row][col] = 1 + elif neighbours_count != 2: + new_grid[row][col] = 0 + return new_grid def step(self) -> None: """ Выполнить один шаг игры. """ - pass + self.prev_generation = self.curr_generation + self.curr_generation = self.get_next_generation() + self.generations += 1 + + def update(self, rows: int, cols: int, max_gen: int = 50, grid=None) -> None: + self.rows = rows + self.cols = cols + self.max_generation = max_gen + self.curr_generation = grid if grid else self.create_grid(randomize=True) + + def is_cell_alive(self, row: int, col: int) -> bool: + return bool(self.curr_generation[row][col]) + + def switch_cell_status(self, row: int, col: int) -> None: + self.curr_generation[row][col] = (self.curr_generation[row][col] + 1) % 2 @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 + with open(filename, encoding="u8") as fi: + grid = [list(map(int, line)) for line in fi] + life = GameOfLife((10, 10)) + life.update(len(grid), len(grid[0]), grid=grid) + return life def save(self, filename: pathlib.Path) -> None: """ Сохранить текущее состояние клеток в указанный файл. """ - pass + with open(filename, "w", encoding="u8") as fo: + for row in self.curr_generation: + print(*row, sep="", file=fo) diff --git a/homework03/life_console.py b/homework03/life_console.py index ddeb9ef..d639738 100644 --- a/homework03/life_console.py +++ b/homework03/life_console.py @@ -1,4 +1,6 @@ +import argparse import curses +import time from life import GameOfLife from ui import UI @@ -7,16 +9,38 @@ class Console(UI): def __init__(self, life: GameOfLife) -> None: super().__init__(life) + self.from_cmd_args() - def draw_borders(self, screen) -> None: - """ Отобразить рамку. """ - pass - - def draw_grid(self, screen) -> None: - """ Отобразить состояние клеток. """ - pass + def draw(self, screen) -> None: + screen.addstr(f"+{'-' * self.life.cols}+\n") + for row in self.life.curr_generation: + screen.addch("|") + for el in row: + screen.addch("*" if el else " ") + screen.addstr("|\n") + screen.addstr(f"+{'-' * self.life.cols}+\n") def run(self) -> None: screen = curses.initscr() - # PUT YOUR CODE HERE + while True: + screen.clear() + self.draw(screen) + screen.refresh() + self.life.step() + time.sleep(0.2) curses.endwin() + + def from_cmd_args(self): + parser = argparse.ArgumentParser() + parser.add_argument("--rows", type=int) + parser.add_argument("--cols", type=int) + parser.add_argument("--max-generations", type=int) + args = parser.parse_args() + if args.rows and args.cols and args.max_generations: + self.life.update(args.rows, args.cols, args.max_generations) + + +if __name__ == "__main__": + life = GameOfLife((24, 80), max_generations=50) + c = Console(life) + c.run() diff --git a/homework03/life_gui.py b/homework03/life_gui.py index 1126b29..f56906f 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -1,21 +1,93 @@ import pygame from life import GameOfLife -from pygame.locals import * from ui import UI class GUI(UI): def __init__(self, life: GameOfLife, cell_size: int = 10, speed: int = 10) -> None: super().__init__(life) + self.cell_size = cell_size + self.speed = speed + self.border_width = 1 + self.screen_size = ( + self.life.cols * cell_size + self.border_width, + self.life.rows * cell_size + self.border_width, + ) + self.display = pygame.display.set_mode(self.screen_size) + self.clock = pygame.time.Clock() + self.is_paused = False + pygame.display.set_caption("Game of Life") - def draw_lines(self) -> None: - # Copy from previous assignment - pass + def draw_lines(self, win) -> None: + # vertical + for col in range(self.life.cols + 1): + pygame.draw.line( + win, + "black", + (col * self.cell_size, 0), + (col * self.cell_size, self.screen_size[1]), + self.border_width, + ) + # horizontal + for row in range(self.life.rows + 1): + pygame.draw.line( + win, + "black", + (0, row * self.cell_size), + (self.screen_size[0], row * self.cell_size), + self.border_width, + ) - def draw_grid(self) -> None: - # Copy from previous assignment - pass + def draw_grid(self, win) -> None: + for row in range(self.life.rows): + for col in range(self.life.cols): + if self.life.is_cell_alive(row, col): + pygame.draw.rect( + win, + "green", + ( + col * self.cell_size, + row * self.cell_size, + self.cell_size, + self.cell_size, + ), + ) + + def process_click(self, pos: tuple[int, int]) -> None: + col = pos[0] // self.cell_size + row = pos[1] // self.cell_size + self.life.switch_cell_status(row, col) + + def redraw(self, win) -> None: + win.fill("white") + self.draw_grid(win) + self.draw_lines(win) + + def handle_events(self) -> None: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + exit() + + if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: + self.is_paused = not self.is_paused + + if event.type == pygame.MOUSEBUTTONDOWN: + if event.button == 1 and self.is_paused: + self.process_click(event.pos) def run(self) -> None: - # Copy from previous assignment - pass + while True: + self.handle_events() + self.redraw(self.display) + if not self.is_paused: + self.life.step() + pygame.display.update() + self.clock.tick(self.speed) + + +if __name__ == "__main__": + pygame.init() + life = GameOfLife((50, 50), max_generations=50) + gui = GUI(life) + gui.run() diff --git a/homework03/life_proto.py b/homework03/life_proto.py index c6d6010..75e6a94 100644 --- a/homework03/life_proto.py +++ b/homework03/life_proto.py @@ -1,8 +1,8 @@ -import random +import random as r import typing as tp +from copy import deepcopy import pygame -from pygame.locals import * Cell = tp.Tuple[int, int] Cells = tp.List[int] @@ -17,103 +17,64 @@ def __init__( self.height = height self.cell_size = cell_size - # Устанавливаем размер окна self.screen_size = width, height - # Создание нового окна self.screen = pygame.display.set_mode(self.screen_size) - # Вычисляем количество ячеек по вертикали и горизонтали self.cell_width = self.width // self.cell_size self.cell_height = self.height // self.cell_size - # Скорость протекания игры + self.rows = self.height // self.cell_size + self.cols = self.width // self.cell_size + self.speed = speed + self.grid: list[list[int]] = [[]] 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 - running = True while running: for event in pygame.event.get(): - if event.type == QUIT: + if event.type == pygame.QUIT: running = False - self.draw_lines() - # Отрисовка списка клеток - # Выполнение одного шага игры (обновление состояния ячеек) - # PUT YOUR CODE HERE + self.screen.fill((255, 255, 255)) pygame.display.flip() clock.tick(self.speed) pygame.quit() def create_grid(self, randomize: bool = False) -> Grid: - """ - Создание списка клеток. - - Клетка считается живой, если ее значение равно 1, в противном случае клетка - считается мертвой, то есть, ее значение равно 0. - - Parameters - ---------- - randomize : bool - Если значение истина, то создается матрица, где каждая клетка может - быть равновероятно живой или мертвой, иначе все клетки создаются мертвыми. - - Returns - ---------- - out : Grid - Матрица клеток размером `cell_height` х `cell_width`. - """ - pass - - def draw_grid(self) -> None: - """ - Отрисовка списка клеток с закрашиванием их в соответствующе цвета. - """ - pass + if not randomize: + return [[0 for _ in range(self.cols)] for _ in range(self.rows)] + return [[r.randint(0, 1) for _ in range(self.cols)] for _ in range(self.rows)] def get_neighbours(self, cell: Cell) -> Cells: - """ - Вернуть список соседних клеток для клетки `cell`. - - Соседними считаются клетки по горизонтали, вертикали и диагоналям, - то есть, во всех направлениях. - - Parameters - ---------- - cell : Cell - Клетка, для которой необходимо получить список соседей. Клетка - представлена кортежем, содержащим ее координаты на игровом поле. - - Returns - ---------- - out : Cells - Список соседних клеток. - """ - pass + row, col = cell + neighbours = [] + for dx in range(-1, 2): + for dy in range(-1, 2): + new_row = row + dx + new_col = col + dy + if 0 <= new_row < self.rows and 0 <= new_col < self.cols: + if new_row != row or new_col != col: + neighbours.append(self.grid[new_row][new_col]) + return neighbours def get_next_generation(self) -> Grid: - """ - Получить следующее поколение клеток. - - Returns - ---------- - out : Grid - Новое поколение клеток. - """ - pass + new_grid = deepcopy(self.grid) + for row in range(self.rows): + for col in range(self.cols): + neighbours_count = sum(self.get_neighbours((row, col))) + if neighbours_count == 3: + new_grid[row][col] = 1 + elif neighbours_count != 2: + new_grid[row][col] = 0 + return new_grid