Python #7 (szyfrowanie, turtle part 2, pętle)

Python #7 (szyfrowanie, turtle part 2, pętle)
Photo by Gabriela / Unsplash

Szyfrowanie

Funkcja skrótu (hash)
  • nie należy jej mylić z szyfrowaniem
  • to specjalny algorytm, który zamienia dowolne dane wejściowe w krótki, stałej długości ciąg znaków (tzw. skrót, hash)
  • działa jednokierunkowo — nie da się z hasha odzyskać oryginalnych danych
  • nawet drobna zmiana danych daje zupełnie inny hash
  • zawsze zwraca skrót o tej samej długości, niezależnie od wielkości danych wejściowych
  • po co? -> umożliwia przechowywanie w serwisach hashy zamiast haseł; możemy z pomocą hasha zweryfikować czy przesłany plik nie został zmodyfikowany; możemy namierzyć wirusa po jego hashu; słowniki i sety korzystają z funkcji skrótu w celu wskazania miejsca gdzie są nasze dane.
  1. Szyfrowanie symetryczne - to metoda ochrony danych, w której ten sam tajny klucz służy do szyfrowania (kodowania) wiadomości oraz odszyfrowania (odkodowania) tej samej wiadomości.
  • Nadawca i odbiorca muszą wcześniej ustalić wspólny tajny klucz.
  • Nadawca: używa klucza i algorytmu szyfrowania, aby przekształcić dane jawne w zaszyfrowane.
  • Odbiorca: używa tego samego klucza i algorytmu, by przekształcić dane z powrotem do postaci jawnej.

WADY:

  • Problem z przekazaniem klucza — trzeba go dostarczyć odbiorcy w bezpieczny sposób (np. osobiście, przez kanał szyfrowany).
  • Słaba skalowalność — w sieci wielu użytkowników każdy musi mieć oddzielne klucze z każdym innym użytkownikiem.
  • Brak uwierzytelnienia — nie można udowodnić, kto wysłał wiadomość, bo każdy z kluczem może tworzyć wiadomości.
  1. Szyfrowanie asymetryczne (zwane też kryptografią klucza publicznego) to metoda, w której używa się dwóch różnych kluczy:
  • klucz publiczny – może znać każdy (do szyfrowania),
  • klucz prywatny – zna tylko właściciel (do odszyfrowania).
    To, co zaszyfrujesz kluczem publicznym, może odszyfrować tylko odpowiadający mu klucz prywatny, i odwrotnie.

Każdy użytkownik generuje parę kluczy (publiczny + prywatny).

  • Nadawca:
    • pobiera klucz publiczny odbiorcy,
    • szyfruje nim wiadomość.
  • Odbiorca:
    • używa swojego klucza prywatnego do odszyfrowania wiadomości.

Dzięki temu nie trzeba przesyłać żadnych tajnych kluczy — klucz publiczny może być dostępny dla wszystkich.

WADY:

  • jest wolniejsze niż symetryczne (więcej obliczeń),
  • klucze są dłuższe i bardziej złożone,
  • zwykle stosuje się je tylko do przesyłania kluczy symetrycznych, a nie do szyfrowania dużych plików.

W praktyce używamy:

szyfrowanie asymetryczne -> do wymiany kluczy,

szyfrowanie symetryczne -> do szybkiego szyfrowania danych.
Obie metody są często łączone (np. w protokole HTTPS).

  1. Szyfr Cezara to jeden z najstarszych i najprostszych szyfrów, używany już w starożytnym Rzymie przez Juliusza Cezara do tajnej korespondencji.

To przykład szyfru podstawieniowego, czyli takiego, w którym każda litera tekstu jawnego jest zastępowana inną literą — przesuniętą o stałą liczbę pozycji w alfabecie.

  • Wybierasz klucz – liczbę przesunięcia (np. 3).
  • Każdą literę przesuwasz w alfabecie o tyle miejsc w prawo. Jeżeli jest to lista - przesuwasz o indeksy. Gdy dojdziemy do końca listy -> zawijamy z powrotem na początek.
