---
# System prepended metadata

title: DEMO. Ефекти 2. Порядок
tags: [1_basic]

---

---
tags: 1_basic
---

# DEMO. Ефекти 2. Порядок

![](https://i.imgur.com/CSzk5lN.gif)

Note:

План:
- цикл for
- counter,i 
- процедури для організації коду

---

## Зав'язка

Гостросюжетний текст про те, як раптом комусь знадобилися скіли програміста.

---

1. Зигзаг
2. Два симетричні зигзаги
3. Вікно
4. Зигзаги "за" вікном

Слайди з контентом

---

Живий кодинг, експерименти, демо

Note:

Копія коду для тих, хто не був присутній на лекції і не дивився відео.

---

### DEMO.Effects2.A0.0 (обов'язкове завдання)

3 або 4 симетричні зигзаги

----

### DEMO.Effects2.A0.1 (обов'язкове завдання)

Намалювати трикутник заданого розміру.

----

### DEMO.Effects2.A0.2 (обов'язкове завдання)

Один з перших ефектів "вогню" з'явився в 1980 роках, і з тих пір неодноразово був використаний у демках та іграх. Цей ефект реалізовували на всіх екранах, включаючи допотопні MS-DOS.

Коли я вперше побачив його код в 9 класі, я не міг зрозуміти як це працює. Але виглядало дуже круто!

<details>
<summary>Насправді...</summary>
Щоб зрозуміти цей код, треба знати:
<ul>
<li>координатну площину
<li>двовимірні масиви, слайси масивів, операції над рядками масивів
<li>матриці та роботу за матрицями: додавання, транспонування, скалярне множення
<li>розуміти в якому порядку оновлюються дані в масивах та матрицях
<li>кольорові схеми RGB та HSL, чим відрізняються та коли друга зручніша
<li>механізм виводу пікселів на екран, дабл буфер
<li>середнє арифметичне
<li>додавання по модулю та коли його треба використовувати
<li>8-бітні палітри
<li>і це тільки теми, які не стосуються самого Python!
</ul>
</details>
<br />

Спробуй і ти запусти "вогонь"! Вибери режим, 1 або 2, а потім переглянь код. Чи зможеш зрозуміти як воно працює?

P.S. Цей та інші ефекти -- це прадіди "демосцени", мінімалістичних арт-перфомансів за допомогою програмування. В завданні [DEMO.Effects2.A3.0](#DEMO.Effects2.A3.0) є більше деталей.

```python=
import pygame
import random
import colorsys


def fps_text(clock, font):
    """
    Повертає об'єкт Surface з текстом про кількість кадрів в секунду
    (frames per second, FPS)
    """
    fps = "FPS: " + str(int(clock.get_fps()))
    return font.render(fps, 1, pygame.Color("violet"))

def color_HSL(h, s, l):
    """
    Перетворює колір з системи HSL в систему RGB.
    HSL дозволяє дуже просто робити палітру з "вогняними" кольорами

    На вході - кольори у діапазоні 0-255
    На виході - (r,g,b), також у діапазоні 0-255
    """
    color = colorsys.hls_to_rgb(h/255, l/255, s/255)
    return tuple( int(x*255) for x in color )

def fire_palette(color_shift=0, color_split=1):
    """
    Палітра кольорів на 256 елементів.

    Параметр color_shift дозволяє керувати домінантним кольором вогню
    """
    return [
        color_HSL((x / color_split + color_shift) % 256, 255, min(255, x*2))
        for x in range(256)
        ]

def check_exit():
    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            raise KeyboardInterrupt()

def fire_transform_numpy(arr):
    """
    Ітерація "вогню" через матриці numpy
    """
    height = arr.shape[0]
    for y in range(height-1):
        arr[y, 1:-1] = (
            arr[y+1, 1:-1]
          + arr[y+1,  :-2]
          + arr[y+1, 2:  ]
          + arr[(y+2)%height, 1:-1]
          ) * 0.2499

def fire_transform_pixelarray(arr):
    """
    Ітерація "вогню" через PixelArray об'єкт pygame
    """
    w = arr.shape[0]
    h = arr.shape[1]
    for y in range(0, h-1):
        for x in range(0, w):
            arr[x, y] = int((
                  arr[(x - 1 + w) % w, (y + 1) % h]
                + arr[x % w          , (y + 1) % h]
                + arr[(x + 1) % w    , (y + 1) % h]
                + arr[x % w          , (y + 2) % h]
                ) * 0.2499)

def non_optimized_demo(W, H):
    screen = pygame.display.set_mode((W, H))
    font = pygame.font.SysFont("Arial", 18)
    clock = pygame.time.Clock()

    # surface для вогню
    fire_surface = pygame.Surface((W, H), pygame.HWPALETTE, 8)
    fire_surface.set_palette(fire_palette(color_split=3))
    fire_surface.fill(0)
    
    while True:
        screen.fill(0) # очистити екран

        pxarray = pygame.PixelArray(fire_surface)

        # Щоб зробити демонстрацію цікавішою, кожні 5 секунд будемо змінювати тип вогню
        if pygame.time.get_ticks() // 1000 % 10 < 5:
            # Нижній ряд - випадкові кольори з палітри
            for x in range(0, W):
                pxarray[x, H - 1] = random.randrange(0, 100)
        
        fire_transform_pixelarray(pxarray) 
        pxarray.close()

        # Намалювати вогонь на екран
        screen.blit(fire_surface, (0,0))

        # Намалювати текст FPS на екран
        screen.blit(fps_text(clock, font), (10, 0))

        check_exit()
        clock.tick(200) # FPS, який ми хотіли би бачити
        pygame.display.flip()

def optimized_demo(W, H):
    import numpy as np
    from pygame import surfarray
    
    screen = pygame.display.set_mode((W, H))
    font = pygame.font.SysFont("Arial", 18)
    clock = pygame.time.Clock()
    
    # матриця для вогню. Координати - спочатку y, потім x (!!!)
    fire = np.zeros((H, W)).astype(int)

    # surface для вогню
    fire_surface = pygame.Surface((fire.shape[1], fire.shape[0]), pygame.HWPALETTE, 8)
    fire_surface.set_palette(fire_palette(120))

    while True:
        screen.fill(0) # очистити екран
            
        # Щоб зробити демонстрацію цікавішою, кожні 5 секунд будемо змінювати тип вогню
        if pygame.time.get_ticks() // 1000 % 10 < 5:
            # Нижній ряд - випадкові кольори з палітри
            fire[-1] = np.random.randint(0, 150, (fire.shape[1],))
        
        fire_transform_numpy(fire) 

        # Намалювати вогонь на екран. Не забуваємо про порядок координат!
        surfarray.blit_array(fire_surface, np.transpose(fire))
        screen.blit(fire_surface, (0,0))

        # Намалювати текст FPS на екран
        screen.blit(fps_text(clock, font), (10, 0))

        check_exit()
        clock.tick(200) # FPS, який ми хотіли би бачити
        pygame.display.flip()

def main():
    try:
        variant = None
        while variant not in ("1", "2"):
            variant = input(
                    "Вибери варіант - (1) неоптимізоване демо, (2) оптимізоване демо: ")
        
        pygame.init()

        if variant == "1":
            non_optimized_demo(80, 100)
        else:
            optimized_demo(300, 150)
    except KeyboardInterrupt:
        pass
    finally:
        pygame.quit()

main()
```

---

### Додаткові завдання

#### DEMO.Effects2.A1.0

https://edabit.com/challenges

#### DEMO.Effects2.A1.1



#### DEMO.Effects2.A1.2

#### DEMO.Effects2.A1.3

#### DEMO.Effects2.A1.4

---

### Додаткові завдання (складні!)

#### DEMO.Effects2.A2.0

#### DEMO.Effects2.A2.1

#### DEMO.Effects2.A2.2

---

### Real-world завдання, для експертів

#### DEMO.Effects2.A3.0

В 2004 році вийшла одна комп'ютерна інді мінігра, котра наробила трошки шуму. Називалася `.kkrieger`. Чули про таку?

{%youtube 9f5TYTRhC64%}

Її фішка була в процедурній генерації всіх текстур, звуків та анімацій. Сьогодні Unreal Engine за допомогою мільйонів трикутників в одному об'єкті досягає практично фотореалістичності, а установлений GTA V займає 65 Гб місця на диску! Це нереально великий об'єм даних, і це в 500 000 раз більше ніж мав розмір `.kkrieger`.

Розмір гри був 96Кб, і змагалась гра на конкурсі ігор розміром до 96Кб. Вона була настільки маленька, що люди думали -- це вірус. Ніхто не вірив, що шутер можна вмістити в такий маленький об'єм, доки самостійно її не запускали.

Корені гри походять від поняття **"демосцени"** -- коли програмісти демонструють своє кунг-фу, керування пікселями без грузних движків, без популярних фреймворків. Тільки ти і пікселі (і аудіо). Це -- арт, мистецтво для гіків.

{%youtube 9NBUM2-wkJE%}

- сама демка -- https://files.scene.org/view/demos/groups/farbrausch/kkrieger-beta.zip
- код гри [знаходиться на Github](https://github.com/farbrausch/fr_public/tree/master/werkkzeug3_kkrieger)
- один з авторів розповів, з чого складається гра -- https://fgiesen.wordpress.com/2012/02/13/debris-opening-the-box

Але завдання ось у чому: створити свою "демку", програму котра демонструє віртуозне володіння програмерськими штучками та математикою процедурної генерації.