Etiket: matrix

  • Matrix Yağmurunu Terminalde Kodlamak

    Matrix Yağmurunu Terminalde Kodlamak

    Siyah ekran, yukarıdan aşağıya akan yeşil karakterler, her sütunun başında parlayan bir harf. 1999’da vizyona giren The Matrix’in bu “dijital yağmur” sahnesi, bilgisayar dünyasının en tanınan görüntülerinden biri oldu. Bu yazıda aynı efekti 50 satırlık bir Python koduyla kendi terminalinde yaratıyoruz. Hiçbir kütüphane kurmadan, sadece Python’un kendi araçlarıyla.

    Kodlar hazır bir not defterinde: Colab’da Aç. Not defterinde Colab’da çalışan bir sürüm de var.

    Efekt aslında nasıl çalışıyor?

    Görüntü karmaşık ama arkasındaki fikir basit. Ekranı sütunlara böl ve her sütunda bir “damla” düşün:

    1. Her sütunun bir damlası var ve damlanın başı belirli bir satırda duruyor.
    2. Her karede damla bir satır aşağı iniyor. Yeni yerine parlak beyaz rastgele bir karakter yazılıyor; bir önceki yer yeşile dönüyor.
    3. Damlanın belirli bir uzunlukta kuyruğu var. Kuyruğun ucundaki karakter silinerek boşluk bırakılıyor.
    4. Damla ekranın altından çıkınca, rastgele bir yükseklikten yeniden başlıyor.

    Bütün sütunlar aynı kuralı izliyor; farklı yüksekliklerden başladıkları için de yağmur rastgele ve canlı görünüyor. Yani ekranda gördüğün “sihir”, Python Dersi 6’daki döngüler ve Python Dersi 7’deki listelerle yapılan küçük bir simülasyon.

    Terminale renk ve konum söylemek: ANSI kaçış kodları

    Normalde print yazıyı imlecin olduğu yere, sırayla yazar. Biz ise ekranın istediğimiz noktasına, istediğimiz renkte yazmak istiyoruz. Bunun için terminallerin anladığı özel komutlar var: ANSI kaçış kodları.

    Hepsi \033[ ile başlar (çoğu yerde “ESC [” diye yazılır). Terminal bunları ekrana yazmaz, komut olarak uygular:

    KodNe yapar?
    \033[32mYazı rengini yeşil yapar
    \033[97mYazı rengini parlak beyaz yapar
    \033[0mRenkleri sıfırlar
    \033[2JEkranı temizler
    \033[5;10Hİmleci 5. satır, 10. sütuna götürür
    \033[?25l / \033[?25hİmleci gizler / gösterir

    Kendin denemek için terminalde şunu çalıştır:

    print("\033[32mBu yazı yeşil\033[0m, bu yazı normal.")

    Kod

    import os
    import random
    import sys
    import time
    KARAKTERLER = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモ0123456789"
    YESIL = "\033[32m" # yazı rengi: yeşil
    BEYAZ = "\033[97m" # yazı rengi: parlak beyaz
    SIFIRLA = "\033[0m" # renkleri sıfırla
    TEMIZLE = "\033[2J" # ekranı temizle
    IMLEC_GIZLE = "\033[?25l"
    IMLEC_GOSTER = "\033[?25h"
    def yaz(satir, sutun, metin):
    """İmleci (satir, sutun) konumuna götürüp metni oraya yazar."""
    sys.stdout.write(f"\033[{satir};{sutun}H{metin}")
    def yagmur(hiz=0.05, kuyruk=12):
    genislik, yukseklik = os.get_terminal_size()
    yukseklik -= 1 # en alt satıra yazıp ekranı kaydırmayalım
    damlalar = [random.randint(-yukseklik, 0) for _ in range(genislik)]
    sys.stdout.write(TEMIZLE + IMLEC_GIZLE)
    try:
    while True:
    for x in range(genislik):
    y = damlalar[x] # bu sütundaki damlanın başı
    if 1 <= y <= yukseklik:
    yaz(y, x + 1, BEYAZ + random.choice(KARAKTERLER))
    if 1 <= y - 1 <= yukseklik:
    yaz(y - 1, x + 1, YESIL + random.choice(KARAKTERLER))
    if 1 <= y - kuyruk <= yukseklik:
    yaz(y - kuyruk, x + 1, " ") # kuyruğun ucunu sil
    damlalar[x] += 1
    if y - kuyruk > yukseklik: # damla ekranın altından çıktı
    damlalar[x] = random.randint(-yukseklik, 0)
    sys.stdout.flush()
    time.sleep(hiz)
    except KeyboardInterrupt: # Ctrl+C ile çıkış
    pass
    finally:
    sys.stdout.write(SIFIRLA + IMLEC_GOSTER + TEMIZLE + "\033[H")
    if __name__ == "__main__":
    yagmur()

    Kod adım adım

    Karakterler. Filmdeki yağmur, Japon katakana alfabesine benzeyen karakterler ve rakamlardan oluşuyor. Biz de KARAKTERLER metnine yarım genişlikteki katakana harflerini ve rakamları koyduk. “Yarım genişlik” önemli: bu harfler terminalde tam bir sütun kaplar, böylece sütunlar kaymaz. random.choice(KARAKTERLER) her seferinde bu metinden rastgele bir karakter seçiyor.

    Ekran boyutu. os.get_terminal_size() terminal penceresinin kaç sütun genişliğinde ve kaç satır yüksekliğinde olduğunu verir. Yağmur pencereni büyütürsen daha geniş yağar.

    Damlalar listesi. damlalar her sütun için tek bir sayı tutan bir liste: o sütundaki damlanın başı hangi satırda? Başlangıçta hepsi ekranın üstünde, eksi sayılarda; böylece her damla farklı zamanda ekrana girer. Bu tek satırlık liste kurma yazımını (liste üreteci) yarınki kod kartında ayrıntılı anlatacağım.

    Ana döngü. while True sonsuza kadar döner. Her turda tüm sütunlar gezilir ve her sütun için üç iş yapılır: başa beyaz karakter, bir üstüne yeşil karakter, kuyruğun ucuna boşluk. if 1 <= y <= yukseklik kontrolleri, ekranın dışına yazmaya kalkışmamak için.

    Her seferinde tüm ekranı çizmiyoruz. Sadece değişen üç hücreyi güncelliyoruz. Bu yüzden kod geniş bir pencerede bile akıcı çalışıyor. sys.stdout.write ise print gibi, ama sona otomatik satır atlama eklemiyor; flush() da biriken yazıları tek seferde ekrana gönderiyor.

    Temiz çıkış. Ctrl+C’ye basınca Python KeyboardInterrupt hatası fırlatır. try / except bu hatayı yakalıyor, finally bloğu da ne olursa olsun çalışıp renkleri sıfırlıyor, imleci geri getiriyor ve ekranı temizliyor. Bunu yapmasaydık, programdan çıktıktan sonra terminal yeşil ve imleçsiz kalırdı. Hata yönetimini ileride Python Dersi 13’te ayrıntılı göreceğiz.

    Çalıştırmak

    1. Kodu kopyala ve matrix.py adıyla kaydet.
    2. Terminali aç, dosyanın olduğu klasöre git ve python matrix.py yaz (bazı sistemlerde python3 matrix.py).
    3. Yağmuru durdurmak için Ctrl+C.

    Mac ve Linux terminallerinde, Windows’ta da Windows Terminal’de çalışır. Eski Windows komut isteminde (cmd) renkler görünmeyebilir; o zaman Windows Terminal’i dene.

    Colab’da çalıştırmak

    Colab’ın çıktı alanı gerçek bir terminal değil; renkleri gösterir ama imleci istediğimiz yere götüremez. Bu yüzden not defterinde farklı bir yöntem kullandım: Ekranı iç içe bir liste olarak tutuyoruz (her satır bir liste, ekran da satırların listesi) ve her karede ekranı silip baştan yazdırıyoruz:

    izgara = [[" "] * genislik for _ in range(yukseklik)]
    izgara[y][x] = random.choice(KARAKTERLER) # y. satırın x. hücresi

    Sonuç terminaldeki kadar akıcı olmasa da aynı fikir. İki yöntemi karşılaştırmak da bir ders: Aynı görüntüyü “sadece değişeni güncelle” ya da “her şeyi baştan çiz” diye iki farklı algoritmayla üretebiliyoruz.

    Kendin dene

    • yagmur(hiz=0.1) ile yavaş, yagmur(hiz=0.02) ile hızlı bir yağmur.
    • kuyruk=25 ile uzun kuyruklu damlalar.
    • KARAKTERLER metnine kendi adının harflerini ekle.
    • Yeşil yerine mavi bir yağmur için YESIL = "\033[34m".
    • Zor olan: Ekranın ortasında sabit duran bir yazı göster, yağmur onun etrafından aksın.

    Son söz

    Matrix’i ilk izlediğimde lisedeydim. O akan yeşil ekrana hayran kalmıştım. Belki de bilgisayar mühendisliği bana ilk o zaman bu kadar havalı gelmişti. Yıllar sonra aynı yağmuru kendi yazdığım birkaç satır kodla kendi terminalimde görmek ise bambaşka bir duyguydu: O zaman ekranın arkasında bir sihir olduğunu düşünüyordum, şimdi o sihrin döngülerden ve listelerden oluştuğunu biliyorum.

    Bir de tavsiye: Hem görsel olarak hem de sorduğu sorularla çok güzel, felsefi bir film serisi. İzlemediysen mutlaka izle; izlediysen bu yazıdan sonra bir kez daha izlemek isteyebilirsin.