Warning
This page is located in archive.

Cvičení 8: Fronta, zásobník, objekty

Dekódování zprávy

  • Dekódujde zprávu ze standardního vstupu pomocí zásobníku:
    • Písmeno znamená push znaku na zásobník
    • Hvězdička znamená pop znaku ze zásobníku na výstup
    • Mezery se ignorují
  • Dekódujte následující vstup

TE*A*QYS***SEU****NI*O**

Flood fill

  • Napište program, který v zadané matici nahradí souvislou oblast 0 ze zadaného bodu hodnotou 2.
  • Matici vezměte jako vnitřní proměnnou:

m=[
[0,0,1,0,0,1,0,0,0,0],
[0,0,1,0,0,1,0,0,0,0],
[0,0,1,1,0,1,0,0,0,1],
[0,0,1,0,0,0,1,0,1,0],
[0,0,1,0,0,0,0,1,0,0],
[0,0,1,1,0,1,0,0,0,0],
[0,0,1,0,1,1,1,1,0,0],
[0,0,1,0,0,1,0,1,1,1],
[0,0,1,0,0,1,0,0,0,0],
[0,0,1,0,0,1,0,0,0,0] ]

  • Program si načte počáteční bod ze standardního vstupu
  • Do zásobníku vloží vstupní bod
  • Opakuje, dokud není zásobník prázdný:
    • uloží si hodnotu (x, y) prvního prvku v zásobníku
    • odstraní první prvek ze zásobníku
    • pokud je hodnota matice v bodě (x, y) rovna 0, vloží do zásobníku:
      • souřadnice (x-1,y),(x+1,y),(x,y-1),(x,y+1), pokud jsou v mezích rozměrů matice
  • Vytiskněte výslednou matici
  • Program otestujte pro vstupy: 4,4 a 9,9
  • Co by se stalo, pokud byste na zásobník vložili i body (x-1,y-1),(x+1,y+1),(x+1,y-1),(x-1,y+1)?
  • Jaká je složitost tohoto algoritmu? Uměli byste tento algoritmus zefektivnit, aby nevkládal jedno pole matice do zásobníku vícekrát?

Fronta

  • Přeměňte řešení úlohy Flood fill změňte zásobník na frontu.
  • Postupnou animací zjistěte jak se změnilo vyplňování prostoru
  • Zjistěte, zda potřebuje více paměti zásobník, nebo fronta. Otestujte i na větším prostoru.

Objekt komlexní číslo

Třídy pro komplexní čísla:

  • Třída obsahuje dvě proměnné real a imag
  • Konstruktor (metoda _init_()) nastavuje tyto proměnné defaultně na 0
  • Pro přímý výpis funkcí print je vhodné definovat metodu _repr_, která vrací string
  • Pozor: init a repr má v názvu dvě podtržítka!!!

class Complex:
    def __init__(self, real=0, imag=0):
        self.real = real
        self.imag = imag
 
    def amplitude(self):
        return (self.real*self.real + self.imag*self.imag)**(1/2)
 
    def add(self, rhs):
        self.real += rhs.real
        self.imag += rhs.imag
 
    def sub(self, rhs):
        self.real -= rhs.real
        self.imag -= rhs.imag
 
    def __repr__(self):
        sign = "+";
        if (self.imag < 0):
            sign = "-";
        return str(self.real) + sign + str(abs(self.imag)) + "i"
 
    def mul(self, rhs):
        r = self.real*rhs.real - self.imag*rhs.imag;
        i = self.real*rhs.imag + self.imag*rhs.real;
        self.real = r
        self.imag = i 
 
a = Complex()
print("a=",a)
 
b = Complex(1,-1)
print("b=",b)
 
a.add(b)
print("a=",a)
 
a.mul(b)
print("a=",a)
 
print("|a|=",a.amplitude())
print("|b|=",b.amplitude())

Témata k procvičení

Inverzní permutace

  • Pokud pole o délce $N$, obsahuje všechna čísla od $0$ do $N-1$ právě jednou, pak toto pole kóduje permutaci tak, že první prvek se zobrazí na místo, kde se v poli nachází $0$, druhý prvek na místo, kde se v poli nachází $1$, atd.
  • Pole [0, 1, 2, 3] kóduje identickou, tzv. jednotkovou, permutaci o čtyřech prvcích,
  • Pole [3, 2, 1, 0] kóduje otočení prvků v poli.
  • Napište program, který načte z jednoho řádku standardního vstupu vektor reprezentující permutaci a najde a vytiskne inverzní permutaci, tj. permutaci, která převede zadanou permutaci na jednotkovou.
  • Inverzní permutace k permutaci [2, 0, 1], je permutace [1, 2, 0], neboť první permutace zobrazí 0→2 a druhá permutace 2→0, obdobně 1→0, druhá permutace 0→1; první 2→1 a druhá 1→2.

Domácí úkol

Lehká varianta

  • Tématem úlohy je napsat program tile_easy.py, který vyplní hexagonální hrací desku zadanými kameny

Příklad 1: vstupní kameny

