> For the complete documentation index, see [llms.txt](https://bytistan.gitbook.io/baytistan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bytistan.gitbook.io/baytistan/kedi-problemi/seviye-6-yagmur-yagiyor.md).

# Seviye 6:  Yağmur Yağıyor

## Amaç

`settings.py` dosyasına eklediğin ASCII kediyi, yağmur damlası animasyonu vererek cizilmelisin.

### Beklenen Davranış

<figure><img src="https://1592666604-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fy0QTCliryu3DkL9lOYgq%2Fuploads%2FJfy6YSYCr4gj2Ic59nXO%2Fezgif.com-video-to-gif-converter(6).gif?alt=media&amp;token=ed928817-86bc-4d6d-8035-1c99cafe04c2" alt=""><figcaption><p>1.0</p></figcaption></figure>

* Karakterlerin farklı aralıklar ile aşağıya düşmesi.
* Karakterlerin düştükleri noktlarda kalmaları.

## Problemin Çözümü

Cevap çok uzun olduğu için gelin tek tek ele alalım. Artık kodumuz tek fonksiyon içerisinde sırıtacağı için sınıf kullanarak devam ediyorum.

<details>

<summary>Cevap</summary>

```python
from settings import *
import time 
import os
import random

class PrintCat:
    def __init__(self):
        self.new_line = "\n"
        self.interval = 0.05

        self.data = [ 
            {
                "point": random.randint(0,len(ascii_art) // 2),
                "loc": 0,
                "row": len(ascii_art),
                "column":i
            }

            for i in range(len(ascii_art[0]))
        ] 
        
        self.pic = [
            " " * len(ascii_art[0])

            for _ in range(len(ascii_art))
        ]
        
        self.count = 0
    
    def reset(self):
        self.count += 1
        os.system("clear")
        self.flag = True        
    
    def print_cat(self):
        screen = self.new_line.join(self.pic)
        print(screen)

    def update(self):
        for index,item in enumerate(self.data):
            point = item.get("point")
            loc = item.get("loc")
            row = item.get("row")
            column = item.get("column")

            if point > self.count or row == 0:
                continue
            
            self.flag = False 

            if loc == row:
                self.data[index]["loc"] = 0 
                self.data[index]["row"] -= 1 
                continue
            
            self.data[index]["loc"] += 1 

            if loc not in [0,len(ascii_art)]:
                self.pic[loc - 1] = self.pic[loc - 1][:column] + " " + self.pic[loc - 1][column + 1:]
            
            self.pic[loc] = self.pic[loc][:column] + ascii_art[loc][column] + self.pic[loc][column + 1:]

    def run(self):
        while True:
            self.reset()
            self.update()
            self.print_cat()

            time.sleep(self.interval)

            if self.flag:
                break

if __name__ == "__main__":
    print_cat = PrintCat()
    print_cat.run()
```

</details>

### Değişkenlerin Tanımlanması

Yağmur animasyonu oluşturacağım için bu her bi yağmur damlası hakkında bazı bilgileri saklamam gerek, bu yüzden `data` listesini kullanıyorum.&#x20;

```python
# ...
   # ...
    self.data = [ 
        {
            "point": random.randint(0,len(ascii_art) // 2),
            "loc": 0,
            "row": len(ascii_art),
            "column":i
        }

        for i in range(len(ascii_art[0]))
    ] 
    # ...
# ...
```

Şimdi gelin sözlük içerisindeki değişkenleri teker teker açıklayalım.

* `point` : Dağınık bir yağmur animasyonu oluşturabilmek için, elemanların başlamaları gereken noktaları belirliyor.
* `loc` : Dikey eksende bulundıkları konumu ifade etmekte.
* `row` : Ulaşacakları nokta taban olarak da düşünülebilir.
* `column` : Elemanın hangi satırda olduğu bilgisini tutuyor.

```python
# ...
    def __init__(self):
        # ...  
        self.pic = [
            " " * len(ascii_art[0])
    
            for _ in range(len(ascii_art))
        ]
        # ...
# ...
```

Bu liste tablo olarak düşünülebilir koordinatlarını bildiğim parçaları bu liste üzerine yerleştiriyorum.

### Metotlar

Bir fonksiyon içerisine birden fazla konuda kod yazmak hem okunulabilirliği hem de kod kalitesini ve yönetimini zorlaştırıyor. Şimdi sınıf içerisindeki metotları tek tek inceleyelim.

#### Sıfırla : `restart`

```python
# ...    
    def reset(self):
        self.count += 1
        os.system("clear")
        self.flag = True      
# ...
```

`restart`, `run` metot'u içerisinde while döngüsünün ilk kısmında çalışıyor. Gelin şimdi içerisine bakalım:

* `self.count` döngü her döndüğünde bir arttırılıyor,&#x20;
* `os.system("clear")` ile ekran temizleniyor.
* `self.flag` değişkeni `True` yapılıyor eğer herhangi bir işlem yapılmaz ise döngüyü sonlandırmak için var.

***

#### Yazdır : `print_cat`

```python
# ...
    def print_cat(self):
        screen = self.new_line.join(self.pic)
        print(screen)
# ...
```

Her satırın sonuna `"\n"` karakteri ekleyip bir `screen` metini içerisinde birleştiriyor sonrasında `print`  fonksiyonu ile yazdırıyor.

***

#### Güncelle : `update`

Bu method çok uzun olduğu için parça parça anlatalım.

```python
# ...
    def update(self):
        for index,item in enumerate(self.data):
            point = item.get("point")
            loc = item.get("loc")
            row = item.get("row")
            column = item.get("column")
            # ...
# ...
```

`self.data` değişkeni üzerinde `enumerate` fonksiyonu ile dönüyorum ve `self.data` içerisindeki değerleri değişkenlere atıyorum.

```python
# ...
    def update(self):
        for index, item in enumerate(self.data):
            # ...
            if point > self.count or row == 0:
                continue
            
            self.flag = False 
            # ...
# ...
```

Bu kısımda `self.count` değeri `point`'den fazla mı diye bakıyorum eğer fazla ise döngü `continue` ile devam ediyor yani alt kısımlar işlenmiyor veya `row` değeri 0 mı diye bakıyorum çünkü 0 olursa başlangıç noktasına gelmiş ve işini bitirimiş demektir.

`self.flag = False` yapma sebebim eğer yukarıda bulunan koşul geçilirse hala işlemde olan karakterler var demektir bu da döngünün devam etmesi gerektiği anlamına gelir.

```python
# ...
    def update(self):
        for index, item in enumerate(self.data):
            # ...
            if loc == row:
                self.data[index]["loc"] = 0 
                self.data[index]["row"] -= 1 
                continue
            
            self.data[index]["loc"] += 1 
            # ...
# ...
```

Bu kısımda eğer karakter istenilen konuma gelmiş ise onu orada bırakılıyor ve yenisini almak için `loc` değeri sıfırlanıyor. Bir üst kısıma karakterler yerleştirmeye devam edilebilmesi için `row` değerini bir azaltılıyor ve son olarak döngü `continue` ile devam ettiriliyor.

Eğer `loc` değeri bu kısımı geçerse artmaya devam etmesi lazım o yüzden bir arttırıyoruz.

{% hint style="warning" %}
Döngü içerisindeki yerel değişkende değil sınıf içerisindeki `self.data` değişkeni üzerinde değişiklik yapıyoruz, yani `loc` değişkeni aynı kalıyor ama `self.data[index]["loc"]` değeri değişiyor.
{% endhint %}

```python
# ...
    def update(self):
        for index, item in enumerate(self.data):
            # ...
            if loc not in [0,len(ascii_art)]:
                self.pic[loc - 1] = self.pic[loc - 1][:column] + " " + self.pic[loc - 1][column + 1:]
                
            self.pic[loc] = self.pic[loc][:column] + ascii_art[loc][column] + self.pic[loc][column + 1:]
# ...
```

Son olarak önceden çizilen kısım resim üzerinden siliniyor ve yeni konumunda karakter tekrar yerleştiriliyor.

{% hint style="info" %}
python programlama dilinde `string` değişkenler değiştirilemez olduğu için bir karakter yerleştirilirken tekrar oluşturmak gerekiyor. Aşağıdaki örnek üzerinde daha iyi anlaşılmakta.

```bash
dogukan@train:~/Develop/Others/meow$ python3 
Python 3.12.3 (main, Sep 11 2024, 14:17:37) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> some_string = "tomato"
>>> some_string[3] = "_"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> some_string = some_string[:3] + "_" + some_string[4:]
>>> print(some_string)
tom_to
```

{% endhint %}

***

#### Çalıştır : `run`

Temel olarak bütün metotların bir döngü içerisinde beraber çalıştığı nokta.

```python
# ...
    def run(self):
        while True:
            self.reset()
            self.update()
            self.print_cat()

            time.sleep(self.interval)

            if self.flag:
                break
# ...
```

`self.flag` eğer `True` ise döngüyü sonlandırıyorum çünkü işlemde olan herhangi bir karakterin olmadığı anlamına geliyor.

Döngünün hızını `time.sleep(self.interval)` ile belirliyorum yoksa bilgisayarın hızına orantılı olarak çalışıyor.

### Programın Çalıştırılması

Nesnemi bir değişken içerisinde tanımlayıp `run` metot'unu çağırıyorum.

```python
if __name__ == "__main__":
    print_cat = PrintCat()
    print_cat.run()
```
