From 8e5143deefe9a51e2a150b36028a735ee9bc172c Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Mon, 19 Sep 2022 20:47:00 +0300 Subject: [PATCH 01/24] Completed lab-00 --- homework00/hello.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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__": From a94006e0f811127259ad1dd2d37adb9c8486166e Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sun, 2 Oct 2022 13:01:55 +0300 Subject: [PATCH 02/24] Completed lab1 --- homework01/caesar.py | 45 +++++++++++------------------------ homework01/rsa.py | 53 +++++++++++++++++------------------------- homework01/vigenere.py | 47 +++++++++++++++++-------------------- 3 files changed, 55 insertions(+), 90 deletions(-) diff --git a/homework01/caesar.py b/homework01/caesar.py index 09c3681..38bc9c9 100644 --- a/homework01/caesar.py +++ b/homework01/caesar.py @@ -1,46 +1,27 @@ import typing as tp +from string import ascii_lowercase as lower, 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..75a3a88 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) diff --git a/homework01/vigenere.py b/homework01/vigenere.py index e51742e..e0d8da3 100644 --- a/homework01/vigenere.py +++ b/homework01/vigenere.py @@ -1,30 +1,25 @@ -def encrypt_vigenere(plaintext: str, keyword: str) -> str: - """ - Encrypts plaintext using a Vigenere cipher. +from string import ascii_lowercase as lower - >>> encrypt_vigenere("PYTHON", "A") - 'PYTHON' - >>> encrypt_vigenere("python", "a") - 'python' - >>> encrypt_vigenere("ATTACKATDAWN", "LEMON") - 'LXFOPVEFRNHR' - """ - ciphertext = "" - # PUT YOUR CODE HERE - return ciphertext +table = [[lower[(lower.index(char) + shift) % 26] for char in lower] for shift in range(26)] -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 encrypt_vigenere(plaintext: str, keyword: str, encode: bool = True) -> str: + ciphertext = [] + for index, char in enumerate(plaintext.lower()): + if char.isalpha(): + keyword_char = keyword[index % len(keyword)].lower() + keyword_char_index = lower.index(keyword_char) + if encode: + char = table[keyword_char_index][lower.index(char)] + else: + table_char_index = table[keyword_char_index].index(char) + char = lower[table_char_index] + if plaintext[index].isupper(): + char = char.upper() + ciphertext.append(char) + return "".join(ciphertext) + + +def decrypt_vigenere(ciphertext: str, keyword: str) -> str: + return encrypt_vigenere(ciphertext, keyword, encode=False) From eb692496ee3de42c9885b39a245179589e1c8479 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sun, 2 Oct 2022 13:06:40 +0300 Subject: [PATCH 03/24] Completed lab-01 --- homework01/caesar.py | 3 ++- homework01/vigenere.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homework01/caesar.py b/homework01/caesar.py index 38bc9c9..381e7ba 100644 --- a/homework01/caesar.py +++ b/homework01/caesar.py @@ -1,5 +1,6 @@ +from string import ascii_lowercase as lower +from string import ascii_uppercase as upper import typing as tp -from string import ascii_lowercase as lower, ascii_uppercase as upper def encrypt_caesar(plaintext: str, shift: int = 3) -> str: diff --git a/homework01/vigenere.py b/homework01/vigenere.py index e0d8da3..5251dcf 100644 --- a/homework01/vigenere.py +++ b/homework01/vigenere.py @@ -1,7 +1,8 @@ from string import ascii_lowercase as lower -table = [[lower[(lower.index(char) + shift) % 26] for char in lower] for shift in range(26)] +table = [[lower[(lower.index(char) + shift) % 26] + for char in lower] for shift in range(26)] def encrypt_vigenere(plaintext: str, keyword: str, encode: bool = True) -> str: From 2a5dc50874c8d73ab76058fd53f2288b61a8a455 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sun, 2 Oct 2022 13:09:32 +0300 Subject: [PATCH 04/24] Completed lab-01 --- homework01/caesar.py | 2 +- homework01/vigenere.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/homework01/caesar.py b/homework01/caesar.py index 381e7ba..7b05368 100644 --- a/homework01/caesar.py +++ b/homework01/caesar.py @@ -1,6 +1,6 @@ +import typing as tp from string import ascii_lowercase as lower from string import ascii_uppercase as upper -import typing as tp def encrypt_caesar(plaintext: str, shift: int = 3) -> str: diff --git a/homework01/vigenere.py b/homework01/vigenere.py index 5251dcf..3ffb062 100644 --- a/homework01/vigenere.py +++ b/homework01/vigenere.py @@ -1,6 +1,5 @@ from string import ascii_lowercase as lower - table = [[lower[(lower.index(char) + shift) % 26] for char in lower] for shift in range(26)] From 3c2ac2e3def284bbee6049e3ebfad72dc11844a3 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sun, 2 Oct 2022 13:11:57 +0300 Subject: [PATCH 05/24] Completed lab-01 --- homework01/rsa.py | 4 ++-- homework01/vigenere.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/homework01/rsa.py b/homework01/rsa.py index 75a3a88..2378b58 100644 --- a/homework01/rsa.py +++ b/homework01/rsa.py @@ -65,7 +65,7 @@ def encrypt(pk: tp.Tuple[int, int], plaintext: str) -> tp.List[int]: key, n = pk # Convert each letter in the plaintext to numbers based on # the character using a^b mod m - cipher = [(ord(char) ** key) % n for char in plaintext] + cipher = [(ord(char)**key) % n for char in plaintext] # Return the array of bytes return cipher @@ -74,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 3ffb062..4502160 100644 --- a/homework01/vigenere.py +++ b/homework01/vigenere.py @@ -1,7 +1,6 @@ from string import ascii_lowercase as lower -table = [[lower[(lower.index(char) + shift) % 26] - for char in lower] for shift in range(26)] +table = [[lower[(lower.index(char) + shift) % 26] for char in lower] for shift in range(26)] def encrypt_vigenere(plaintext: str, keyword: str, encode: bool = True) -> str: From b768a8055ea7367ba5b8eac46a0270a13cc68eaa Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sun, 2 Oct 2022 13:14:35 +0300 Subject: [PATCH 06/24] Completed lab-01 --- homework01/rsa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework01/rsa.py b/homework01/rsa.py index 2378b58..1d56554 100644 --- a/homework01/rsa.py +++ b/homework01/rsa.py @@ -65,7 +65,7 @@ def encrypt(pk: tp.Tuple[int, int], plaintext: str) -> tp.List[int]: key, n = pk # Convert each letter in the plaintext to numbers based on # the character using a^b mod m - cipher = [(ord(char)**key) % n for char in plaintext] + cipher = [(ord(char) ** key) % n for char in plaintext] # Return the array of bytes return cipher From a4dc7ad74807824cc82aaee152d4f833ff099a0a Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 5 Oct 2022 16:12:31 +0300 Subject: [PATCH 07/24] Completed lab-01 --- homework01/vigenere.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/homework01/vigenere.py b/homework01/vigenere.py index 4502160..d0086b3 100644 --- a/homework01/vigenere.py +++ b/homework01/vigenere.py @@ -1,22 +1,16 @@ -from string import ascii_lowercase as lower - -table = [[lower[(lower.index(char) + shift) % 26] for char in lower] for shift in range(26)] +from caesar import encrypt_caesar, decrypt_caesar def encrypt_vigenere(plaintext: str, keyword: str, encode: bool = True) -> str: ciphertext = [] for index, char in enumerate(plaintext.lower()): - if char.isalpha(): - keyword_char = keyword[index % len(keyword)].lower() - keyword_char_index = lower.index(keyword_char) - if encode: - char = table[keyword_char_index][lower.index(char)] - else: - table_char_index = table[keyword_char_index].index(char) - char = lower[table_char_index] - if plaintext[index].isupper(): - char = char.upper() - ciphertext.append(char) + 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) From ccc77a8c0ac82ed4c6b14a6664ca33a42988cccf Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 5 Oct 2022 16:14:17 +0300 Subject: [PATCH 08/24] Completed lab-01 --- homework01/vigenere.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework01/vigenere.py b/homework01/vigenere.py index d0086b3..1cbb09d 100644 --- a/homework01/vigenere.py +++ b/homework01/vigenere.py @@ -1,4 +1,4 @@ -from caesar import encrypt_caesar, decrypt_caesar +from caesar import decrypt_caesar, encrypt_caesar def encrypt_vigenere(plaintext: str, keyword: str, encode: bool = True) -> str: From 6314f6643ba7fe19b838510d1642f34069e8e85c Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 12 Oct 2022 19:56:43 +0300 Subject: [PATCH 09/24] Done --- homework02/sudoku.py | 148 ++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 80 deletions(-) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index df78ab1..8bc3f2d 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -1,11 +1,12 @@ import pathlib +from random import randint 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): @@ -35,103 +36,79 @@ 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 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: + 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]] = "." + + +def check_solution(grid: tp.List[tp.List[str]]) -> bool: """ Если решение solution верно, то вернуть True, в противном случае False """ - # TODO: Add doctests with bad puzzles - pass + 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]]: @@ -156,12 +133,23 @@ def generate_sudoku(N: int) -> tp.List[tp.List[str]]: >>> check_solution(solution) True """ - pass + 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__": for fname in ["puzzle1.txt", "puzzle2.txt", "puzzle3.txt"]: grid = read_sudoku(fname) + print(find_possible_values(grid, (0, 2))) + break display(grid) solution = solve(grid) if not solution: From e1ed86d20e01866cc8d56af6cb07ba5fbaa10ad4 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 12 Oct 2022 19:59:44 +0300 Subject: [PATCH 10/24] Done --- homework02/sudoku.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index 8bc3f2d..46f2056 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -1,6 +1,6 @@ import pathlib -from random import randint import typing as tp +from random import randint T = tp.TypeVar("T") From 4e33aeb9ff30c7f9c21e52e765f8e988d23d5d3f Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 12 Oct 2022 20:02:00 +0300 Subject: [PATCH 11/24] Done --- homework02/sudoku.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index 46f2056..00f2d26 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -35,8 +35,7 @@ def display(grid: tp.List[tp.List[str]]) -> None: def group(values: tp.List[T], n: int) -> tp.List[tp.List[T]]: - """ - Сгруппировать значения values в список, состоящий из списков по n элементов""" + """Сгруппировать значения values в список, состоящий из списков по n элементов""" res = [] tmp = [] for value in values: @@ -85,7 +84,7 @@ def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) - def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: - """ Решение пазла, заданного в grid """ + """Решение пазла, заданного в grid""" pos = find_empty_positions(grid) if not pos: return grid @@ -98,7 +97,7 @@ def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: def check_solution(grid: tp.List[tp.List[str]]) -> bool: - """ Если решение solution верно, то вернуть True, в противном случае False """ + """Если решение solution верно, то вернуть True, в противном случае False""" length = len(grid) flag1 = True flag2 = True From 8874973af5de5041455a0e811dc798cf1153e607 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 12 Oct 2022 20:03:41 +0300 Subject: [PATCH 12/24] Done --- homework02/sudoku.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index 00f2d26..833aa4d 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -6,7 +6,7 @@ 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() @@ -111,27 +111,7 @@ def check_solution(grid: tp.List[tp.List[str]]) -> bool: 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 - """ + """Генерация судоку заполненного на N элементов""" grid = [["."] * 9 for _ in range(9)] solve(grid) for _ in range(81 - N): From a846b3c72575e4c4e66a10be9e2f2e4c5b35b782 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 12 Oct 2022 20:11:59 +0300 Subject: [PATCH 13/24] Done --- homework02/sudoku.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index 833aa4d..8503b7d 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -73,6 +73,7 @@ def find_empty_positions(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.Tuple[in for col in range(len(grid[0])): if grid[row][col] == ".": return row, col + return def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.Set[str]: @@ -94,6 +95,7 @@ def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: if solve(grid): return grid grid[pos[0]][pos[1]] = "." + return def check_solution(grid: tp.List[tp.List[str]]) -> bool: From a115a3949de7d87d86dd2b3ccfcc99e7d3aecf51 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Wed, 12 Oct 2022 20:13:48 +0300 Subject: [PATCH 14/24] Done --- homework02/sudoku.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index 8503b7d..a56f666 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -73,7 +73,7 @@ def find_empty_positions(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.Tuple[in for col in range(len(grid[0])): if grid[row][col] == ".": return row, col - return + return None def find_possible_values(grid: tp.List[tp.List[str]], pos: tp.Tuple[int, int]) -> tp.Set[str]: @@ -95,7 +95,7 @@ def solve(grid: tp.List[tp.List[str]]) -> tp.Optional[tp.List[tp.List[str]]]: if solve(grid): return grid grid[pos[0]][pos[1]] = "." - return + return None def check_solution(grid: tp.List[tp.List[str]]) -> bool: From 5e1a5848350c15e2cfa3ab1099ef62dd2597b683 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sat, 29 Oct 2022 21:10:27 +0300 Subject: [PATCH 15/24] Done --- homework03/life.py | 41 +++++++++++++-------- homework03/life_console.py | 39 ++++++++++++++++---- homework03/life_gui.py | 73 +++++++++++++++++++++++++++++++++----- homework03/life_proto.py | 58 ++++++++++++++++++++---------- 4 files changed, 164 insertions(+), 47 deletions(-) diff --git a/homework03/life.py b/homework03/life.py index 7aef0b6..3a3f9d2 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -4,13 +4,14 @@ import pygame from pygame.locals import * +from life_proto import GameOfLife as GameOfLifeProto Cell = tp.Tuple[int, int] Cells = tp.List[int] Grid = tp.List[Cells] -class GameOfLife: +class GameOfLife(GameOfLifeProto): def __init__( self, size: tp.Tuple[int, int], @@ -28,47 +29,59 @@ def __init__( # Текущее число поколений self.generations = 1 - def create_grid(self, randomize: bool = False) -> Grid: - # Copy from previous assignment - pass - def get_neighbours(self, cell: Cell) -> Cells: - # Copy from previous assignment - pass + return super().get_neighbours(cell, self.curr_generation) def get_next_generation(self) -> Grid: - # Copy from previous assignment - pass + return super().get_next_generation(self.curr_generation) 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) -> None: + self.rows = rows + self.cols = cols + self.max_generation = max_gen + self.curr_generation = 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: + self.current_generation = [list(map(int, line)) for line in fi] def save(self, filename: pathlib.Path) -> None: """ Сохранить текущее состояние клеток в указанный файл. """ - pass + with open(filename, "w", encoding="u8") as fo: + for row in self.current_generation: + print(*row, sep="", file=fo) diff --git a/homework03/life_console.py b/homework03/life_console.py index ddeb9ef..f373f5b 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,39 @@ 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: + def draw(self, screen) -> None: """ Отобразить состояние клеток. """ - pass + 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..81f695a 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -7,15 +7,72 @@ 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..2028787 100644 --- a/homework03/life_proto.py +++ b/homework03/life_proto.py @@ -1,5 +1,6 @@ -import random +import random as r import typing as tp +from copy import deepcopy import pygame from pygame.locals import * @@ -26,6 +27,9 @@ def __init__( 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 @@ -41,7 +45,6 @@ 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 @@ -51,11 +54,10 @@ def run(self) -> None: for event in pygame.event.get(): if event.type == QUIT: running = False - self.draw_lines() - # Отрисовка списка клеток - # Выполнение одного шага игры (обновление состояния ячеек) - # PUT YOUR CODE HERE + self.screen.fill((255, 255, 255)) + self.draw_grid() + self.draw_lines() pygame.display.flip() clock.tick(self.speed) @@ -79,15 +81,11 @@ def create_grid(self, randomize: bool = False) -> Grid: out : Grid Матрица клеток размером `cell_height` х `cell_width`. """ - 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 draw_grid(self) -> None: - """ - Отрисовка списка клеток с закрашиванием их в соответствующе цвета. - """ - pass - - def get_neighbours(self, cell: Cell) -> Cells: + def get_neighbours(self, cell: Cell, grid: Cells = None) -> Cells: """ Вернуть список соседних клеток для клетки `cell`. @@ -105,9 +103,19 @@ def get_neighbours(self, cell: Cell) -> Cells: out : Cells Список соседних клеток. """ - pass - - def get_next_generation(self) -> Grid: + grid = grid or self.grid + 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(grid[new_row][new_col]) + return neighbours + + def get_next_generation(self, grid: Cells = None) -> Grid: """ Получить следующее поколение клеток. @@ -116,4 +124,18 @@ def get_next_generation(self) -> Grid: out : Grid Новое поколение клеток. """ - pass + new_grid = deepcopy(grid or 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 + + +if __name__ == '__main__': + life = GameOfLife() + life.grid = life.create_grid(randomize=True) + life.run() From f9437efe14c4a34d3d2d29f56a83552235316abf Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sat, 29 Oct 2022 21:13:58 +0300 Subject: [PATCH 16/24] Done --- homework02/sudoku.py | 2 -- homework03/life.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/homework02/sudoku.py b/homework02/sudoku.py index a56f666..6de9720 100644 --- a/homework02/sudoku.py +++ b/homework02/sudoku.py @@ -129,8 +129,6 @@ def generate_sudoku(N: int) -> tp.List[tp.List[str]]: if __name__ == "__main__": for fname in ["puzzle1.txt", "puzzle2.txt", "puzzle3.txt"]: grid = read_sudoku(fname) - print(find_possible_values(grid, (0, 2))) - break display(grid) solution = solve(grid) if not solution: diff --git a/homework03/life.py b/homework03/life.py index 3a3f9d2..6007729 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -3,8 +3,8 @@ import typing as tp import pygame -from pygame.locals import * from life_proto import GameOfLife as GameOfLifeProto +from pygame.locals import * Cell = tp.Tuple[int, int] Cells = tp.List[int] From 2557fd61edc56267fc27a0cba1d683c37ce2993e Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sat, 29 Oct 2022 21:19:56 +0300 Subject: [PATCH 17/24] Done --- homework03/life.py | 3 +- homework03/life_console.py | 1 - homework03/life_gui.py | 36 ++++++++++++++++------- homework03/life_proto.py | 58 -------------------------------------- 4 files changed, 27 insertions(+), 71 deletions(-) diff --git a/homework03/life.py b/homework03/life.py index 6007729..d270b56 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -53,8 +53,7 @@ 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 + self.curr_generation[row][col] = (self.curr_generation[row][col] + 1) % 2 @property def is_max_generations_exceeded(self) -> bool: diff --git a/homework03/life_console.py b/homework03/life_console.py index f373f5b..fd85b6a 100644 --- a/homework03/life_console.py +++ b/homework03/life_console.py @@ -12,7 +12,6 @@ def __init__(self, life: GameOfLife) -> None: self.from_cmd_args() def draw(self, screen) -> None: - """ Отобразить состояние клеток. """ screen.addstr(f"+{'-' * self.life.cols}+\n") for row in self.life.curr_generation: screen.addch("|") diff --git a/homework03/life_gui.py b/homework03/life_gui.py index 81f695a..cead03e 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -12,7 +12,7 @@ def __init__(self, life: GameOfLife, cell_size: int = 10, speed: int = 10) -> No self.border_width = 1 self.screen_size = ( self.life.cols * cell_size + self.border_width, - self.life.rows * 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() @@ -22,21 +22,37 @@ def __init__(self, life: GameOfLife, cell_size: int = 10, speed: int = 10) -> No 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) + 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) + 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, 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)) + 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 @@ -71,7 +87,7 @@ def run(self) -> None: self.clock.tick(self.speed) -if __name__ == '__main__': +if __name__ == "__main__": pygame.init() life = GameOfLife((50, 50), max_generations=50) gui = GUI(life) diff --git a/homework03/life_proto.py b/homework03/life_proto.py index 2028787..dfb7fa5 100644 --- a/homework03/life_proto.py +++ b/homework03/life_proto.py @@ -18,37 +18,27 @@ 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 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") - - # Создание списка клеток - # PUT YOUR CODE HERE - running = True while running: for event in pygame.event.get(): @@ -64,45 +54,11 @@ def run(self) -> None: pygame.quit() def create_grid(self, randomize: bool = False) -> Grid: - """ - Создание списка клеток. - - Клетка считается живой, если ее значение равно 1, в противном случае клетка - считается мертвой, то есть, ее значение равно 0. - - Parameters - ---------- - randomize : bool - Если значение истина, то создается матрица, где каждая клетка может - быть равновероятно живой или мертвой, иначе все клетки создаются мертвыми. - - Returns - ---------- - out : Grid - Матрица клеток размером `cell_height` х `cell_width`. - """ 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, grid: Cells = None) -> Cells: - """ - Вернуть список соседних клеток для клетки `cell`. - - Соседними считаются клетки по горизонтали, вертикали и диагоналям, - то есть, во всех направлениях. - - Parameters - ---------- - cell : Cell - Клетка, для которой необходимо получить список соседей. Клетка - представлена кортежем, содержащим ее координаты на игровом поле. - - Returns - ---------- - out : Cells - Список соседних клеток. - """ grid = grid or self.grid row, col = cell neighbours = [] @@ -116,14 +72,6 @@ def get_neighbours(self, cell: Cell, grid: Cells = None) -> Cells: return neighbours def get_next_generation(self, grid: Cells = None) -> Grid: - """ - Получить следующее поколение клеток. - - Returns - ---------- - out : Grid - Новое поколение клеток. - """ new_grid = deepcopy(grid or self.grid) for row in range(self.rows): for col in range(self.cols): @@ -133,9 +81,3 @@ def get_next_generation(self, grid: Cells = None) -> Grid: elif neighbours_count != 2: new_grid[row][col] = 0 return new_grid - - -if __name__ == '__main__': - life = GameOfLife() - life.grid = life.create_grid(randomize=True) - life.run() From bd57e7f7f151c669d76d758981b42f711b6f2d43 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sat, 29 Oct 2022 21:23:11 +0300 Subject: [PATCH 18/24] Done --- homework03/life_console.py | 2 +- homework03/life_gui.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homework03/life_console.py b/homework03/life_console.py index fd85b6a..d639738 100644 --- a/homework03/life_console.py +++ b/homework03/life_console.py @@ -40,7 +40,7 @@ def from_cmd_args(self): self.life.update(args.rows, args.cols, args.max_generations) -if __name__ == '__main__': +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 cead03e..82864f0 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -50,7 +50,7 @@ def draw_grid(self, win) -> None: col * self.cell_size, row * self.cell_size, self.cell_size, - self.cell_size + self.cell_size, ), ) From 3c2329aa0159df9c3ba424b74dd3606baff73d2c Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Tue, 1 Nov 2022 18:23:17 +0300 Subject: [PATCH 19/24] Done --- homework03/life_proto.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/homework03/life_proto.py b/homework03/life_proto.py index dfb7fa5..1d8c5d4 100644 --- a/homework03/life_proto.py +++ b/homework03/life_proto.py @@ -3,7 +3,6 @@ from copy import deepcopy import pygame -from pygame.locals import * Cell = tp.Tuple[int, int] Cells = tp.List[int] @@ -28,6 +27,7 @@ def __init__( self.cols = self.width // self.cell_size self.speed = speed + self.grid = [[]] def draw_lines(self) -> None: for x in range(0, self.width, self.cell_size): @@ -42,12 +42,10 @@ def run(self) -> None: running = True while running: for event in pygame.event.get(): - if event.type == QUIT: + if event.type == pygame.QUIT: running = False self.screen.fill((255, 255, 255)) - self.draw_grid() - self.draw_lines() pygame.display.flip() clock.tick(self.speed) From f6d92aefde13e9232895017f7724247961497a7d Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Tue, 1 Nov 2022 18:31:04 +0300 Subject: [PATCH 20/24] Done --- homework03/life.py | 3 --- homework03/life_gui.py | 1 - 2 files changed, 4 deletions(-) diff --git a/homework03/life.py b/homework03/life.py index d270b56..292cb6f 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -1,10 +1,7 @@ import pathlib -import random import typing as tp -import pygame from life_proto import GameOfLife as GameOfLifeProto -from pygame.locals import * Cell = tp.Tuple[int, int] Cells = tp.List[int] diff --git a/homework03/life_gui.py b/homework03/life_gui.py index 82864f0..f56906f 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -1,6 +1,5 @@ import pygame from life import GameOfLife -from pygame.locals import * from ui import UI From ea3a1363f3c329363fa80fbb84202b483d881310 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Tue, 1 Nov 2022 18:39:34 +0300 Subject: [PATCH 21/24] Done --- homework03/life.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/homework03/life.py b/homework03/life.py index 292cb6f..8b22b18 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -40,11 +40,11 @@ def step(self) -> None: self.curr_generation = self.get_next_generation() self.generations += 1 - def update(self, rows: int, cols: int, max_gen: int) -> None: + def update(self, rows: int, cols: int, max_gen: int = 50, grid: Cells = None) -> None: self.rows = rows self.cols = cols self.max_generation = max_gen - self.curr_generation = self.create_grid(randomize=True) + 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]) @@ -72,7 +72,9 @@ def from_file(filename: pathlib.Path) -> "GameOfLife": Прочитать состояние клеток из указанного файла. """ with open(filename, encoding="u8") as fi: - self.current_generation = [list(map(int, line)) for line in fi] + grid = [list(map(int, line)) for line in fi] + life = GameOfLife((10, 10)) + life.update(len(grid), len(grid[0]), grid=grid) def save(self, filename: pathlib.Path) -> None: """ From 1b9c77f2e4a28787719cc9076cd4d4720fba66b9 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Tue, 1 Nov 2022 19:15:29 +0300 Subject: [PATCH 22/24] Done! --- homework03/life.py | 39 +++++++++++++++++++++++++++++++-------- homework03/life_gui.py | 1 + homework03/life_proto.py | 11 +++++------ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/homework03/life.py b/homework03/life.py index 8b22b18..f44dbc8 100644 --- a/homework03/life.py +++ b/homework03/life.py @@ -1,19 +1,19 @@ import pathlib +import random as r import typing as tp - -from life_proto import GameOfLife as GameOfLifeProto +from copy import deepcopy Cell = tp.Tuple[int, int] Cells = tp.List[int] Grid = tp.List[Cells] -class GameOfLife(GameOfLifeProto): +class GameOfLife: 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 @@ -26,11 +26,33 @@ def __init__( # Текущее число поколений self.generations = 1 + def create_grid(self, randomize: bool = False) -> Grid: + 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: - return super().get_neighbours(cell, self.curr_generation) + 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: - return super().get_next_generation(self.curr_generation) + 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: """ @@ -40,7 +62,7 @@ def step(self) -> None: self.curr_generation = self.get_next_generation() self.generations += 1 - def update(self, rows: int, cols: int, max_gen: int = 50, grid: Cells = None) -> None: + 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 @@ -75,11 +97,12 @@ def from_file(filename: pathlib.Path) -> "GameOfLife": 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: """ Сохранить текущее состояние клеток в указанный файл. """ with open(filename, "w", encoding="u8") as fo: - for row in self.current_generation: + for row in self.curr_generation: print(*row, sep="", file=fo) diff --git a/homework03/life_gui.py b/homework03/life_gui.py index f56906f..fc07edd 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -1,4 +1,5 @@ import pygame + from life import GameOfLife from ui import UI diff --git a/homework03/life_proto.py b/homework03/life_proto.py index 1d8c5d4..75e6a94 100644 --- a/homework03/life_proto.py +++ b/homework03/life_proto.py @@ -27,7 +27,7 @@ def __init__( self.cols = self.width // self.cell_size self.speed = speed - self.grid = [[]] + self.grid: list[list[int]] = [[]] def draw_lines(self) -> None: for x in range(0, self.width, self.cell_size): @@ -56,8 +56,7 @@ def create_grid(self, randomize: bool = False) -> Grid: 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, grid: Cells = None) -> Cells: - grid = grid or self.grid + def get_neighbours(self, cell: Cell) -> Cells: row, col = cell neighbours = [] for dx in range(-1, 2): @@ -66,11 +65,11 @@ def get_neighbours(self, cell: Cell, grid: Cells = None) -> Cells: 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(grid[new_row][new_col]) + neighbours.append(self.grid[new_row][new_col]) return neighbours - def get_next_generation(self, grid: Cells = None) -> Grid: - new_grid = deepcopy(grid or self.grid) + def get_next_generation(self) -> Grid: + 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))) From 0ea139e6ee77276668f6d2df151656b00fef4de3 Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk <51047596+fuetser@users.noreply.github.com> Date: Sun, 20 Nov 2022 13:25:20 +0300 Subject: [PATCH 23/24] Update cs102.yml --- .github/workflows/cs102.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 83437fdfb527c650da9c6d77bcef88ff88fec22e Mon Sep 17 00:00:00 2001 From: Alexander Zakharchuk Date: Sun, 20 Nov 2022 13:32:02 +0300 Subject: [PATCH 24/24] done --- homework03/life_gui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homework03/life_gui.py b/homework03/life_gui.py index fc07edd..f56906f 100644 --- a/homework03/life_gui.py +++ b/homework03/life_gui.py @@ -1,5 +1,4 @@ import pygame - from life import GameOfLife from ui import UI