Úkolem je vyplnit hrací desku o velikosti 4×4 (4-řádky, každý řádek 4 pole). Všechna řešení jsou:

  • Vstup programu: argumenty příkazové řádky
    • první argument: velikost hrací desky (jedno celé číslo) určující jak počet řádků tak i počet sloupců
    • druhý argument: jméno vstupního souboru s hracími kameny (je zaručeno, že existuje, že obsahuje alespoň jeden kamen a obsahuje správně zadané kameny)
    • třetí argument: jméno výstupního souboru
  • Program nalezne kombinaci kamenů na hrací desce tak, aby každé políčko bylo vyplněno právě jedním kamenem
  • V lehké variantě domácí úlohy je povolena pouze translace kamenů, tedy je zakázáno kameny otáčet
  • Je možné použít všechny kameny, ale není to nutné. Každý kamen se ale použije nejvýše jednou.
  • Pokud je nějaký kamen použit, musí se celý vejít do hrací desky, nesmí přečnívat přes hrací desku ani se nesmí překrývat s jiným kamenem
  • Program do výstupního souboru vypíše:
    • právě jedno řešení - libovolné ze všech existujících řešení
      • Výstup programu se zapisuje do výstupního souboru takto:
        • každý řádek obsahuje tři celá čísla ve tvaru 'p q i', kde (p,q) je celočíselná souřadnice na hrací desce a 'i' je index kamene, který tuto buňku obarvuje
        • kameny jsou číslovány od jedničky podle pořadí ve vstupním souboru (kamen definován na první řádce bude barvit na 1, kamen na druhá řádce obarvuje hodnotou 2 atd..)
    • NOSOLUTION, pokud žádné neexistuje
      • pokud řešení neexistuje, obsahuje soubor pouze jednu řádku s textem 'NOSOLUTION'
  • Program odevzdejte jako tile_easy.py do HW08
  • Timeout je nastaven dostatečně, pokud by Váš program měl problémy s časovým omezením, kontaktujte nás na fóru předmětu.
  • Jak bude hodnoceno:
    • Vyřešení hrací desky o velikosti 3×3, počet kamenů 2 až 4 (0.4b)
    • Vyřešení hrací desky o velikosti 4×4, počet kamenů 3 až 5 (0.4b)
    • Vyřešení hrací desky o velikosti 5×5, počet kamenů 2 až 6 (0.4b)
    • Správná detekce hrací desky bez řešení (0.3b)

Nápověda a pomocný program

  • Obtížnost této úlohy spočívá jednak v tom, jak najít vhodné poskládání kamenů, které zcela pokrývá hrací desku, a také v tom, jak pracovat s hexagonálním gridem
  • Pro usnadnění práce na domácí úloze si stáhněte program base.py (na konci této stránky), který vám nabízí užitečné nástroje

Ukázka programu

  • Volání programu

python3 tile_easy.py 3 stones.txt solution.txt

  • Stones.txt obsahuje:

2 0 
1 2 1 1 0 2 
0 0 1 0 0 1 -1 2 
2 1

  • Visualizace kamenů - pozor - při vykreslovani jsou kameny posunuty směrem do stredu hraci desky. Následující obrázky tedy nevykresují absolutní souřadnice ze vstupního souboru!

  • Jedno z řešeních je

0 0 3
0 1 3
0 2 1
1 0 3
1 1 2
1 2 4
2 0 2
2 1 2
-1 2 3

  • Toto řešení odpovídá levému obrázku níže.
  • Všechna řešení jsou:

Kameny

  • Kamen je tvořen jednou nebo více buňkami
  • Kameny jsou tedy zadány ve stejné notaci jako hexagonální grid
  • Na každém řádku vstupního souboru je zadán jeden kamen ve formátu 'p1 q1 p2 q2 … pn qn', pro 'n' buněk, kde p,q jsou celá čísla
  • V souboru mohou být kameny stejného tvaru (ale mohou být zadány různými souřadnicemi).
    • Kamen1: 0 0 1 0 2 0
    • Kamen2: 1 0 0 0 2 0
    • Kamen3: -1 1 0 1 1 1
    • Tyto kameny jsou tvarově stejné, ale pro vyplňování hrací desky je považujte za různé (každý bude obarvovat jinou barvou podle pořadí v souboru.
  • Pro načtení kamenů můžete využít funkci

stones = base.loadStones(filename)

  • Pro výpis jednotlivých kamenů můžete použít např.

for si in range(len(stones)):
   print("Stone ", si, " has ", len(stones[si]), " cells ") 
   stoneColor = si + 1    #toto je barva kamene, kterou zapisujeme do hraci desku
   for i in range(len(stones[si])):
      p,q = stones[si][i]
      print("Cell[",i,"] of stone ",si, " is ",p,q)

Takto bude vypadat typický program pro HW08:

import base
import sys
 
#nacteni vstupnich parametru z prikazove radky do promennych size, inputFile, outputFile
size = int(sys.argv[1])
board = base.Board(size)
stones = base.loadStones(sys.argv[2])
 
#program hledajici rozmísteni kamenu, ktery svuj vysledek zapisuje do board.board
 
#vytvoreni, ci otevreni vystupniho souboru do handleru f
f=open(sys.argv[3], "w")
if reseni_exist:
  for p in board.board:
   for q in board.board[p]:
      f.write("{} {} {}\n".format(p,q,board.board[p][q]))
else:
  f.write("NOSOLUTION")
f.close()

base.py

Následující program si uložte do stejného adresáře jako program pro tuto úlohu. Uložte jej pod názvem base.py

 
import copy
import math
#import random
from PIL import Image, ImageDraw
 
 
def loadStones(filename):
    f = open(filename,"r")
    stones = []
    for line in f:
        coords = list(map(int, line.rstrip().split()))
        if len(coords) > 0:
            stones.append( [] )
            for i in range(len(coords)//2):
                x = coords[2*i]
                y = coords[2*i+1]
                stones[-1].append([ x,y ] )
    return stones;
 
 
class Board:
    def __init__(self, size):
        self.size = size
        self.board = {}
 
        #create empty board as a dictionary
        self.b2 = {}
        for p in range(-self.size,self.size):
            for q in range(-self.size, self.size):
                if self.inBoard(p,q):
                    if not p in self.board:
                        self.board[p] = {}
                    self.board[p][q] = 0
 
                    if not q in self.b2:
                        self.b2[q] = {}
                    self.b2[q][p] = 0
 
        #this is for visualization and to synchronize colors between png/js
        self._colors = {}
        self._colors[-1] = "#fdca40" #sunglow
        self._colors[0] = "#ffffff" #white
        self._colors[1] = "#947bd3" #medium purple
        self._colors[2] = "#ff0000" #red
        self._colors[3] = "#00ff00" #green
        self._colors[4] = "#0000ff" #blue
        self._colors[5] = "#566246" #ebony
        self._colors[6] = "#a7c4c2" #opan
        self._colors[7] = "#ADACB5" #silver metalic
        self._colors[8] = "#8C705F" #liver chestnut
        self._colors[9] = "#FA7921" #pumpkin
        self._colors[10] = "#566E3D" #dark olive green
 
 
 
    def inBoard(self,p,q):
        """ return True if (p,q) is valid coordinate """
        return (q>= 0) and (q < self.size) and (p >= -(q//2)) and (p < (self.size - q//2))
 
 
    def rotateRight(self,p,q):
        pp = -q
        qq = p+q
        return pp,qq
 
    def rotateLeft(self, p,q):
        pp = p+q
        qq = -p
        return pp, qq
 
 
 
    def saveImage(self, filename):
        """ draw actual board to png. Empty cells are white, -1 = red, 1 = green, other values according to
            this list 
            -1 red, 0 = white, 1 = green 
        """
 
        cellRadius = 25
        cellWidth = int(cellRadius*(3**0.5))
        cellHeight = 2*cellRadius
 
        width = cellWidth*self.size + cellRadius*3
        height = cellHeight*self.size
 
        img = Image.new('RGB',(width,height),"white")
 
        draw = ImageDraw.Draw(img)
 
        lineColor = (50,50,50)
 
 
        for p in self.board:
            for q in self.board[p]:
                cx = cellRadius*(math.sqrt(3)*p + math.sqrt(3)/2*q) + cellRadius
                cy = cellRadius*(0*p + 3/2*q) + cellRadius
 
                pts = []
                for a in [30,90,150,210,270,330]:
                    nx = cx + cellRadius * math.cos(a*math.pi/180)
                    ny = cy + cellRadius * math.sin(a*math.pi/180)
                    pts.append(nx)
                    pts.append(ny)
                color = "#ff00ff" #pink is for values out of range -1,..10
                if self.board[p][q] in self._colors:
                    color = self._colors[self.board[p][q]]
 
                draw.polygon(pts,fill=color)
                pts.append(pts[0])
                pts.append(pts[1])
                draw.line(pts,fill="black", width=1)
                draw.text([cx-3,cy-3], "{} {}".format(p,q), fill="black", anchor="mm")
        img.save(filename)

Těžká varianta

  • Zadání je stejné jako pro lehkou úlohu, kromě této změny:
    • Hledejte kombinaci kamenů, aby úplně vyplnily hrací desku s tím, že je možné použít jak translace, tak i rotace kamenů
  • Je zakázáno kameny překlápět zrcadlově
  • V hexagonálním gridu můžeme rotovat kameny o 60 stupňů, existuje 6 rotací
  • Pro rotaci buňky (p,q) doleva o 60 stupňů okolo buňky (0,0) použijte funkci:

newp, newq = board.rotateLeft(p,q)

  • Pro rotaci buňky (p,q) doprava o 60 stupňů okolo buňky (0,0) použijte funkci:

newp, newq = board.rotateRight(p,q)

  • Opakovaným volání těchto funkcí dostanete další rotace
  • Odevzdejte jako tile_hard.py do HW08
  • Vstupy i výstupy jsou shodné s lehkou úlohou
Ukázka rotace

Základní pozice kamene

1. rotace

2. rotace

3. rotace

4. rotace

5. rotace

courses/b3b33alp/cviceni/t08.txt · Last modified: 2023/12/11 11:40 by stepan