ZNAKI = [
    "a",
    "b",
    "c",
    "d",
    "e",
    "f",
    "g",
    "h",
    "i",
    "j",
    "k",
    "l",
    "m",
    "n",
    "o",
    "p",
    "q",
    "r",
    "s",
    "t",
    "u",
    "v",
    "w",
    "x",
    "y",
    "z",
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "O",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z",
    "Ą",
    "ą",
    "Ć",
    "ć",
    "Ę",
    "ę",
    "Ł",
    "ł",
    "Ń",
    "ń",
    "Ó",
    "ó",
    "Ś",
    "ś",
    "Ź",
    "ź",
    "Ż",
    "ż",
    " ",
    ".",
    "?",
    "!",
]

zdanie = input("Wprowadź zdanie w języku polskim: ")
klucz = int(input("Zaproponuj klucz (cyfra w zakresie 1-9): "))
zaszyfrowany = ""

for znak in zdanie:
    index_znaku = ZNAKI.index(znak)
    zaszyfrowany += ZNAKI[(index_znaku + klucz) % len(ZNAKI)]
print(f"Po przesunieciu o {klucz} uzyskamy {zaszyfrowany}")


# powyższa lista znaków ułatwi stworzenie kodu odpowiedzialnego za szyfrowanie

# żeby przy bardzo dużym kluczu , lub przy zamianie znaku z końca listy nie wyjść za nią
# można skorzystać z operatora modulo (to ten od reszty z dzielenia)
# wg. schematu (indeksznaku + klucz) % len(ZNAKI)

# indeksznaku = ZNAKI.index("!")
# print(f"Indeks znaku to: {indeksznaku}")
# klucz = 3
# nowy_indeks = (indeksznaku + klucz) % len(ZNAKI)
# print(f"Po przesunieciu o {klucz} uzyskamy {nowy_indeks}")

# jeśli nie chcemy skorzystać z naszej własnej tablicy znaków,
# to można to też zrobic korzystajac z tablicy znaków Unicode.
# Przydadzą się wtedy funkcje:
# ord - zwraca nr unicode znaku
# chr - zwraca znak po podaniu nr unicode

Turtle i ciąg Fibonacciego

# Fibonacci graficznie - kwadraty w połowie wypełnione kolorem

import turtle
import random

zolwik = turtle.Turtle() # stworz obiekt rysujacego zolwia pod zmienna zolwik
zolwik.speed(1) # ustaw szybkosc zolwia wbudowana metoda speed
zolwik.shape("turtle") # uzyj metody shape do ustawienia wygladu kursora / zolwia
zolwik.pensize(3)

n = 77
fib = [0, 1]
nast = fib[-2] + fib[-1]
while nast <= n:
    fib.append(nast)
    nast = fib[-2] + fib[-1]   
print(fib)

kolory = [
    "CornflowerBlue",
    "DarkOrchid",
    "IndianRed",
    "DeepSkyBlue",
    "LightSeaGreen",
    "wheat",
    "SlateGray",
    "SeaGreen",
]

dlugosc_boku = 25
while True:
    for mnoznik in fib[1:]:
        zolwik.fillcolor(random.choice(kolory))
        for _ in range(5):
            zolwik.begin_fill()
            zolwik.forward(dlugosc_boku*mnoznik)
            zolwik.left(90)
        zolwik.forward(dlugosc_boku*mnoznik)
        zolwik.end_fill()
    
turtle.exitonclick()

# Fibonacci graficznie - kwadraty całe wypełnione kolorem

import turtle
import random

zolwik = turtle.Turtle() # stworz obiekt rysujacego zolwia pod zmienna zolwik
zolwik.speed(1) # ustaw szybkosc zolwia wbudowana metoda speed
zolwik.shape("turtle") # uzyj metody shape do ustawienia wygladu kursora / zolwia
zolwik.pensize(3)

