Skip to content

Python: funkcie, range, vlastná hra

Funkcie

Príkazy print, int, input, len, ..., ktoré sme používali doteraz, majú niečo spoločné. Pozrime sa na ne ako na stroje.

  1. Do stroja niečo vhodíme, spustíme ho,
  2. on niečo spraví,
  3. a nakoniec niečo vyhodí zo seba von.

Do príkazu int vhodíme hodnotu, napr. '10', prevedie ju na číslo a vrátí nám hodnotu 10. Takýmto príkazom hovoríme funkcie. Funkcie majú vstup a výstup, niečo berú ako argument na vstupe a vrátia hodnotu na výstupe (návratová hodnota). Niektoré funkcie nemusia mať argument, napr. input(), naopak print( 'a', 4, 10 ) má viac argumentov (jednotlivé argumenty oddeľujeme čiarkou), ale zase žiadnu návratovú hodnotu.

Poznámka

To, že print nemá návratovú hodnotu nie je tak úplne pravda:

>>> r = print( 'test' )
test
>>> r
>>> print( r )
None
>>> type( r )
<class 'NoneType'>

Návratová hodnota je None. Je to jediná možná hodnota dátového typu NoneType. Len pre zaujímavosť, pre nás to teraz vôbec nie je dôležité.

Dobrá správa je: Python umožňuje vytvoriť si vlastné funkcie! Slúži na to príkaz def, znovu na konci obsahuje dvojbodku a nasleduje blok kódu (telo funckie) odsadený vpravo, ktorý sa vykoná, ak túto funkciu zavoláme (to znamená, použijeme ju v programe).

def say_hi( who ):
    print( 'Hi', who )

say_hi( 'robot' )
say_hi( 'Adam' )

Prvý riadok sa nazýva hlavička funkcie. Určuje názov funkcie a parametry. Medzi parametrom a argumentom je veľmi slabý rozdiel a často sa ich význam zamieňa. Ak chceme byť presný, 'robot' a 'Adam' sú argumenty. who je parameter. Ako auto zaparkuje na parkovisko, tak aj argument zaparkuje na parameter, takže hodnota 'robot' (argument) sa uloží do premennej who (parameter). Ešte inak: ak sa pozeráme zvnútra funkcie, vidíme parametre. Ak sa pozeráme zvonka, vidíme argumenty.

Úloha 1

Napíšte funkciu print_max, ktorá vypíše maximálny prvok v zozname. Ak je zoznam prázdny, vypíše túto informáciu. Napr:

print_max( [ 3, 9, 0 ] ) vypíše: 9
print_max( [] ) vypíše: Max element does not exist (the list is empty)
print_max( 'dkrn' ) vypíše: r
Úloha 2

Pokúste sa v definícii funkcie "print_max" použiť funkciu max (ktorá je už vstavaná v Pythone). Krátka ukážka ako funguje:

>>> max( [ 1, 2, 0 ] )
2
>>> max( 'dkrn' )
'r'
>>> max( [] )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ValueError: max() arg is an empty sequence

Takže pozor na to, aby argumentom nebol prázdny zoznam, toto bude treba otestovať ešte pred tým, ako sa zavolá funkcia max.

Čo ak by sme chceli aj my niečo vrátiť z našej vlastnej funkcie, nielen vypisovať? Slúži na to slovíčko return:

def area( a, b ):
    return a * b

print( area( 2, 3 ) )
print( area( 0, 10 ) + area( 10, 0 ) )
print( area( 1, area( 2, 3 ) ) ) # well, this is "volume"

Prvý riadok (po definícii funkcie area) zavolá area( 2, 3 ). Do premennej a sa uloží hodnota 2, do b hodnota 3. Potom sa vyhodnotí a * b na hodnotu 12, ktorá sa vráti spať a vykoná sa print( 12 ). Podobne si vieme rozobrať aj posledný riadok:

  1. print( area( 1, area( 2, 3 ) ) )
  2. print( area( 1, 6 ) )
  3. print( 6 )
  4. Na obrazovke uvidíme text "6"
Úloha

Napíšte funkciu, ktorá vráti počet kladných čísel v zozname.

[] -> 0
[ 2, 3 ] -> 2
[ 0, 4, 9, -2, 10 ] -> 3
[ -2, 0, -5 ] -> 0

