You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
9.3 KiB
182 lines
9.3 KiB
import pygame
|
|
import asyncio # PŘIDÁNO PRO WEB: importujeme asyncio pro neblokující smyčku
|
|
import random
|
|
import sys
|
|
|
|
# Povinná příprava knihovny Pygame
|
|
async def main(): # PŘIDÁNO PRO WEB: Zabalíme celou hru do asynchronní funkce
|
|
pygame.init()
|
|
|
|
# --- NASTAVENÍ OKNA A MŘÍŽKY ---
|
|
# Hra SNAKE (Had) většinou nefunguje na pixely jako ostatní hry, ale funguje na jakési neviditelné "Mřížce".
|
|
SIRKA_OKNA = 800
|
|
VYSKA_OKNA = 600
|
|
VELIKOST_DILKU = 20 # Velikost jednoho čtverečku na mřížce (dílku hada i jídla)
|
|
FPS = 15 # Rychlost hada. Zde je nižší 15, protože pohyb po skocích na mřížce nevyžaduje 60 FPS.
|
|
|
|
# Definice Palety Barev (Red, Green, Blue)
|
|
CERNA = (0, 0, 0)
|
|
BILA = (255, 255, 255)
|
|
ZELENA = (0, 255, 0)
|
|
CERVENA = (255, 0, 0)
|
|
|
|
okno = pygame.display.set_mode((SIRKA_OKNA, VYSKA_OKNA))
|
|
pygame.display.set_caption("Klasický Had (Snake)")
|
|
|
|
hodiny = pygame.time.Clock()
|
|
font = pygame.font.SysFont("arial", 36)
|
|
|
|
# Pomocná funkce pro rychlé vykreslení textu na daných x, y.
|
|
# Zkracuje to kód, abychom pořád neopakovali metody render() a blit().
|
|
def zobraz_text(text, barva, x, y):
|
|
text_plocha = font.render(text, True, barva)
|
|
okno.blit(text_plocha, (x, y))
|
|
|
|
# Veškerou logiku hry teď poprvé schováme do tzv. Hlavní Funkce.
|
|
# Proč? Jakmile funkce skončí (kvůli výhře/prohře), velmi jednoduše se tímto trikem dá celá hra resetovat,
|
|
# jelikož stačí funkci zavolat odznova.
|
|
def hlavni_smycka():
|
|
|
|
# --- POČÁTEČNÍ STAV HRY ---
|
|
# Nejdůležitější princip této hry! Tělo hada není jeden objekt, je to SEZNAM SOUŘADNIC (pole v poli).
|
|
# Každý článek jeho těla má své [x, y]. Ten ÚPLNĚ PRVNÍ prvek na pozici 0 je HLAVA HADA.
|
|
had_telo = [
|
|
[SIRKA_OKNA // 2, VYSKA_OKNA // 2], # Hlava
|
|
[SIRKA_OKNA // 2 - VELIKOST_DILKU, VYSKA_OKNA // 2], # Druhý článek břicha
|
|
[SIRKA_OKNA // 2 - 2 * VELIKOST_DILKU, VYSKA_OKNA // 2] # Ocas
|
|
]
|
|
|
|
# Směr pohybu! Tohle číslo přidáme při každém snímku k souřadnicím HLAVY.
|
|
# Tím ji virtuálně posuneme vpřed.
|
|
smer_x = VELIKOST_DILKU # Z počátku letí had doprava, protože z osy X roste pozitivně
|
|
smer_y = 0 # Do vertikály Y mu zpočátku nepřidáváme nic, letí rovně
|
|
|
|
# --- POZICE JÍDLA (ČERVENÝ ČTVEREČEK) ---
|
|
# Jídlo musíme nasměrovat přesně do nějaké buňky mřížky!
|
|
# Trik: random.randrange(od, do, krok) ... Jídlo padne například na X=20, 40, 60... ale NIKDY na 17, 33 atd.
|
|
# Kdyby nebylo zarovnané na mřížku, tak ho had miné!
|
|
jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU)
|
|
jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU)
|
|
|
|
skore = 0
|
|
konec_hry = False # Boolean kontrolka pro Game Over
|
|
|
|
# Nekonečná smyčka tohoto jednoho konkrétního zápasu
|
|
while True:
|
|
|
|
# 1. ZPRACOVÁNÍ UDÁLOSTÍ
|
|
for udalost in pygame.event.get():
|
|
if udalost.type == pygame.QUIT:
|
|
pygame.quit()
|
|
sys.exit()
|
|
|
|
# Detekce šipek pro změnu SMĚRU pohybu HADA
|
|
if udalost.type == pygame.KEYDOWN:
|
|
|
|
# OCHRANA: Had nemůže provést fyzicky nemožný obrat o 180 stupňů a zajet si rovnou do těla!
|
|
# Tzn. pokud zmáčknul šipku DOLEVA, a had letí rovně/doprava (smer_x == 0),
|
|
# teprve pak si smí dovolit nastavit změnu směru.
|
|
if udalost.key == pygame.K_LEFT and smer_x == 0:
|
|
smer_x = -VELIKOST_DILKU # Záporná hodnota pro směr vlevo
|
|
smer_y = 0 # Vertikální se ruší
|
|
elif udalost.key == pygame.K_RIGHT and smer_x == 0:
|
|
smer_x = VELIKOST_DILKU # Kladná hodnota pro směr vpravo
|
|
smer_y = 0
|
|
elif udalost.key == pygame.K_UP and smer_y == 0:
|
|
smer_x = 0
|
|
smer_y = -VELIKOST_DILKU # Záporná hodnota směrem vzhůru k Y=0
|
|
elif udalost.key == pygame.K_DOWN and smer_y == 0:
|
|
smer_x = 0
|
|
smer_y = VELIKOST_DILKU
|
|
|
|
# Pokud zemřel a bliká varování, čekáme na Mezerník
|
|
if udalost.key == pygame.K_SPACE and konec_hry:
|
|
# Pokud nastane RETURN, znamená to, že se celá tato 'hlavni_smycka()' okamžitě ukončí,
|
|
# její paměť (proměnné had_telo atd) se nenávratně vymaže.
|
|
# Následně ji však náš spouštěč úplně dole v souboru spustí naprosto na čisto znova. (RESTART)
|
|
return
|
|
|
|
# LOGIKA HRY: Provede se jenom pokud žijeme
|
|
if not konec_hry:
|
|
|
|
# --- ZÁKLADNÍ PRINCIP POHYBU HADA ---
|
|
# Jak hada posunout?
|
|
# 1. Spočítáme, kde se bude nacházet hadova hlava v PŘÍŠTÍM Snímku
|
|
# Vezmeme si pozici staré hlavy, neboli had_telo na nultém indexu
|
|
nova_hlava_x = had_telo[0][0] + smer_x
|
|
nova_hlava_y = had_telo[0][1] + smer_y
|
|
|
|
# 2. Tuto ZCELA NOVOU POZICI vsuneme pomocí "insert" na ÚPLNÝ ZAČÁTEK (index 0) našeho Seznamu
|
|
# Náš had je najednou o kousek delší! (Natáhl hlavu tam, kam letí)
|
|
had_telo.insert(0, [nova_hlava_x, nova_hlava_y])
|
|
|
|
# --- SEŽRÁNÍ JÍDLA (Růst hada) ---
|
|
# Zjišťujeme, zda se souřadnice nové hlavy náhodou rovnou nerovnají pozici Jídla na mřížce.
|
|
# Nepoužíváme obdélníkové kolize, protože pracujeme s přesnou mřížkou!
|
|
if nova_hlava_x == jidlo_x and nova_hlava_y == jidlo_y:
|
|
skore += 1 # Bod do tabulky!
|
|
|
|
# Zrušíme staré jídlo a najdeme mu na hracím plánu nové náhodné místo (zarovnané na mřížku)
|
|
jidlo_x = random.randrange(0, SIRKA_OKNA, VELIKOST_DILKU)
|
|
jidlo_y = random.randrange(0, VYSKA_OKNA, VELIKOST_DILKU)
|
|
|
|
# TOHLE JE DŮLEŽITÉ: Náš hadí Seznam byl momentálně obohacen o přední novou "hlavu", tzn vyrostl, a
|
|
# a my mu ten starý OCAS NEZKRÁTÍME. Takže je opravdu objektivně o jedno políčko natrvalo větší!
|
|
else:
|
|
# Pokud jídlo na této pozici NEBYLO, my musíme zachovat hada stále stejně dlouhého.
|
|
# Nahoře jsme mu před chvílí přidali hlavičku. Tady dole mu z konce seznamu (ocásku)
|
|
# jeden prvek odebereme (pomocí .pop()), takže vlastně provedl POHYB A JE STÁLE STEJNĚ DLOUHÝ.
|
|
had_telo.pop()
|
|
|
|
# --- SMRTELNÉ NÁRAZY (Stěna a Sebevražda) ---
|
|
|
|
# Pokud vylétla nová hlava mimo rozměry Okna...
|
|
if (nova_hlava_x < 0 or nova_hlava_x >= SIRKA_OKNA or
|
|
nova_hlava_y < 0 or nova_hlava_y >= VYSKA_OKNA):
|
|
konec_hry = True # GAME OVER
|
|
|
|
# Nebo pokud had sežral vlastního ocas!
|
|
# for-cyklus "for dilek in had_telo[1:]" prohlédne VŠE kromě samotné hlavy (začíná od indexu 1, čili tělo)
|
|
# A pokud se na jedné jediné z těchto pozic těla nachází nově nakreslená hlava, tak had do sebe kousl.
|
|
for dilek in had_telo[1:]:
|
|
if nova_hlava_x == dilek[0] and nova_hlava_y == dilek[1]:
|
|
konec_hry = True
|
|
|
|
# --- VYKRESLOVÁNÍ (Obarvování monitoru) ---
|
|
okno.fill(CERNA) # Umytí tabule na černo
|
|
|
|
# 1. Jídlo (Čtverec o rozměrech jedné buňky na mřížce)
|
|
pygame.draw.rect(okno, CERVENA, (jidlo_x, jidlo_y, VELIKOST_DILKU, VELIKOST_DILKU))
|
|
|
|
# 2. Celý samotný dlouhý had
|
|
# Projdeme seznam všech buněk a pro každičkou z nich na daném [X, Y] nakreslíme jeden zelený kvádr
|
|
for dilek in had_telo:
|
|
pygame.draw.rect(okno, ZELENA, (dilek[0], dilek[1], VELIKOST_DILKU, VELIKOST_DILKU))
|
|
|
|
zobraz_text(f"Skóre: {skore}", BILA, 10, 10)
|
|
|
|
if konec_hry:
|
|
# Zásadní hláška uprostřed pole o tom, ať to zkusí hráč znovu.
|
|
zobraz_text("Konec hry! Stiskni MEZERNÍK pro novou hru.", CERVENA, 100, VYSKA_OKNA // 2)
|
|
|
|
# Hotový frame se prohazuje s tím na monitoru
|
|
pygame.display.flip()
|
|
|
|
# Omezení rychlosti
|
|
hodiny.tick(FPS)
|
|
# PŘIDÁNO PRO WEB: Dáme prohlížeči šanci překreslit obrazovku
|
|
await asyncio.sleep(0)
|
|
|
|
# Úplný spodek skriptu Pythonu
|
|
# Tento 'if' ověřuje, jestli Python zapnul tento soubor napřímo jako první.
|
|
if __name__ == "__main__":
|
|
|
|
# NEKONEČNÁ SLUČKA ŽIVOTA.
|
|
# Jakmile uvnitř `hlavni_smycka` zavoláme 'return' kvůli Game Over + stisku Mezerníku,
|
|
# funkce se zničí, a tento while True ji opět vzkřísí z prachu k novému životu s čistým listem skóre.
|
|
while True:
|
|
hlavni_smycka()
|
|
|
|
|
|
# PŘIDÁNO PRO WEB: Spuštění asynchronní hry
|
|
asyncio.run(main())
|