n = 77
fib = [0, 1]
nast = fib[-2] + fib[-1]
while nast <= n:
    fib.append(nast)
    nast = fib[-2] + fib[-1]   
print(fib)

kolory = [
    "CornflowerBlue",
    "DarkOrchid",
    "IndianRed",
    "DeepSkyBlue",
    "LightSeaGreen",
    "wheat",
    "SlateGray",
    "SeaGreen",
]

dlugosc_boku = 25
while True:
    for mnoznik in fib[1:]:
        zolwik.fillcolor(random.choice(kolory))
        zolwik.begin_fill()
        for _ in range(5):
            zolwik.forward(dlugosc_boku*mnoznik)
            zolwik.left(90)
        zolwik.end_fill()
        zolwik.forward(dlugosc_boku*mnoznik)
        
    
turtle.exitonclick()

Zadanie. Książka kontaktów

# imie / pseudonim
# dane kontaktowe (dowolne - tel, / email)
# dodawanie, aktualizacja, usuwanie
# wybor dziwnego wariantu

kontakty = {"Alice" : "[email protected]",
            "Bob" : "[email protected]"
            }

def nowy_kontakt():
    pseudonim = input("Podaj pseudonim: ").strip()
    if pseudonim in kontakty.keys():
        print("Taki kontakt już istnieje!")
    else:
        dane = input("Podaj e-mail): ").strip()
        kontakty[pseudonim] = dane
        print("Dodano nowy kontakt")
        print("***************")

def aktualizacja_kontaktu():
    wybrany_kontakt = input("Wybierz imię/pseudonim do modyfikacji: ").strip()
    if wybrany_kontakt in kontakty:
        kontakty[wybrany_kontakt] = input("Wprowadź nowe dane kontaktowe: ").strip()
    else:
        print("Taki kontakt już istnieje!")

def usuwanie_kontaktow():
    pseudonimdelete = input("Wprowadź imię/pseudonim kontaktu do usunięcia: ").strip()
    if pseudonimdelete in kontakty.keys():
        kontakty.pop(pseudonimdelete) # lub del kontakty[pseudonimdelete]
        print("Kontakt usunięty")
        print("***************")
    else:
        print("Nie ma takiej pozycji w kontaktach")
        print("***************")


def wyswietlanie_kontaktow():
    for imie, dane in kontakty.items():
        print(f"{imie}: {dane}")
    print("***************")

def wyswietlenie_wybranego():
    wybrany_kontakt = input("Podaj imię/pseudonim kontaktu: ").strip()
    if wybrany_kontakt in kontakty:
        print(wybrany_kontakt)
        print(kontakty[wybrany_kontakt])
        print("***************")


while True:
    print("Książka kontaktowa:")
    print("Wybierz operację:")
    print("1 - dodanie wpisu")
    print("2 - aktualizacja wpisu")
    print("3 - usunięcie wpisu")
    print("4 - wydruk wszystkich kontaktów")
    print("5 - wydruk konretntego kontaktu")
    print("6 - koniec programu")

    wybor = input("Podaj kod operacji: ").strip()

    if wybor == "1":
        print("1")
        nowy_kontakt()
    elif wybor == "2":
        print("2")
        aktualizacja_kontaktu()
    elif wybor == "3":
        print("3")
        usuwanie_kontaktow()
    elif wybor == "4":
        print("4")
        wyswietlanie_kontaktow()
    elif wybor == "5":
        print("5")
        wyswietlenie_wybranego()
    elif wybor == "6":
        print("Koniec programu")
        break
    else:
        print("Nie ma opcji o podanym kodzie.")  
# # Zadanie - kwestionariusz

# Zbudowaliśmy strukturę danych dla kwestionariusza - wygląda ona tak:

# ```python
# quiz = [
#     {
#         "pytanie": "tresc pytania",
#         "odpowiedzi": [
#             ("odp1", 1),
#             ("odp2", 0),
#             ("odp3", 0),
#         ],
#     },
# ]
# ```

# ## Baza - użyj pętli do wyświetlenia pytań.