range

Ukážeme si jednu novú, užitočnú funkciu, hlavne v spojení s for ... in cyklom. Volá sa range a môže brať jeden, dva, alebo aj tri argumenty. Vráti špeciálnu hodnotu typu 'range', ktorú ale vieme konvertovať do zoznamu pomocou funkcie list:

>>> list( range( 10 ) )
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list( range( 0 ) )
[]
>>> list( range( 1 ) )
[0]
>>> list( range( 0, 10 ) )
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list( range( 0, 0 ) )
[]
>>> list( range( 3, 5 ) )
[3, 4]
>>> list( range( -3, 3 ) )
[-3, -2, -1, 0, 1, 2]
>>> list( range( -3, 3, 2 ) )
[-3, -1, 1]
>>> list( range( 0, 10, 1 ) )
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list( range( 0, 10, 3 ) )
[0, 3, 6, 9]
>>> list( range( 10, 0, -3 ) )
[10, 7, 4, 1]

range(a, b, c) vygeneruje čísla od a (vrátane) do b (vynímajúc) s rozdielmi c medzi každými dvomi číslami. Ak použijeme iba dva argumenty, c sa nastaví automaticky na jednotku. A ak iba jeden, a sa nastaví na nulu.

Ak ale použijeme túto funkciu s konštrukciou for ... in, môžme vynechať konvertovanie cez list, lebo vie pracovať aj s špeciálnou hodnotou typu 'range', akú vracia funkcia range.

>>> type( range( 5 ) )
<class 'range'>
>>> for i in range( 5 ):
...     print( i )
...
0
1
2
3
4

for spolu s range častokrát dokáže nahradiť cyklus while a lepšie sa s ním pracuje. Ak si spomenieme napr. na náš program kresliaci trojuholníky, teraz by sme samotné kreslenie (načítanie veľkosti na vstupe zostáva rovnako) vedeli napísať nasledovne:

for row_size in range( size, 0, -1 ):
    print( row_size * '*' )

A nakresliť opačný trojuholník (so špičkou na vrchu) by bolo podobne jednoduché:

for row_size in range( 1, size + 1 ):
    print( row_size * '*' )
Úloha

Napíšte program, ktorý sa spýta užívateľa na dve čísla. Tie budú znamenať veľkosť strán obdĺžnika. Potom pomocou for ... in range vykreslite takýto obdĺžnik. Pre veľkosti 4 a 6 by mal vyzerať takto:

######
#    #
#    #
######

Labyrint

Poďme si spolu naprogramovať jednoduchú hru. Už máme nejaké skusenosti s programovaním, takže pokým sa trochu obmedzíme v našich nárokoch na kvalitu, zvládneme to ľavou zadnou.

