Už umíš vykreslit hada ze seznamu souřadnic. Hadí videohra ale nebude jen „fotka“. Seznam se bude měnit a had se bude hýbat!
Tvůj program teď, doufám, vypadá nějak takhle:
import pyglet
TILE_SIZE = 64
snake = [(1, 2), (2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (4, 5)]
food = [(2, 0), (5, 1), (1, 4)]
red_image = pyglet.image.load('apple.png')
snake_tiles = {}
for start in ['bottom', 'end', 'left', 'right', 'top']:
for end in ['bottom', 'end', 'left', 'right', 'top', 'dead', 'tongue']:
key = start + '-' + end
image = pyglet.image.load('snake-tiles/' + key + '.png')
snake_tiles[key] = image
window = pyglet.window.Window()
@window.event
def on_draw():
window.clear()
pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
for x, y in snake:
before = 'end' # (Tady případně je nějaké
after = 'end' # složitější vybírání políčka)
snake_tiles[before + '-' + after].blit(
x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)
for x, y in food:
red_image.blit(
x * TILE_SIZE, y * TILE_SIZE, width=TILE_SIZE, height=TILE_SIZE)
pyglet.app.run()
Zkus těsně nad řádek pyglet.app.run
doplnit funkci,
která se bude volat každou šestinu vteřiny
a přidá hadovi políčko navíc:
def move(dt):
x, y = snake[-1]
new_x = x + 1
new_y = y
new_head = new_x, new_y
snake.append(new_head)
pyglet.clock.schedule_interval(move, 1/6)
Funguje?
Tak do téhle funkce ještě přidej del snake[0]
, aby had nerostl donekonečna.
Víš co tenhle příkaz dělá? Jestli ne, koukni znovu na poznámky k seznamům!
Zvládneš funkci upravit tak, aby had lezl nahoru?
Jestli ano, gratuluji! Zvývá směr hada ovládat šipkami na klávesnici, a většina hry bude hotová!
Než ale uděláme interaktivního hada, zkusíme trošku uklidit. Program se nám rozrůstá a za chvíli bude složité se v něm vyznat.
Stav hry máme zatím ve dvou seznamech: snake
a food
.
Časem ale bude podobných proměnných víc.
Abychom je měli všechny pohromadě, vytvoříme pro stav hry třídu.
Na všechno, co se ve hře může stát, nadefinujeme metody. Zatím budou dvě: začátek hry a pohyb hada.
Na začátku hry se zavolá metoda __init__
.
Má trochu divné jméno se dvěma podtržítkama na každé straně.
Podle toho Python ví, že tahle metoda je speciální a se má volat
při vytvoření objektu.
Metoda __init__
nastaví celý stav hry jako atributy.
Stav hry je všechno, co potřebujeme o hře vědět a může se to časem měnit.
V našem případě to zatím budou souřadnice hada a jídla.
Metoda move
, kterou budeme volat při každém „tahu“ hry, je bude tyhle
atributy měnit.
Pro funkčnost, kterou zatím náš had umí, bude třída se stavem vypadat následovně. Přidej ji do programu hned za nastavení konstant.
class State:
def __init__(self):
self.food = [(2, 0), (5, 1), (1, 4)]
self.snake = [(0, 0), (1, 0)]
def move(self):
old_x, old_y = self.snake[-1]
new_x = old_x + 1
new_y = old_y
new_head = new_x, new_y
self.snake.append(new_head)
del self.snake[0]
Použij prosím pro třídu jméno State
a i atributy pojmenuj podle
materiálů (snake
, food
, a později i další).
Bude se ti to hodit.
Všimni si, že metody berou argument self
.
To označuje konkrétní objekt, stav hry se kterým metoda pracuje nebo
který mění.
Ke všem atributům přistupují pomocí tečky –
self.jméno_atributu
.
Tak, máme třídu se stavem. No jo, ale jak ji teď použít?
Na to potřebuješ ještě několik změn:
Nastavování seznamů snake
a food
(mimo třídu) zruš; místo nich nastav
jedinou proměnnou state
na nový stav:
state = State()
Místo snake
a food
ve funkci on_draw
použij state.snake
a state.food
– atributy našeho stavu.
Všimni si že tady se nepoužívá self
, což je jméno které používají jen
metody v rámci třídy.
Jinde musíš pojmenovat konkrétní objekt, se kterým pracujeme.
Funkci move
přepiš tak, aby jen volala metodu state.move
:
def move(dt):
state.move()
Všimni si že ani tady se nepoužívá self
.
Vykreslování hada z funkce on_draw
přesuň do nové metody
draw
:
class State:
...
def draw(self):
for x, y in state.snake:
...
for x, y in state.food:
...
...
@window.event
def on_draw():
window.clear()
pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
state.draw()
Všimni si že ani tady se nepoužívá self
.
Povedlo se? Funguje to jako předtím? Pro kontrolu můžeš svůj program porovnat s mým (ale nejde o jediné správné řešení):
Nyní k onomu slíbenému ovládání. Respektive nejdřív k změnám směru.
Had ze hry se plazí stále stejným směrem, dokud hráč nezmáckne klávesu. Had z naší ukázky se plazí doprava. Jestli jsi to ještě neudělala, zkus zařídit, aby se místo toho plazil nahoru.
A co dolů?
Aby si had „pamatoval“ kam se zrovna plazí, je potřeba mít směr jako součást
stavu hry.
Uložme ho tedy do atrubutu jménem snake_direction
.
Co tam ale přesně uložit? Jak reprezentovat směr v Pythonu – pomocí čísel, n-tic a tak?
Asi nejpříhodnější řešení je uložit si o kolik políček se had má posunout, a to zvlášť v x-ovém a zvlášť v y-ovém směru. Čili jako dvojici:
(1, 0)
= doprava (o jedno políčko v kladném x-ovém směru;
v y-ovém neposouvat)(-1, 0)
= doleva (o jedno políčko v záporném x-ovém směru)(0, 1)
= nahoru (+y)(0, -1)
= dolů (-y)Nový atribut přidej do metody __init__
ve stavu:
self.snake_direction = 0, 1
A v metodě move
změň nastavování new_x
a new_y
podle nového atributu:
dir_x, dir_y = self.snake_direction
new_x = old_x + dir_x
new_y = old_y + dir_y
Směr hada teď můžeš měnit změnou snake_direction
v __init__
.
Funguje to? (Jestli ne, oprav to – a jestli to nejde, zavolej někoho na pomoc!)
Nyní zbývá atribut snake_direction
měnit, když uživatel něco stiskne na
klávesnici.
To už je doména Pygletu.
Je potřeba přidat funkci, která reaguje na stisk klávesy.
Aby Pyglet tuhle funkci našel a uměl zavolat, musí se jmenovat on_key_press
,
musí mít dekorátor @window.event
, a musí brát dva parametry:
číslo klávesy, která byla zmáčknutá a informace o modifikátorech
jako Shift nebo Ctrl:
@window.event
def on_key_press(key_code, modifier):
...
Druhý parametr nebude v naší hře potřeba, ale v hlavičce funkce musí být.
Podle prvního ale nastav aktuální směr hada.
Čísla kláves jsou definována v modulu pyglet.window.key
jako konstanty se jmény LEFT
, ENTER
, Q
či AMPERSAND
.
My použijeme šipky – LEFT
, RIGHT
, UP
a DOWN
:
@window.event
def on_key_press(key_code, modifier):
if key_code == pyglet.window.key.LEFT:
state.snake_direction = -1, 0
if key_code == pyglet.window.key.RIGHT:
state.snake_direction = 1, 0
if key_code == pyglet.window.key.DOWN:
state.snake_direction = 0, -1
if key_code == pyglet.window.key.UP:
state.snake_direction = 0, 1
Tuhle funkci je potřeba dát někam za nastavení window
(aby byl k dispozici
window.event
) a před pyglet.app.run()
(protože nastavovat ovládání až
potom, co hra proběhne, je zbytečné).
Nejlepší je ji dát vedle jiné funkce s dekorátorem @window.event
,
aby byly pěkně pohromadě.
Funguje to? Můžeš ovládat směr hada? To je skvělé! Určitě ale při zkoušení narazíš na pár věcí, které je potřeba dodělat:
Pojďme je vyřešit, jednu po druhé.
„Hadí“ hry jako ta naše mají dvě varianty: buď je kolem hřiště „zeď“ a hráč při nárazu do okraje prohraje, nebo je hřiště „nekonečné“ – had okrajem proleze a objeví se na druhé straně. My naprogramujeme tu první variantu – zeď.
Abys zjistila, jestli had „vylezl“ z levého okraje okna ven,
je potřeba zkontrolovat, jestli x-ová souřadnice hlavy
je menší než 0.
To je dobré udělat hned poté, co nové souřadnice hlavy získáš – konkrétně
hned za řádkem new_head = new_x, new_y
v metodě move
.
A co při takovém nárazu udělat?
Pro začátek bude nejjednodušší ukončit hru.
Na to má Python funkci exit()
, která funguje podobně jako když v programu
nastane chyba.
Jen místo chybového výpisu ukáže daný text.
Ukončení programu není příliš příjemný způsob, jak říct hráčovi že prohrál. Za chvíli ale tuhle část předěláme, tak prozatím tenhle jednoduchý způsob postačí.
def move(self):
old_x, old_y = self.snake[-1]
dir_x, dir_y = self.snake_direction
new_x = old_x + dir_x
new_y = old_y + dir_y
new_head = new_x, new_y
# Nový kód – kontrola vylezení z hrací plochy
if new_x < 0:
exit('GAME OVER')
self.snake.append(new_head)
del self.snake[0]
Věřím, že zvládneš udělat stejnou kontrolu pro vylezení ze spodního okraje.
Jak ale ošetřit ty zbylé okraje – pravý a horní? Na to je potřeba znát velikost okýnka. A tu zná Pyglet; třída se stavem by k okýnku neměla mít přístup!
Na velikosti herní plochy závisí chování hry.
Tahle informace tedy bude tedy muset být součást stavu.
Pro začátek nějakou velikost – třeba 10×10 – nastav v __init__
:
self.width = 10
self.height = 10
A pak zařiď, aby po nárazu na neviditelnou stěnu kolem hřiště velkého 10×10 políček hra skončila. Pořádně vyzkoušej všechny varianty – severní, jižní, východní i západní zeď. Had je virtuální, nemusíš se bát že mu z toho vyroste boule.
A pak v souboru se hrou hned po tom co vytvoříš stav (state = State()
)
a okýnko (window
) nastav opravdovou velikost.
Použij celočíselné dělení, aby počet políček byl v celých číslech:
state.width = window.width // TILE_SIZE
state.height = window.height // TILE_SIZE
Tak. Had je v kleci, už nemůže vylézt. Co dál?
Teď se musíš o hada postarat: pravidelně ho krmit. Ale ještě předtím je potřeba ho naučit, jak se vůbec jí – na naši potravu ještě není zvyklý. Když to zvládneš, poroste jako z vody!
Konkrétně musíš hlavně zajistit aby, když se had připlazí na políčko s jídlem, jídlo zmizelo. K tomu se dá použít:
in
, který zjišťuje jestli něco (třeba
souřadnice) je v nějakém seznamu (třeba seznamu souřadnic jídla), aremove
, která ze seznamu odstraní daný prvek (podle hodnoty prvku
– na rozdíl od del
, který maže podle pozice).Za kontrolu vylezení z hrací plochy potřebuješ dát kód, který dělá následující:
Zvládneš ho napsat?
Vyzkoušej, jestli to funguje. Had by měl jíst jídlo.
Ještě ale zbývá zařídit, aby po každém soustu trochu povyrostl. Ale jak? Kterým směrem má růst?
Tady je dobré se podívat na existující kód a uvědomit si, co dělá.
Náš had se plazí tak, že napřed vepředu povyroste (pomocí append
)
a potom se vzadu zmenší (pomocí del self.snake[0]
).
Aby tedy po snězení jídla vyrostl, stačí přeskočit ono zmenšování!
Ono přeskočit znamená podmínit, pomocí if
.
Logika jezení a zmenšování hada tedy bude:
Neboli přeloženo do Pythonu:
if new_head in self.food:
self.food.remove(new_head)
else:
del self.snake[0]
Pro ty, co se začínají ztrácet, dám k dispozici celou metodu move
.
Běda ale těm, kdo opisují kód aniž by se mu snažili porozumět!
Když už had umí jíst, je potřeba mu zajistit pravidelný přísun jídla. Nejlépe tak, že se každé snězené jídlo nahradí novým.
Přidej do třídy State
následující novou metodu, která umí přidat jídlo:
def add_food(self):
x = 0
y = 0
position = x, y
self.food.append(position)
Pak tuhle metodu zavolej – najdi v programu kód, který se provádí když je potřeba přidat nové jídlo, a přidej tam následující řádek:
self.add_food()
Tahle metoda přidává jídlo na pozici (0, 0), tedy stále do stejného rohu.
Bylo by ale fajn, kdyby se nové jídlo objevilo vždycky jinde,
na náhodném místě.
Na to můžeme použít funkci random.randrange
.
Vzpomeň si, že volání randrage(N)
vrátí náhodné celé číslo od
0 do N - 1.
Jaký rozsah čísel potřebujeme pro hadí jídlo?
Až na to přijdeš, zkus přidat náhodu do programu: jídlo by se mělo objevit na úplně náhjodném políčku na herní ploše.
Nezapomeň na import random
– to patří na úplný začátek souboru.
Další změny ale už dělej jen v metodě add_food
.
Až to budeš testovat, asi zjistíš, že úplně náhodné políčko není ideální. Občas se totiž jídlo objeví na políčku s hadem, nebo dokonce na jiném jídle. Je proto dobré tuhle situaci zkontrolovat, a když volba padne na plné políčko, jídlo nepřidávat:
if (position not in self.snake) and (position not in self.food):
self.food.append(position)
Když ale zkusíš tohle, zjistíš, že občas se nové jídlo vůbec nepřidá. To taky není vhodná varianta – had by tak měl hlad. Co s tím?
Překvapivě dobré (i když ne úplně ideální) řešení je zkusit políčko vybrat několikrát. Když padne prázdné políčko, šup tam s jídlem; když padne plné, tak to prostě zkusit znovu.
Je ale potřeba počet pokusů omezit, aby v situaci, kdy je pole úplně plné, počítač nevybíral donekonečna. Řekněme že když se na 100 pokusů nepodaří prázdné políčko vybrat, vzdáme to. Jídla už je nejspíš dost.
Metoda add_food
po všech úpravách bude vypadat takhle:
def add_food(self):
for try_number in range(100):
x = random.randrange(self.width)
y = random.randrange(self.height)
position = x, y
if (position not in self.snake) and (position not in self.food):
self.food.append(position)
# Ukončení funkce ("vyskočí" i z cyklu for)
return
Jestli ti to funguje, ještě zařiď, aby na začátku hry bylo jídlo na náhodných pozicích.
Had teď může narůst do obrovských rozměrů – a lze prohrát jen tím, že narazí do stěny. Zaříď teď, aby hra skončila i když narazí sám do sebe.
Jak na to?
Do metody move
, vedle kontrola vylezení z hrací plochy,
dej kód který udělá následující:
Dokážeš to převést do Pythonu?
Hotovo!
Není ale dobré při konci hry ukončit celý program a zavřít okýnko.
Lepší je hru „zapauzovat“ a ukázat hráči situaci, do které nešťastného hada dostal, aby se z ní mohl pro příště poučit.
Aby to bylo možné, dáme do stavu hry další atribut: alive
.
Ten bude nastavený na True
, dokud bude had žít.
Když had narazí, nastaví se na False
, a od té doby se už nebude pohybovat.
Je dobré i graficky ukázat, že hadovi není dobře – hráč pak spíš bude
zpytovat svědomí.
Zkus zapřemýšlet, kam v kódu patří následující kousky kódu, které prohru implementují:
# Prvotní nastavení atributu
self.alive = True
# Zastavení hada
self.alive = False
# Zabránění pohybu
if not self.alive:
return
# Grafická indikace
if after == 'end' and not state.alive:
after = 'dead'
Gratuluji, máš funkční a hratelnou hru! Doufám že jsi na sebe hrdá!
Dej si něco sladkého, zasloužíš si to.
Tady je moje řešení. To se touhle dobou od toho tvého může dost lišit – to je úplně normální. (A nedívej se sem dokud hada nenaprogramuješ sama, Chybami a neustálým zkoušením se člověk učí – a programátor zvlášť. Čtením už vyřešeného se učí hůř.)
Najdeš ještě nějaké další vylepšení, které by se dalo udělat?
Zkus třeba následující rozšíření. Jsou seřazené zhruba podle složitosti:
Vylepši ovládání (a hratelnost!) podle návodu.
Každých 30 vteřin hry přibude samo od sebe nové jídlo, takže jich pak bude na hrací ploše víc.
Hra se bude postupně zrychlovat.
(Na to je nejlepší předělat funkci move
, aby sama
naplánovala, kdy se má příště zavolat. Volání schedule_interval
tak už
nebude potřeba.)
Když had vyleze ven z okýnka, místo konce hry se objeví na druhé straně. (Viz návod.)
Hadi budou dva; druhý se ovládá klávesami
W A S D.
(Na to je nejlepší udělat novou třídu, Snake
, a všechen stav hada
přesunout ze State
do ní. Ve State
pak měj seznam hadů.
Téhle změně je potřeba přizpůsobit celý zytek programu.)
{ "data": { "sessionMaterial": { "id": "session-material:2019/brno-podzim-snake:workshop:3", "title": "Logika hry Had", "html": "\n \n \n\n <h1>Logika hry</h1>\n<p>Už umíš vykreslit hada ze seznamu souřadnic.\nHadí videohra ale nebude jen „fotka“.\nSeznam se bude měnit a had se bude hýbat!</p>\n<!--\n# Ukládání revizí\n\nXXX - Nestíhám dopsat, omlouvám se\n-->\n\n<h1>Rozhýbejme hada</h1>\n<p>Tvůj program teď, doufám, vypadá nějak takhle:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n\n<span class=\"n\">TILE_SIZE</span> <span class=\"o\">=</span> <span class=\"mi\">64</span>\n\n<span class=\"n\">snake</span> <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">5</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">4</span><span class=\"p\">,</span> <span class=\"mi\">5</span><span class=\"p\">)]</span>\n<span class=\"n\">food</span> <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">5</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">)]</span>\n\n<span class=\"n\">red_image</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">'apple.png'</span><span class=\"p\">)</span>\n<span class=\"n\">snake_tiles</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n<span class=\"k\">for</span> <span class=\"n\">start</span> <span class=\"ow\">in</span> <span class=\"p\">[</span><span class=\"s1\">'bottom'</span><span class=\"p\">,</span> <span class=\"s1\">'end'</span><span class=\"p\">,</span> <span class=\"s1\">'left'</span><span class=\"p\">,</span> <span class=\"s1\">'right'</span><span class=\"p\">,</span> <span class=\"s1\">'top'</span><span class=\"p\">]:</span>\n <span class=\"k\">for</span> <span class=\"n\">end</span> <span class=\"ow\">in</span> <span class=\"p\">[</span><span class=\"s1\">'bottom'</span><span class=\"p\">,</span> <span class=\"s1\">'end'</span><span class=\"p\">,</span> <span class=\"s1\">'left'</span><span class=\"p\">,</span> <span class=\"s1\">'right'</span><span class=\"p\">,</span> <span class=\"s1\">'top'</span><span class=\"p\">,</span> <span class=\"s1\">'dead'</span><span class=\"p\">,</span> <span class=\"s1\">'tongue'</span><span class=\"p\">]:</span>\n <span class=\"n\">key</span> <span class=\"o\">=</span> <span class=\"n\">start</span> <span class=\"o\">+</span> <span class=\"s1\">'-'</span> <span class=\"o\">+</span> <span class=\"n\">end</span>\n <span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">'snake-tiles/'</span> <span class=\"o\">+</span> <span class=\"n\">key</span> <span class=\"o\">+</span> <span class=\"s1\">'.png'</span><span class=\"p\">)</span>\n <span class=\"n\">snake_tiles</span><span class=\"p\">[</span><span class=\"n\">key</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">image</span>\n\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n\n<span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_draw</span><span class=\"p\">():</span>\n <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">clear</span><span class=\"p\">()</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glEnable</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_BLEND</span><span class=\"p\">)</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glBlendFunc</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_SRC_ALPHA</span><span class=\"p\">,</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_ONE_MINUS_SRC_ALPHA</span><span class=\"p\">)</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">snake</span><span class=\"p\">:</span>\n <span class=\"n\">before</span> <span class=\"o\">=</span> <span class=\"s1\">'end'</span> <span class=\"c1\"># (Tady případně je nějaké</span>\n <span class=\"n\">after</span> <span class=\"o\">=</span> <span class=\"s1\">'end'</span> <span class=\"c1\"># složitější vybírání políčka)</span>\n <span class=\"n\">snake_tiles</span><span class=\"p\">[</span><span class=\"n\">before</span> <span class=\"o\">+</span> <span class=\"s1\">'-'</span> <span class=\"o\">+</span> <span class=\"n\">after</span><span class=\"p\">]</span><span class=\"o\">.</span><span class=\"n\">blit</span><span class=\"p\">(</span>\n <span class=\"n\">x</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">width</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">height</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">)</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"n\">red_image</span><span class=\"o\">.</span><span class=\"n\">blit</span><span class=\"p\">(</span>\n <span class=\"n\">x</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">width</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">height</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n</pre></div><p>Zkus těsně nad řádek <code>pyglet.app.run</code> doplnit funkci,\nkterá se bude volat každou šestinu vteřiny\na přidá hadovi políčko navíc:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"n\">dt</span><span class=\"p\">):</span>\n <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">x</span> <span class=\"o\">+</span> <span class=\"mi\">1</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">y</span>\n <span class=\"n\">new_head</span> <span class=\"o\">=</span> <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span>\n <span class=\"n\">snake</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_interval</span><span class=\"p\">(</span><span class=\"n\">move</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"o\">/</span><span class=\"mi\">6</span><span class=\"p\">)</span>\n</pre></div><p>Funguje?\nTak do téhle funkce ještě přidej <code>del snake[0]</code>, aby had nerostl donekonečna.\nVíš co tenhle příkaz dělá? Jestli ne, koukni znovu na poznámky k seznamům!</p>\n<p>Zvládneš funkci upravit tak, aby had lezl nahoru?</p>\n<p>Jestli ano, gratuluji!\nZvývá směr hada ovládat šipkami na klávesnici, a většina hry bude hotová!</p>\n<h2>Třída pro stav</h2>\n<p>Než ale uděláme interaktivního hada, zkusíme trošku uklidit.\nProgram se nám rozrůstá a za chvíli bude složité se v něm vyznat.</p>\n<p>Stav hry máme zatím ve dvou seznamech: <code>snake</code> a <code>food</code>.\nČasem ale bude podobných proměnných víc.</p>\n<p>Abychom je měli všechny pohromadě, vytvoříme pro stav hry <em>třídu</em>.</p>\n<p>Na všechno, co se ve hře může stát, nadefinujeme <em>metody</em>.\nZatím budou dvě: začátek hry a pohyb hada.</p>\n<p>Na začátku hry se zavolá metoda <code>__init__</code>.\nMá trochu divné jméno se dvěma podtržítkama na každé straně.\nPodle toho Python ví, že tahle metoda je speciální a se má volat\npři vytvoření objektu.</p>\n<p>Metoda <code>__init__</code> nastaví celý stav hry jako <em>atributy</em>.\nStav hry je všechno, co potřebujeme o hře vědět a může se to časem měnit.\nV našem případě to zatím budou souřadnice hada a jídla.</p>\n<p>Metoda <code>move</code>, kterou budeme volat při každém „tahu“ hry, je bude tyhle\natributy měnit.</p>\n<p>Pro funkčnost, kterou zatím náš had umí, bude třída se stavem vypadat\nnásledovně.\nPřidej ji do programu hned za nastavení konstant.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">State</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span> <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">5</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">)]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span> <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">)]</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span> <span class=\"o\">+</span> <span class=\"mi\">1</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span>\n <span class=\"n\">new_head</span> <span class=\"o\">=</span> <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n</pre></div><div class=\"admonition note\"><p>Použij prosím pro třídu jméno <code>State</code> a i atributy pojmenuj podle\nmateriálů (<code>snake</code>, <code>food</code>, a později i další).\nBude se ti to hodit.</p>\n</div><p>Všimni si, že metody berou argument <code>self</code>.\nTo označuje konkrétní objekt, stav hry se kterým metoda pracuje nebo\nkterý mění.\nKe všem atributům přistupují pomocí tečky –\n<code>self.<var>jméno_atributu</var></code>.</p>\n<p>Tak, máme třídu se stavem.\nNo jo, ale jak ji teď použít?</p>\n<p>Na to potřebuješ ještě několik změn:</p>\n<ul>\n<li><p>Nastavování seznamů <code>snake</code> a <code>food</code> (mimo třídu) zruš; místo nich nastav\njedinou proměnnou <code>state</code> na nový stav:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">state</span> <span class=\"o\">=</span> <span class=\"n\">State</span><span class=\"p\">()</span>\n</pre></div></li>\n<li><p>Místo <code>snake</code> a <code>food</code> ve funkci <code>on_draw</code> použij <code>state.snake</code>\na <code>state.food</code> – atributy našeho stavu.</p>\n<p>Všimni si že tady se nepoužívá <code>self</code>, což je jméno které používají jen\n<em>metody</em> v rámci třídy.\nJinde musíš pojmenovat konkrétní objekt, se kterým pracujeme.</p>\n</li>\n<li><p>Funkci <code>move</code> přepiš tak, aby jen volala metodu <code>state.move</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"n\">dt</span><span class=\"p\">):</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">move</span><span class=\"p\">()</span>\n</pre></div><p>Všimni si že ani tady se nepoužívá <code>self</code>.</p>\n</li>\n<li><p>Vykreslování hada z funkce <code>on_draw</code> přesuň do nové metody\n<code>draw</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">State</span><span class=\"p\">:</span>\n <span class=\"o\">...</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">draw</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">:</span>\n <span class=\"o\">...</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"o\">...</span>\n<span class=\"o\">...</span>\n\n<span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_draw</span><span class=\"p\">():</span>\n <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">clear</span><span class=\"p\">()</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glEnable</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_BLEND</span><span class=\"p\">)</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glBlendFunc</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_SRC_ALPHA</span><span class=\"p\">,</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_ONE_MINUS_SRC_ALPHA</span><span class=\"p\">)</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">draw</span><span class=\"p\">()</span>\n</pre></div><p>Všimni si že ani tady se nepoužívá <code>self</code>.</p>\n</li>\n</ul>\n<p>Povedlo se? Funguje to jako předtím?\nPro kontrolu můžeš svůj program porovnat s mým (ale nejde o jediné správné\nřešení):</p>\n<div class=\"solution\" id=\"solution-0\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/0/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n\n<span class=\"n\">TILE_SIZE</span> <span class=\"o\">=</span> <span class=\"mi\">64</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">State</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span> <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">5</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">4</span><span class=\"p\">)]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span> <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">)]</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span> <span class=\"o\">+</span> <span class=\"mi\">1</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span>\n <span class=\"n\">new_head</span> <span class=\"o\">=</span> <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">draw</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">:</span>\n <span class=\"n\">before</span> <span class=\"o\">=</span> <span class=\"s1\">'end'</span> <span class=\"c1\"># (Tady případně je nějaké</span>\n <span class=\"n\">after</span> <span class=\"o\">=</span> <span class=\"s1\">'end'</span> <span class=\"c1\"># složitější vybírání políčka)</span>\n <span class=\"n\">snake_tiles</span><span class=\"p\">[</span><span class=\"n\">before</span> <span class=\"o\">+</span> <span class=\"s1\">'-'</span> <span class=\"o\">+</span> <span class=\"n\">after</span><span class=\"p\">]</span><span class=\"o\">.</span><span class=\"n\">blit</span><span class=\"p\">(</span>\n <span class=\"n\">x</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">width</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">height</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">)</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"n\">red_image</span><span class=\"o\">.</span><span class=\"n\">blit</span><span class=\"p\">(</span>\n <span class=\"n\">x</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">width</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">height</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">)</span>\n\n<span class=\"n\">red_image</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">'apple.png'</span><span class=\"p\">)</span>\n<span class=\"n\">snake_tiles</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n<span class=\"k\">for</span> <span class=\"n\">start</span> <span class=\"ow\">in</span> <span class=\"p\">[</span><span class=\"s1\">'bottom'</span><span class=\"p\">,</span> <span class=\"s1\">'end'</span><span class=\"p\">,</span> <span class=\"s1\">'left'</span><span class=\"p\">,</span> <span class=\"s1\">'right'</span><span class=\"p\">,</span> <span class=\"s1\">'top'</span><span class=\"p\">]:</span>\n <span class=\"k\">for</span> <span class=\"n\">end</span> <span class=\"ow\">in</span> <span class=\"p\">[</span><span class=\"s1\">'bottom'</span><span class=\"p\">,</span> <span class=\"s1\">'end'</span><span class=\"p\">,</span> <span class=\"s1\">'left'</span><span class=\"p\">,</span> <span class=\"s1\">'right'</span><span class=\"p\">,</span> <span class=\"s1\">'top'</span><span class=\"p\">,</span> <span class=\"s1\">'dead'</span><span class=\"p\">,</span> <span class=\"s1\">'tongue'</span><span class=\"p\">]:</span>\n <span class=\"n\">key</span> <span class=\"o\">=</span> <span class=\"n\">start</span> <span class=\"o\">+</span> <span class=\"s1\">'-'</span> <span class=\"o\">+</span> <span class=\"n\">end</span>\n <span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">'snake-tiles/'</span> <span class=\"o\">+</span> <span class=\"n\">key</span> <span class=\"o\">+</span> <span class=\"s1\">'.png'</span><span class=\"p\">)</span>\n <span class=\"n\">snake_tiles</span><span class=\"p\">[</span><span class=\"n\">key</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">image</span>\n\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n\n<span class=\"n\">state</span> <span class=\"o\">=</span> <span class=\"n\">State</span><span class=\"p\">()</span>\n\n<span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_draw</span><span class=\"p\">():</span>\n <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">clear</span><span class=\"p\">()</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glEnable</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_BLEND</span><span class=\"p\">)</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glBlendFunc</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_SRC_ALPHA</span><span class=\"p\">,</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_ONE_MINUS_SRC_ALPHA</span><span class=\"p\">)</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">draw</span><span class=\"p\">()</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"n\">dt</span><span class=\"p\">):</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">move</span><span class=\"p\">()</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_interval</span><span class=\"p\">(</span><span class=\"n\">move</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"o\">/</span><span class=\"mi\">6</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n</pre></div>\n </div>\n</div><h2>Ovládání</h2>\n<p>Nyní k onomu slíbenému ovládání. Respektive nejdřív k změnám směru.</p>\n<p>Had ze hry se plazí stále stejným směrem, dokud hráč nezmáckne klávesu.\nHad z naší ukázky se plazí doprava.\nJestli jsi to ještě neudělala, zkus zařídit, aby se místo toho\nplazil nahoru.</p>\n<div class=\"solution\" id=\"solution-1\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/1/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <p>Ve funkci <code>move</code> je potřeba jinak nastavit proměnné <code>new_x</code> a <code>new_y</code>:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span> <span class=\"o\">+</span> <span class=\"mi\">1</span>\n</pre></div>\n </div>\n</div><p>A co dolů?</p>\n<div class=\"solution\" id=\"solution-2\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/2/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span> <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span> <span class=\"o\">-</span> <span class=\"mi\">1</span>\n</pre></div>\n </div>\n</div><p>Aby si had „pamatoval“ kam se zrovna plazí, je potřeba mít směr jako součást\nstavu hry.\nUložme ho tedy do atrubutu jménem <code>snake_direction</code>.</p>\n<p>Co tam ale přesně uložit?\nJak reprezentovat směr v Pythonu – pomocí čísel, <var>n</var>-tic a tak?</p>\n<p><span class=\"figure\"><a href=\"/2019/brno-podzim-snake/snake/logic/static/coord-vectors.svg\"><img src=\"/2019/brno-podzim-snake/snake/logic/static/coord-vectors.svg\" alt=\"Mřížka s X a Y souřadnicemi\"></a></span></p>\n<p>Asi nejpříhodnější řešení je uložit si o kolik políček se had má posunout,\na to zvlášť v <var>x</var>-ovém a zvlášť v <var>y</var>-ovém směru.\nČili jako dvojici:</p>\n<ul>\n<li><code>(1, 0)</code> = doprava (o jedno políčko v kladném <var>x</var>-ovém směru;\n v <var>y</var>-ovém neposouvat)</li>\n<li><code>(-1, 0)</code> = doleva (o jedno políčko v záporném <var>x</var>-ovém směru)</li>\n<li><code>(0, 1)</code> = nahoru (+<var>y</var>)</li>\n<li><code>(0, -1)</code> = dolů (-<var>y</var>)</li>\n</ul>\n<p>Nový atribut přidej do metody <code>__init__</code> ve stavu:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">1</span>\n</pre></div><p>A v metodě <code>move</code> změň nastavování <code>new_x</code> a <code>new_y</code> podle nového atributu:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"n\">dir_x</span><span class=\"p\">,</span> <span class=\"n\">dir_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span> <span class=\"o\">+</span> <span class=\"n\">dir_x</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span> <span class=\"o\">+</span> <span class=\"n\">dir_y</span>\n</pre></div><p>Směr hada teď můžeš měnit změnou <code>snake_direction</code> v <code>__init__</code>.\nFunguje to? (Jestli ne, oprav to – a jestli to nejde, zavolej někoho na pomoc!)</p>\n<p>Nyní zbývá atribut <code>snake_direction</code> měnit, když uživatel něco stiskne na\nklávesnici.\nTo už je doména Pygletu.</p>\n<p>Je potřeba přidat funkci, která reaguje na stisk klávesy.\nAby Pyglet tuhle funkci našel a uměl zavolat, musí se jmenovat <code>on_key_press</code>,\nmusí mít dekorátor <code>@window.event</code>, a musí brát dva parametry:\nčíslo klávesy, která byla zmáčknutá a informace o modifikátorech\njako <kbd>Shift</kbd> nebo <kbd>Ctrl</kbd>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_key_press</span><span class=\"p\">(</span><span class=\"n\">key_code</span><span class=\"p\">,</span> <span class=\"n\">modifier</span><span class=\"p\">):</span>\n <span class=\"o\">...</span>\n</pre></div><p>Druhý parametr nebude v naší hře potřeba, ale v hlavičce funkce musí být.</p>\n<p>Podle prvního ale nastav aktuální směr hada.\nČísla kláves jsou definována v modulu <a href=\"https://pyglet.readthedocs.io/en/pyglet-1.3-maintenance/modules/window_key.html#key-constants\"><code>pyglet.window.key</code></a>\njako konstanty se jmény <code>LEFT</code>, <code>ENTER</code>, <code>Q</code> či <code>AMPERSAND</code> .\nMy použijeme šipky – <code>LEFT</code>, <code>RIGHT</code>, <code>UP</code> a <code>DOWN</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_key_press</span><span class=\"p\">(</span><span class=\"n\">key_code</span><span class=\"p\">,</span> <span class=\"n\">modifier</span><span class=\"p\">):</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">LEFT</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">RIGHT</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">DOWN</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"o\">-</span><span class=\"mi\">1</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">UP</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">1</span>\n</pre></div><p>Tuhle funkci je potřeba dát někam za nastavení <code>window</code> (aby byl k dispozici\n<code>window.event</code>) a před <code>pyglet.app.run()</code> (protože nastavovat ovládání až\npotom, co hra proběhne, je zbytečné).\nNejlepší je ji dát vedle jiné funkce s dekorátorem <code>@window.event</code>,\naby byly pěkně pohromadě.</p>\n<p>Funguje to?\nMůžeš ovládat směr hada?\nTo je skvělé!\nUrčitě ale při zkoušení narazíš na pár věcí, které je potřeba dodělat:</p>\n<ul>\n<li>Had by neměl mít možnost vylézt ven z okýnka.</li>\n<li>Had by měl jíst jídlo a růst.</li>\n<li>Hra by měla skončit, když had narazí sám do sebe nebo do okraje okna.</li>\n</ul>\n<p>Pojďme je vyřešit, jednu po druhé.</p>\n<h2>Zatím dobrý, teď ale narazíme</h2>\n<p>„Hadí“ hry jako ta naše mají dvě varianty: buď je kolem hřiště „zeď“\na hráč při nárazu do okraje prohraje, nebo je hřiště „nekonečné“ – had okrajem\nproleze a objeví se na druhé straně.\nMy naprogramujeme tu první variantu – zeď.</p>\n<p>Abys zjistila, jestli had „vylezl“ z levého okraje okna ven,\nje potřeba zkontrolovat, jestli <var>x</var>-ová souřadnice hlavy\nje menší než 0.\nTo je dobré udělat hned poté, co nové souřadnice hlavy získáš – konkrétně\nhned za řádkem <code>new_head = new_x, new_y</code> v metodě <code>move</code>.</p>\n<p>A co při takovém nárazu udělat?\nPro začátek bude nejjednodušší ukončit hru.\nNa to má Python funkci <code>exit()</code>, která funguje podobně jako když v programu\nnastane chyba.\nJen místo chybového výpisu ukáže daný text.</p>\n<p>Ukončení programu není příliš příjemný způsob, jak říct hráčovi že prohrál.\nZa chvíli ale tuhle část předěláme, tak prozatím tenhle jednoduchý způsob postačí.</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n <span class=\"n\">dir_x</span><span class=\"p\">,</span> <span class=\"n\">dir_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span> <span class=\"o\">+</span> <span class=\"n\">dir_x</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span> <span class=\"o\">+</span> <span class=\"n\">dir_y</span>\n <span class=\"n\">new_head</span> <span class=\"o\">=</span> <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span>\n\n <span class=\"c1\"># Nový kód – kontrola vylezení z hrací plochy</span>\n <span class=\"k\">if</span> <span class=\"n\">new_x</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n</pre></div><p>Věřím, že zvládneš udělat stejnou kontrolu pro vylezení ze spodního okraje.</p>\n<p>Jak ale ošetřit ty zbylé okraje – pravý a horní?\nNa to je potřeba znát velikost okýnka.\nA tu zná Pyglet; třída se stavem by k okýnku neměla mít přístup!</p>\n<p>Na velikosti herní plochy závisí chování hry.\nTahle informace tedy bude tedy muset být součást stavu.\nPro začátek nějakou velikost – třeba 10×10 – nastav v <code>__init__</code>:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n</pre></div><p>A pak zařiď, aby po nárazu na neviditelnou stěnu kolem hřiště velkého\n10×10 políček hra skončila.\nPořádně vyzkoušej všechny varianty – severní, jižní, východní i západní zeď.\nHad je virtuální, nemusíš se bát že mu z toho vyroste boule.</p>\n<div class=\"solution\" id=\"solution-3\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/3/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span> <span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n <span class=\"n\">dir_x</span><span class=\"p\">,</span> <span class=\"n\">dir_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span> <span class=\"o\">+</span> <span class=\"n\">dir_x</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span> <span class=\"o\">+</span> <span class=\"n\">dir_y</span>\n\n <span class=\"c1\"># Kontrola vylezení z hrací plochy</span>\n <span class=\"k\">if</span> <span class=\"n\">new_x</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">new_y</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">new_x</span> <span class=\"o\">>=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">new_y</span> <span class=\"o\">>=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n\n <span class=\"n\">new_head</span> <span class=\"o\">=</span> <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n</pre></div>\n </div>\n</div><p>A pak v souboru se hrou hned po tom co vytvoříš stav (<code>state = State()</code>)\na okýnko (<code>window</code>) nastav <em>opravdovou</em> velikost.\nPoužij celočíselné dělení, aby počet políček byl v celých číslech:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">width</span> <span class=\"o\">=</span> <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">width</span> <span class=\"o\">//</span> <span class=\"n\">TILE_SIZE</span>\n<span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">height</span> <span class=\"o\">=</span> <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">height</span> <span class=\"o\">//</span> <span class=\"n\">TILE_SIZE</span>\n</pre></div><h2>Krmení</h2>\n<p>Tak. Had je v kleci, už nemůže vylézt.\nCo dál?</p>\n<p>Teď se musíš o hada postarat: pravidelně ho krmit.\nAle ještě předtím je potřeba ho naučit, jak se vůbec jí – na naši potravu\nještě není zvyklý.\nKdyž to zvládneš, poroste jako z vody!</p>\n<p>Konkrétně musíš hlavně zajistit aby, když se had připlazí na políčko\ns jídlem, jídlo zmizelo.\nK tomu se dá použít:</p>\n<ul>\n<li>operátor <code>in</code>, který zjišťuje jestli něco (třeba\nsouřadnice) je v nějakém seznamu (třeba seznamu souřadnic jídla), a</li>\n<li>metoda <code>remove</code>, která ze seznamu odstraní daný prvek (podle <em>hodnoty</em> prvku\n– na rozdíl od <code>del</code>, který maže podle pozice).</li>\n</ul>\n<p>Za kontrolu vylezení z hrací plochy potřebuješ dát kód,\nkterý dělá následující:</p>\n<ul>\n<li>Pokud je nová pozice hlavy v seznamu souřadnic jídla:<ul>\n<li>Odeber tuhle pozici ze seznamu souřadnic jídla</li>\n</ul>\n</li>\n</ul>\n<p>Zvládneš ho napsat?</p>\n<div class=\"solution\" id=\"solution-4\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/4/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span> <span class=\"k\">if</span> <span class=\"n\">new_head</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">remove</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n</pre></div>\n </div>\n</div><p>Vyzkoušej, jestli to funguje. Had by měl jíst jídlo.</p>\n<p>Ještě ale zbývá zařídit, aby po každém soustu trochu povyrostl.\nAle jak? Kterým směrem má růst?</p>\n<p>Tady je dobré se podívat na existující kód a uvědomit si, co dělá.</p>\n<p>Náš had se plazí tak, že napřed vepředu povyroste (pomocí <code>append</code>)\na potom se vzadu zmenší (pomocí <code>del self.snake[0]</code>).</p>\n<p>Aby tedy po snězení jídla vyrostl, stačí <em>přeskočit</em> ono zmenšování!\nOno <em>přeskočit</em> znamená podmínit, pomocí <code>if</code>.\nLogika jezení a zmenšování hada tedy bude:</p>\n<ul>\n<li>Když had sní jídlo, jídlo zmizí. Had se nezmenší.</li>\n<li>Jinak (tedy když had <em>nesní</em> jídlo) se had zmenší (a tudíž neroste).</li>\n</ul>\n<p>Neboli přeloženo do Pythonu:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"k\">if</span> <span class=\"n\">new_head</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">remove</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n</pre></div><p>Pro ty, co se začínají ztrácet, dám k dispozici celou metodu <code>move</code>.\nBěda ale těm, kdo opisují kód aniž by se mu snažili porozumět!</p>\n<div class=\"solution\" id=\"solution-5\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/5/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span> <span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n <span class=\"n\">dir_x</span><span class=\"p\">,</span> <span class=\"n\">dir_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span> <span class=\"o\">+</span> <span class=\"n\">dir_x</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span> <span class=\"o\">+</span> <span class=\"n\">dir_y</span>\n <span class=\"n\">new_head</span> <span class=\"o\">=</span> <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span>\n\n <span class=\"c1\"># Kontrola vylezení z hrací plochy</span>\n <span class=\"k\">if</span> <span class=\"n\">new_x</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">new_y</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">new_x</span> <span class=\"o\">>=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">new_y</span> <span class=\"o\">>=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">new_head</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">remove</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n</pre></div>\n </div>\n</div><h3>Nové jídlo</h3>\n<p>Když už had umí jíst, je potřeba mu zajistit pravidelný přísun jídla.\nNejlépe tak, že se každé snězené jídlo nahradí novým.</p>\n<p>Přidej do třídy <code>State</code> následující novou metodu, která umí přidat jídlo:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"k\">def</span> <span class=\"nf\">add_food</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n <span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n <span class=\"n\">position</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">)</span>\n</pre></div><p>Pak tuhle metodu zavolej – najdi v programu kód, který se provádí když\nje potřeba přidat nové jídlo, a přidej tam následující řádek:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">add_food</span><span class=\"p\">()</span>\n</pre></div><p>Tahle metoda přidává jídlo na pozici (0, 0), tedy stále do stejného rohu.\nBylo by ale fajn, kdyby se nové jídlo objevilo vždycky jinde,\nna náhodném místě.\nNa to můžeme použít funkci <code>random.randrange</code>.\nVzpomeň si, že volání <code>randrage(N)</code> vrátí náhodné celé číslo od\n0 do <var>N</var> - 1.</p>\n<p>Jaký rozsah čísel potřebujeme pro hadí jídlo?</p>\n<p>Až na to přijdeš, zkus přidat náhodu do programu: jídlo by se mělo objevit\nna <em>úplně náhjodném</em> políčku na herní ploše.</p>\n<p>Nezapomeň na <code>import random</code> – to patří na úplný začátek souboru.\nDalší změny ale už dělej jen v metodě <code>add_food</code>.</p>\n<div class=\"solution\" id=\"solution-6\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/6/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span> <span class=\"k\">def</span> <span class=\"nf\">add_food</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">random</span><span class=\"o\">.</span><span class=\"n\">randrange</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span><span class=\"p\">)</span>\n <span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">random</span><span class=\"o\">.</span><span class=\"n\">randrange</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span><span class=\"p\">)</span>\n <span class=\"n\">position</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">)</span>\n</pre></div>\n </div>\n</div><p>Až to budeš testovat, asi zjistíš, že <em>úplně náhodné</em> políčko není ideální.\nObčas se totiž jídlo objeví na políčku s hadem, nebo dokonce na jiném jídle.\nJe proto dobré tuhle situaci zkontrolovat, a když volba padne na plné políčko,\njídlo nepřidávat:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">position</span> <span class=\"ow\">not</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">)</span> <span class=\"ow\">and</span> <span class=\"p\">(</span><span class=\"n\">position</span> <span class=\"ow\">not</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">)</span>\n</pre></div><p>Když ale zkusíš <em>tohle</em>, zjistíš, že občas se nové jídlo vůbec nepřidá.\nTo taky není vhodná varianta – had by tak měl hlad.\nCo s tím?</p>\n<p>Překvapivě dobré (i když ne <em>úplně</em> ideální) řešení je zkusit políčko vybrat\nněkolikrát.\nKdyž padne prázdné políčko, šup tam s jídlem; když padne plné, tak to\nprostě zkusit znovu.</p>\n<p>Je ale potřeba počet pokusů omezit, aby v situaci, kdy je pole <em>úplně</em> plné,\npočítač nevybíral donekonečna.\nŘekněme že když se na 100 pokusů nepodaří prázdné políčko vybrat,\nvzdáme to. Jídla už je nejspíš dost.</p>\n<p>Metoda <code>add_food</code> po všech úpravách bude vypadat takhle:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"k\">def</span> <span class=\"nf\">add_food</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">for</span> <span class=\"n\">try_number</span> <span class=\"ow\">in</span> <span class=\"nb\">range</span><span class=\"p\">(</span><span class=\"mi\">100</span><span class=\"p\">):</span>\n <span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">random</span><span class=\"o\">.</span><span class=\"n\">randrange</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span><span class=\"p\">)</span>\n <span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">random</span><span class=\"o\">.</span><span class=\"n\">randrange</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span><span class=\"p\">)</span>\n <span class=\"n\">position</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">position</span> <span class=\"ow\">not</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">)</span> <span class=\"ow\">and</span> <span class=\"p\">(</span><span class=\"n\">position</span> <span class=\"ow\">not</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">)</span>\n <span class=\"c1\"># Ukončení funkce ("vyskočí" i z cyklu for)</span>\n <span class=\"k\">return</span>\n</pre></div><p>Jestli ti to funguje, ještě zařiď, aby na začátku hry bylo jídlo na náhodných\npozicích.</p>\n<div class=\"solution\" id=\"solution-7\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/7/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <p>V metodě <code>__init__</code> se dá místo nastavení <code>self.food</code> na seznam s pozicemi\njídla napsat:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span> <span class=\"o\">=</span> <span class=\"p\">[]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">add_food</span><span class=\"p\">()</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">add_food</span><span class=\"p\">()</span>\n</pre></div><p>Pak budou na začátku hry na hada čekat dvě náhodná jídla.</p>\n </div>\n</div><h2>Konec</h2>\n<p>Had teď může narůst do obrovských rozměrů – a lze prohrát jen tím, že\nnarazí do stěny.\nZaříď teď, aby hra skončila i když narazí sám do sebe.</p>\n<p>Jak na to?\nDo metody <code>move</code>, vedle kontrola vylezení z hrací plochy,\ndej kód který udělá následující:</p>\n<ul>\n<li>Pokud jsou souřadnice nové hlavy už součást hada:<ul>\n<li>Ukonči hru (podobně jako po nárazu do stěny).</li>\n</ul>\n</li>\n</ul>\n<p>Dokážeš to převést do Pythonu?</p>\n<div class=\"solution\" id=\"solution-8\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/8/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span> <span class=\"c1\"># Kontrola, jestli had narazil</span>\n <span class=\"k\">if</span> <span class=\"n\">new_head</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">:</span>\n <span class=\"nb\">exit</span><span class=\"p\">(</span><span class=\"s1\">'GAME OVER'</span><span class=\"p\">)</span>\n</pre></div>\n </div>\n</div><p>Hotovo!</p>\n<h3>Pauza</h3>\n<p>Není ale dobré při konci hry ukončit celý program a zavřít okýnko.</p>\n<p>Lepší je hru „zapauzovat“ a ukázat hráči situaci, do které nešťastného hada\ndostal, aby se z ní mohl pro příště poučit.</p>\n<p>Aby to bylo možné, dáme do stavu hry další atribut: <code>alive</code>.\nTen bude nastavený na <code>True</code>, dokud bude had žít.\nKdyž had narazí, nastaví se na <code>False</code>, a od té doby se už nebude pohybovat.\nJe dobré i graficky ukázat, že hadovi není dobře – hráč pak spíš bude\nzpytovat svědomí.</p>\n<p>Zkus zapřemýšlet, kam v kódu patří následující\nkousky kódu, které prohru implementují:</p>\n<div class=\"highlight\"><pre><span></span> <span class=\"c1\"># Prvotní nastavení atributu</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">True</span>\n</pre></div><div class=\"highlight\"><pre><span></span> <span class=\"c1\"># Zastavení hada</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n</pre></div><div class=\"highlight\"><pre><span></span> <span class=\"c1\"># Zabránění pohybu</span>\n <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span><span class=\"p\">:</span>\n <span class=\"k\">return</span>\n</pre></div><div class=\"highlight\"><pre><span></span> <span class=\"c1\"># Grafická indikace</span>\n <span class=\"k\">if</span> <span class=\"n\">after</span> <span class=\"o\">==</span> <span class=\"s1\">'end'</span> <span class=\"ow\">and</span> <span class=\"ow\">not</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">alive</span><span class=\"p\">:</span>\n <span class=\"n\">after</span> <span class=\"o\">=</span> <span class=\"s1\">'dead'</span>\n</pre></div><div class=\"solution\" id=\"solution-9\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/9/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <ul>\n<li>„Prvotní nastavení atributu“ do metody <code>__init__</code>.</li>\n<li>„Zastaveni hada“ místo všech výskytů <code>raise("Game Over")</code>.</li>\n<li>„Zabránění pohybu“ na úplný začátek metody <code>move</code> (příkaz <code>return</code>\nokamžitě ukončí provádění metody).</li>\n<li>„Grafická indikace“ za sekci pro vybírání obrázku pro kousek\nhada.</li>\n</ul>\n </div>\n</div><h2>A to je vše?</h2>\n<p>Gratuluji, máš funkční a hratelnou hru!\nDoufám že jsi na sebe hrdá!</p>\n<p>Dej si něco sladkého, zasloužíš si to.</p>\n<hr>\n<p>Tady je moje řešení.\nTo se touhle dobou od toho tvého může dost lišit – to je úplně normální.\n(A nedívej se sem dokud hada nenaprogramuješ sama,\nChybami a neustálým zkoušením se člověk učí – a programátor zvlášť.\nČtením už vyřešeného se učí hůř.)</p>\n<div class=\"solution\" id=\"solution-10\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-podzim-snake/snake/logic/index/solutions/10/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">random</span>\n<span class=\"kn\">from</span> <span class=\"nn\">pathlib</span> <span class=\"kn\">import</span> <span class=\"n\">Path</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">pyglet</span>\n\n<span class=\"n\">TILE_SIZE</span> <span class=\"o\">=</span> <span class=\"mi\">64</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">State</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span> <span class=\"o\">=</span> <span class=\"p\">[(</span><span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">),</span> <span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span><span class=\"p\">)]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">1</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span> <span class=\"o\">=</span> <span class=\"p\">[]</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">add_food</span><span class=\"p\">()</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">add_food</span><span class=\"p\">()</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">True</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span><span class=\"p\">:</span>\n <span class=\"k\">return</span>\n\n <span class=\"n\">old_x</span><span class=\"p\">,</span> <span class=\"n\">old_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n <span class=\"n\">dir_x</span><span class=\"p\">,</span> <span class=\"n\">dir_y</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span>\n <span class=\"n\">new_x</span> <span class=\"o\">=</span> <span class=\"n\">old_x</span> <span class=\"o\">+</span> <span class=\"n\">dir_x</span>\n <span class=\"n\">new_y</span> <span class=\"o\">=</span> <span class=\"n\">old_y</span> <span class=\"o\">+</span> <span class=\"n\">dir_y</span>\n\n <span class=\"c1\"># Kontrola vylezení z hrací plochy</span>\n <span class=\"k\">if</span> <span class=\"n\">new_x</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n <span class=\"k\">if</span> <span class=\"n\">new_y</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n <span class=\"k\">if</span> <span class=\"n\">new_x</span> <span class=\"o\">>=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n <span class=\"k\">if</span> <span class=\"n\">new_y</span> <span class=\"o\">>=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n\n <span class=\"n\">new_head</span> <span class=\"o\">=</span> <span class=\"n\">new_x</span><span class=\"p\">,</span> <span class=\"n\">new_y</span>\n <span class=\"k\">if</span> <span class=\"n\">new_head</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">alive</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n\n <span class=\"k\">if</span> <span class=\"n\">new_head</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">remove</span><span class=\"p\">(</span><span class=\"n\">new_head</span><span class=\"p\">)</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">add_food</span><span class=\"p\">()</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">del</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">add_food</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">for</span> <span class=\"n\">try_number</span> <span class=\"ow\">in</span> <span class=\"nb\">range</span><span class=\"p\">(</span><span class=\"mi\">100</span><span class=\"p\">):</span>\n <span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">random</span><span class=\"o\">.</span><span class=\"n\">randrange</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">width</span><span class=\"p\">)</span>\n <span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"n\">random</span><span class=\"o\">.</span><span class=\"n\">randrange</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">height</span><span class=\"p\">)</span>\n <span class=\"n\">position</span> <span class=\"o\">=</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">position</span> <span class=\"ow\">not</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">)</span> <span class=\"ow\">and</span> <span class=\"p\">(</span><span class=\"n\">position</span> <span class=\"ow\">not</span> <span class=\"ow\">in</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">)</span>\n <span class=\"k\">return</span>\n\n<span class=\"n\">red_image</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">'apple.png'</span><span class=\"p\">)</span>\n<span class=\"n\">snake_tiles</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n<span class=\"k\">for</span> <span class=\"n\">start</span> <span class=\"ow\">in</span> <span class=\"p\">[</span><span class=\"s1\">'bottom'</span><span class=\"p\">,</span> <span class=\"s1\">'end'</span><span class=\"p\">,</span> <span class=\"s1\">'left'</span><span class=\"p\">,</span> <span class=\"s1\">'right'</span><span class=\"p\">,</span> <span class=\"s1\">'top'</span><span class=\"p\">]:</span>\n <span class=\"k\">for</span> <span class=\"n\">end</span> <span class=\"ow\">in</span> <span class=\"p\">[</span><span class=\"s1\">'bottom'</span><span class=\"p\">,</span> <span class=\"s1\">'end'</span><span class=\"p\">,</span> <span class=\"s1\">'left'</span><span class=\"p\">,</span> <span class=\"s1\">'right'</span><span class=\"p\">,</span> <span class=\"s1\">'top'</span><span class=\"p\">,</span> <span class=\"s1\">'dead'</span><span class=\"p\">,</span> <span class=\"s1\">'tongue'</span><span class=\"p\">]:</span>\n <span class=\"n\">key</span> <span class=\"o\">=</span> <span class=\"n\">start</span> <span class=\"o\">+</span> <span class=\"s1\">'-'</span> <span class=\"o\">+</span> <span class=\"n\">end</span>\n <span class=\"n\">image</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">image</span><span class=\"o\">.</span><span class=\"n\">load</span><span class=\"p\">(</span><span class=\"s1\">'snake-tiles/'</span> <span class=\"o\">+</span> <span class=\"n\">key</span> <span class=\"o\">+</span> <span class=\"s1\">'.png'</span><span class=\"p\">)</span>\n <span class=\"n\">snake_tiles</span><span class=\"p\">[</span><span class=\"n\">key</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">image</span>\n\n<span class=\"n\">window</span> <span class=\"o\">=</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">Window</span><span class=\"p\">()</span>\n\n<span class=\"n\">state</span> <span class=\"o\">=</span> <span class=\"n\">State</span><span class=\"p\">()</span>\n<span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">width</span> <span class=\"o\">=</span> <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">width</span> <span class=\"o\">//</span> <span class=\"n\">TILE_SIZE</span>\n<span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">height</span> <span class=\"o\">=</span> <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">height</span> <span class=\"o\">//</span> <span class=\"n\">TILE_SIZE</span>\n\n\n<span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_draw</span><span class=\"p\">():</span>\n <span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">clear</span><span class=\"p\">()</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glEnable</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_BLEND</span><span class=\"p\">)</span>\n <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">glBlendFunc</span><span class=\"p\">(</span><span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_SRC_ALPHA</span><span class=\"p\">,</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">gl</span><span class=\"o\">.</span><span class=\"n\">GL_ONE_MINUS_SRC_ALPHA</span><span class=\"p\">)</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake</span><span class=\"p\">:</span>\n <span class=\"n\">before</span> <span class=\"o\">=</span> <span class=\"s1\">'end'</span> <span class=\"c1\"># (Tady případně je nějaké</span>\n <span class=\"n\">after</span> <span class=\"o\">=</span> <span class=\"s1\">'end'</span> <span class=\"c1\"># složitější vybírání políčka)</span>\n <span class=\"k\">if</span> <span class=\"n\">after</span> <span class=\"o\">==</span> <span class=\"s1\">'end'</span> <span class=\"ow\">and</span> <span class=\"ow\">not</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">alive</span><span class=\"p\">:</span>\n <span class=\"n\">after</span> <span class=\"o\">=</span> <span class=\"s1\">'dead'</span>\n <span class=\"n\">snake_tiles</span><span class=\"p\">[</span><span class=\"n\">before</span> <span class=\"o\">+</span> <span class=\"s1\">'-'</span> <span class=\"o\">+</span> <span class=\"n\">after</span><span class=\"p\">]</span><span class=\"o\">.</span><span class=\"n\">blit</span><span class=\"p\">(</span>\n <span class=\"n\">x</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">width</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">height</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">)</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"ow\">in</span> <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">food</span><span class=\"p\">:</span>\n <span class=\"n\">red_image</span><span class=\"o\">.</span><span class=\"n\">blit</span><span class=\"p\">(</span>\n <span class=\"n\">x</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">y</span> <span class=\"o\">*</span> <span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">width</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">,</span> <span class=\"n\">height</span><span class=\"o\">=</span><span class=\"n\">TILE_SIZE</span><span class=\"p\">)</span>\n\n\n<span class=\"nd\">@window.event</span>\n<span class=\"k\">def</span> <span class=\"nf\">on_key_press</span><span class=\"p\">(</span><span class=\"n\">key_code</span><span class=\"p\">,</span> <span class=\"n\">modifier</span><span class=\"p\">):</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">LEFT</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">RIGHT</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">0</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">DOWN</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"o\">-</span><span class=\"mi\">1</span>\n <span class=\"k\">if</span> <span class=\"n\">key_code</span> <span class=\"o\">==</span> <span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">window</span><span class=\"o\">.</span><span class=\"n\">key</span><span class=\"o\">.</span><span class=\"n\">UP</span><span class=\"p\">:</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">snake_direction</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">,</span> <span class=\"mi\">1</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">move</span><span class=\"p\">(</span><span class=\"n\">dt</span><span class=\"p\">):</span>\n <span class=\"n\">state</span><span class=\"o\">.</span><span class=\"n\">move</span><span class=\"p\">()</span>\n\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">clock</span><span class=\"o\">.</span><span class=\"n\">schedule_interval</span><span class=\"p\">(</span><span class=\"n\">move</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"o\">/</span><span class=\"mi\">6</span><span class=\"p\">)</span>\n\n<span class=\"n\">pyglet</span><span class=\"o\">.</span><span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">run</span><span class=\"p\">()</span>\n</pre></div>\n </div>\n</div><h2>Co dál?</h2>\n<p>Najdeš ještě nějaké další vylepšení, které by se dalo udělat?</p>\n<p>Zkus třeba následující rozšíření. Jsou seřazené zhruba podle složitosti:</p>\n<ul>\n<li><p>Vylepši ovládání (a hratelnost!) podle <a href=\"/2019/brno-podzim-snake/snake/handling/\">návodu</a>.</p>\n</li>\n<li><p>Každých 30 vteřin hry přibude samo od sebe nové jídlo,\ntakže jich pak bude na hrací ploše víc.</p>\n</li>\n<li><p>Hra se bude postupně zrychlovat.<br>\n<em>(Na to je nejlepší předělat funkci <code>move</code>, aby </em>sama<em>\nnaplánovala, kdy se má příště zavolat. Volání <code>schedule_interval</code> tak už\nnebude potřeba.)</em></p>\n</li>\n<li><p>Když had vyleze ven z okýnka, místo konce hry se objeví na druhé straně.\n(Viz <a href=\"/2019/brno-podzim-snake/snake/toroid/\">návod</a>.)</p>\n</li>\n<li><p>Hadi budou dva; druhý se ovládá klávesami\n<kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd>.<br>\n<em>(Na to je nejlepší udělat novou třídu, <code>Snake</code>, a všechen stav hada\npřesunout ze <code>State</code> do ní. Ve <code>State</code> pak měj seznam hadů.\nTéhle změně je potřeba přizpůsobit celý zytek programu.)</em></p>\n</li>\n</ul>\n\n\n " } } }