# Użyj pętli `for` żeby wyświetlić użytkownikowi pytania i możliwe warianty odpowiedzi.

# ## Krok 1 - dodaj losową kolejność odpowiedzi

# Skorzystaj z biblioteki `random` i  dostępnej w niej funkcji `shuffle` do przemieszania kolejności odpowiedzi. Jeśli masz nr / literkę odpowiedzi dodaną w jej treści  - na razie nie przejmuj się tym. Na tym etapie jeszcze nie liczymy też pkt. - zajmujemy się jedynie wyświetlaniem zawartości.

# ## Co dalej?

# Dążymy do tego, żeby w pętli obsłużyć też zliczanie punktów. Jakie kłopoty na tym etapie widzę?

# - jeśli pomieszam kolejność odpowiedzi to jak przypisywać to co wpisał użytkownik z punktacją (czyli np. "a" nie będzie już na pierwszej pozycji tylko np. na trzeciej - co z tym zrobić)?
# - co zrobić żeby "pomieszać" też pytania?
# - dobrze by było na początku poprosić użytkownika o imię etc. Jakiej struktury danych do tego moglibyśmy użyć?

print("*************************")
print("""Witaj w quizie Python""")
print("*************************")

print("Zaczynamy!")
dane_gracza = input("Podaj swoje imię: ")
print("")
punkty = 0

baza_pytan = [ 
    {
        "pytanie" : "Gdzie w komórce eukariotycznej znajduje się DNA?", 
        "odpowiedzi" : [
            ("w jądrze komórkowym", 1), 
            ("w wakuoli", -1),
            ("w cytoplazmie", -1)
        ]
    } ,
{
        "pytanie" : "Jaką strukturę przestrzenną ma cząsteczka DNA?", 
        "odpowiedzi" : [
            ("Podwójna helisa", 1), 
            ("Potrójna spirala", -1),
            ("Potrójna helisa", -1)
        ]
    } ,
{
        "pytanie" : "Która zasada azotowa łączy się zawsze z tyminą?", 
        "odpowiedzi" : [
            ("Guanina", -1), 
            ("Adenina", 1),
            ("Cytozyna", -1)
        ]
    } ,
{
        "pytanie" : "Z jakich zasad azotowych zbudowany jest DNA?", 
        "odpowiedzi" : [
            ("Adenina, Tymina, Cytozyna, Guanina", 1), 
            ("Adenina, Uracyl, Cytozyna, Guanina", -1),
            ("Adenina, Tyrozyna, Cysteina, Glutamina", -1)
        ]
    } ,
{
        "pytanie" : "Jaką rolę pełni DNA w organizmach żywych?", 
        "odpowiedzi" : [
            ("Odpowiada za transport tlenu", -1), 
            ("Dostarcza energii do reakcji komórkowych", -1),
            ("Jest nośnikiem informacji genetycznej", 1)
        ]
    }
]



import random
pytania_pomieszane = baza_pytan.copy()
random.shuffle(pytania_pomieszane)


for pytanie in pytania_pomieszane:
    print("\n" , pytanie["pytanie"])
    for i, (odp, punkty_odp) in enumerate(pytanie["odpowiedzi"], start=1):
        print(f"{i}. {odp}")
    wybor = int(input("Wybierz odpowiedź (numer 1, 2 lub 3): "))
    while True:
        try:
            wybor = int(input("Wybierz odpowiedź (numer 1, 2 lub 3): "))
            if 1 <= wybor <= 3:
                break  
            else:
                wybor = int(input("Wybierz odpowiedź (numer 1, 2 lub 3): "))
        except ValueError:
            print("To nie jest liczba! Spróbuj ponownie.")
    punkty += pytanie["odpowiedzi"][wybor-1][1]


print("\n***************************************")
print(f"Twoja liczba punktów to {punkty}/5")
if punkty >= 3:
    print("Test zaliczony. Gratulacje!")
else:
    print("Spróbuj jeszcze raz")
print("\n***************************************")