Skip to content

Enigma w Konsoli

Oto kompletny, gotowy do uruchomienia kod w Pythonie, który emuluje działanie 3-wirnikowej maszyny Enigma I (najpopularniejszego modelu wojskowego) wraz z odwracaczem (reflektorem) oraz pełną mechanizmem obrotu bębnów (wliczając w to słynny krok potrójny, czyli double stepping).
Kod został napisany tak, aby po uruchomieniu działał jako automatyczny wygaszacz / animacja w tle. Generuje losowy tekst, a następnie litera po literze pokazuje na żywo aktualną pozycję wirników (ich obrót) oraz proces szyfrowania.

Kod symulatora Enigmy (Python 3)

Możesz skopiować poniższy kod i zapisać go jako plik np. enigma_stream.py. Wymaga jedynie standardowej biblioteki Pythona (nie musisz niczego instalować przez pip).

Python
import time
import random
import sys

class EnigmaMachine:
    def __init__(self):
        # Definicje okablowania historycznych wirników Enigmy I
        self.ROTOR_WIRING = {
            'I':   "EKMFLGDQVZNTOWYHXUSPAIBRCJ",
            'II':  "AJDKSIRUXBLHWTMCQGZNPYFVOE",
            'III': "BDFHJLCPRTXVZNYEIWGAKMUSQO"
        }
        # Punkty obrotu (Notch) - moment, w którym wirnik popycha następny
        self.ROTOR_NOTCH = {'I': 'Q', 'II': 'E', 'III': 'V'}

        # Definicja reflektora (Reflektor B)
        self.REFLECTOR = "YRUHQSLDPXNGOKMIEBFZCWVJAT"

        # Początkowe ustawienie bębnów (np. 'A', 'A', 'A')
        self.r_pos = [0, 0, 0] # [Lewy, Środkowy, Prawy]
        self.rotors = ['I', 'II', 'III']

    def set_positions(self, pos_str):
        """Ustawia początkowe litery wirników, np. 'XYZ'"""
        self.r_pos = [ord(c) - ord('A') for c in pos_str.upper()]

    def _rotate(self):
        """Mechanizm obrotu wirników uwzględniający Double Stepping (potrójny krok)"""
        # Pobranie aktualnych liter w miejscach karbów (notch)
        r_notch = self.ROTOR_NOTCH[self.rotors[0]] # Lewy
        m_notch = self.ROTOR_NOTCH[self.rotors[1]] # Środkowy
        p_notch = self.ROTOR_NOTCH[self.rotors[2]] # Prawy

        r_char = chr(self.r_pos[0] + 65)
        m_char = chr(self.r_pos[1] + 65)
        p_char = chr(self.r_pos[2] + 65)

        # Flagi obrotu
        rotate_left = False
        rotate_mid = False
        rotate_right = True # Prawy wirnik obraca się zawsze przy każdym klawiszu

        # Double stepping: jeśli środkowy jest na pozycji karbu, obraca siebie i lewy
        if m_char == m_notch:
            rotate_left = True
            rotate_mid = True
        # Jeśli prawy jest na pozycji karbu, obraca środkowy
        elif p_char == p_notch:
            rotate_mid = True

        # Zastosowanie obrotów w pamięci
        if rotate_left:  self.r_pos[0] = (self.r_pos[0] + 1) % 26
        if rotate_mid:   self.r_pos[1] = (self.r_pos[1] + 1) % 26
        if rotate_right: self.r_pos[2] = (self.r_pos[2] + 1) % 26

    def _map_forward(self, char_idx, rotor_name, pos):
        """Przepływ sygnału od klawiatury do reflektora"""
        shift_idx = (char_idx + pos) % 26
        out_char = self.ROTOR_WIRING[rotor_name][shift_idx]
        out_idx = (ord(out_char) - ord('A') - pos) % 26
        return out_idx

    def _map_backward(self, char_idx, rotor_name, pos):
        """Przepływ powrotny sygnału od reflektora do żarówki"""
        shift_idx = (char_idx + pos) % 26
        out_char = chr(self.ROTOR_WIRING[rotor_name].index(chr(shift_idx + 65)) + 65)
        out_idx = (ord(out_char) - ord('A') - pos) % 26
        return out_idx

    def encrypt_char(self, char):
        """Szyfruje pojedynczy znak i obraca bębny"""
        if not char.isalpha():
            return char

        char = char.upper()
        self._rotate()

        idx = ord(char) - ord('A')

        # Przejście przez wirniki w prawo -> lewo
        idx = self._map_forward(idx, self.rotors[2], self.r_pos[2]) # Prawy
        idx = self._map_forward(idx, self.rotors[1], self.r_pos[1]) # Środkowy
        idx = self._map_forward(idx, self.rotors[0], self.r_pos[0]) # Lewy

        # Reflektor
        idx = (ord(self.REFLECTOR[idx]) - ord('A')) % 26

        # Przejście powrotne lewo -> prawo
        idx = self._map_backward(idx, self.rotors[0], self.r_pos[0]) # Lewy
        idx = self._map_backward(idx, self.rotors[1], self.r_pos[1]) # Środkowy
        idx = self._map_backward(idx, self.rotors[2], self.r_pos[2]) # Prawy

        return chr(idx + 65)

# --- SKRYPT AUTOMATYCZNEJ PREZENTACJI W TLE ---
if __name__ == "__main__":
    enigma = EnigmaMachine()
    enigma.set_positions("AAA")

    print("\033[H\033[J") # Czyszczenie ekranu terminala
    print("=============================================================")
    print("  SYMULACJA HISTORII ENIGMY I — TRANSMISJA AUTOMATYCZNA")
    print("=============================================================\n")

    try:
        while True:
            # Generowanie losowej "wiadomości wojskowej" do zaszyfrowania
            length = random.randint(15, 30)
            raw_text = "".join(random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") for _ in range(length))

            for char in raw_text:
                cipher_char = enigma.encrypt_char(char)

                # Odczyt aktualnego stanu bębnów jako litery (np. K - M - A)
                pos_str = f"[{chr(enigma.r_pos[0]+65)}][{chr(enigma.r_pos[1]+65)}][{chr(enigma.r_pos[2]+65)}]"

                # Formatowanie linii wyświetlania w terminalu
                sys.stdout.write(f"\rBĘBNY: {pos_str}  |  WEJŚCIE: {char}  --->  SZYFR: {cipher_char}")
                sys.stdout.flush()

                # Szybkość "obracania się" mechanizmu (0.15 sekundy na znak)
                time.sleep(0.15)

            # Mała przerwa między paczkami danych, udająca transmisję kolejnej wiadomości
            print("  [OK]")
            time.sleep(1.0)

    except KeyboardInterrupt:
        print("\n\nZatrzymano symulację.")

Jak to wygląda i działa w tle podczas wykładu?

1. Kod uruchamia nieskończoną pętlę (while True). Nie musisz nic klikać.
2. W terminalu zobaczysz nieustannie odświeżającą się linię tekstu.
3. Wskaźnik BĘBNY: [A][A][B] zacznie gwałtownie zmieniać litery. Zobaczysz, że prawy wirnik zmienia się co krok, środkowy raz na 26 kroków, a lewy jeszcze rzadziej – dokładnie tak, jak w fizycznym urządzeniu.
4. Całość daje bardzo surowy, "kryptograficzny" efekt tekstowy, idealnie pasujący jako dynamiczne tło, kiedy opowiadasz o matematycznym podejściu von Neumanna do systemów binarnych i przetwarzania danych.