Cieľom hry bude dostať sa v labyrinte do cieľa. Labyrint budeme kresliť podobne ako v poslednej úlohe, mriežky (#) budú označovať steny, medzery ( ) voľné políčka. Cieľ označíme ako písmeno X a našu postavičku O. V každom kole vyzveme používateľa aby zadal jedno z písmenok:

  • w - krok hore
  • a - krok vľavo
  • s - krok dole
  • d - krok vpravo
  • q - ukončiť hru

Na začiatku si zadefinujeme všetky tieto konštanty, teda premenné, ktoré sa nebudú meniť. V Pythone je zvykom písať ich názvy veľkými písmenami.

FIELD = [
'#############',
'#  #  ### #X#',
'# #       # #',
'# ## ## # # #',
'#     # #   #',
'##### #  #  #',
'#   # #######',
'# #         #',
'#############' ]

HEIGHT = len( FIELD )
WIDTH = len( FIELD[ 0 ] )

END = 'X'
PLAYER = 'O'
WALL = '#'

UP = 'w'
LEFT = 'a'
DOWN = 's'
RIGHT = 'd'
QUIT = 'q'

Hracie pole si ukladáme ako zoznam textových reťazcov - riadkov. Výška poľa je počet riadkov, ktorý vieme zistiť funkciou len. Tiež ju použijeme na zistenie šírky, t.j. počet stĺpcov, ktorý sa rovná dĺžke každého (a teda napr. prvého) riadku. Na začiatku hráča umiestnime do ľavého dolného rohu. Pamätajme, že indexy začínajú nulou. X-ová súradnica je horizontálna (zľava doprava), y-ová je vertikálna (zhora dolu).

INITIAL_X = 1
INITIAL_Y = 7

Teraz si napíšeme funkciu, ktorá vykreslí hracie pole aj s hráčom. Funkcia bude mať dva parametre: aktuálne súradnice hráča. Budeme prechádzať pomocou vnoreného for cyklu celé hracie pole po riadkoch, ak na danom políčku stojí hráč, vykreslíme jeho znak, inak vykreslíme políčko z hracieho pola. Doteraz sme print používali tak, že na konci výstupu vždy vložil znak konca riadku (akoby stlačil na konci riadku Enter). To je jeho prednastavené správanie. Teraz ale nechceme aby bolo každé políčko na svojom vlastnom riadku, chceme ich vykresliť hneď vedľa seba. Preto použijeme špeciálny (optional) parameter end, kde mu nastavíme znak, ktorý má vykresliť na konci výstupu. Skúste si v shelli spustiť napr. print( 'afad', end='###' ). My naozaj nechceme vypísať medzi políčkami nič, preto nastavíme tento parameter na prázdny text.

def print_field( player_x, player_y ):
    for y in range( HEIGHT ):
        for x in range( WIDTH ):
            if y == player_y and x == player_x:
                print( PLAYER, end='' )
            else:
                print( FIELD[ y ][ x ], end='' )
        print()

Napíšeme si ešte jednu dôležitú funkciu. Bude načítavať vstup od užívateľa a posúvať hráča. Ako vstup zoberie tiež súradnice hráča a vráti 3 hodnoty: nové súradnice po posune a logickú hodnotu, či sa má hra ukončiť. Viac ako jednu hodnotu sme doteraz nevrátili v žiadnej funkcii, ale je to jednoduché:

def is_valid_command( command ):
    return command in 'wasdq'

def is_empty( x, y ):
    return not FIELD[ y ][ x ] == WALL

def move( player_x, player_y ):
    new_player_x = player_x
    new_player_y = player_y

    while True:
        command = input( 'Move "w", "a", "s", "d" or end "q": ' )
        if not is_valid_command( command ):
            print( 'Invalid command' )
        else:
            if command == 'w':
                new_player_y -= 1
            elif command == 'a':
                new_player_x -= 1
            elif command == 's':
                new_player_y += 1
            elif command == 'd':
                new_player_x += 1
            else: # quit
                return player_x, player_y, True

            if is_empty( new_player_x, new_player_y ):
                return new_player_x, new_player_y, False
            else:
                print( 'You cannot move there' )
                new_player_x = player_x
                new_player_y = player_y

Teraz implementujeme hlavnú logiku. Načítať viacero hodnôt z funkcie je rovnako jednoduché.

x = INITIAL_X
y = INITIAL_Y

end = False

while not end:
    print_field( x, y )
    x, y, end = move( x, y )
    if not end and FIELD[ y ][ x ] == END:
        print( 'Congratulation!' )
        end = True

print( 'The game ends' )

Tak, a hra je hotová :) Nezabudnite, že príkaz input vždy čaká na stlačenie klávesy Enter, takže sa hra nedá ovládať plynule ako sme zvyknutí, len držaním "w" "a" "s" "d". To by sme mohli ešte vylepšiť, no najbližšie si už postavíme svojho prvého robota a rozpohybujeme ho Pythonom! Zároveň si postupne budeme ukazovať všeliaké nové užitočné príkazy.

Robot

Úloha

Ak ešte máte chuť trochu popracovať na našej hre, tu je niekoľko návrhov:

  1. Vytvorte viacero rôznych bludísk, nechajte hráča, aby si z nich vybral.
  2. Pridajte počítadlo krokov. Čím viac krokov hráč spraví pokým sa dostane do cieľa, tým nižšie bude jeho skóre, ktoré na konci hra vypíše.
  3. Do bludiska pridajte diamanty (napr. znak bodky "."), tie bude hráč zbierať, takže po prejdení políčka s diamantom, diamant na ňom už nesmie zostať! Pozbierané diamanty hráčovi zvyšujú skóre.
  4. Do hry môžete pridať dvere "D" a kľúč "K". Keď hráč zoberie kľúč, dvere sa mu otvoria a bude môcť prejsť do cieľa.