Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.

177/480

Logik -- příklad

Obsah:

  1. Zadání
  2. Reference
  3. Kostra zdrojového kódu
  4. GUI poprvé
  5. GUI podruhé
  6. Obslužné funkce

Zadání

Implementujte deskovou hru Logik. Hracím kamenům budou odpovídat barvy.

Máme k dispozici

Na začátku hry je náhodně rozmístěno 5 různých hracích kamenů. Druh hracího kamenu se nesmí opakovat. Hráč hádá rozmístění kamenů.

Program při každém pokusu hráči sdělí:

  1. kolik kamenů je použito správně, co do druhu i pozice (černé vyhodnocovací kameny)
  2. kolik kamenů je správného druhu, ale na nesprávné pozici (bílé vyhodnocovací kameny) Toto sdělení může být číslovkou nebo pomocí vyhodnocovacího kamene

Hráč má 10 pokusů na to, aby uhodl skrytou kombinaci. Po uhodnutí nebo po vyčerpání všech pokusů program ukáže zadanou kombinaci.

Reference

Kostra zdrojového kódu

#!/usr/bin/python
# -*- coding: utf8 -*-

from Tkinter import *
####################################################
## Globální proměnné


##################################################
### Definice funkcí 


##################################################
### GUI


okno = Tk()
okno.title("logik.py")
okno.config( padx=10, pady=10)


##################################################
## Nekonečná smyčka
okno.mainloop()

`--> stáhnout

GUI poprvé


### Globální proměnné
barvy="#c90000 #99dd00 #0000ff #ffff00 #008888 #880088 #dd9900 #ffffff".split()
sirka=30
vyska=20

### skryté pole
for sloupec in range(5):
    c = Canvas(okno, background='black', width=sirka, height=vyska)
    c.grid(column=sloupec,row=0)

### titulek
Label(okno, text=u"Logik").grid(columnspan=5)


### pole s hádanou barvou
for sloupec in range(5):
    for radek in range(10):
        c = Canvas(okno, background='white', width=sirka, height=vyska)
        c.grid(column=sloupec, row=radek+2)
### odpověď programu 
for radek in range(10):
    info = Label(okno,text="-/-").grid(column=6,row=radek+2)

# tlačítka s barvou
# bitmap= "error" "gray75" "gray50" "gray25" "gray12" 
#         "hourglass" "info" "questhead" "question" "warning"
### tlačítka
for sloupec in range(5):
    for radek,barva in enumerate(barvy):
        b = Button(okno, width=sirka, height=vyska,  
                bg=barva, fg=barva, 
                activebackground=barva,activeforeground=barva,
                bitmap='gray12' )
        b.grid(column=sloupec, row=radek+11)

Button(okno,text=u'Odeslat').grid(column=6,row=11)

`--> stáhnout

GUI podruhé

Výše uvedený zdrojový kód má jednu zásadní nevýhodu: udělátka jsou sice umístěna, ale už nelze měnit jejich vlastnosti, protože odkaz na ně je uložen do proměnné, která se neustále přepisuje. Musíme si proto vytvořit proměnnou, do které všechna udělátka umístíme. K tomu nejlépe poslouží seznam.

### skryté pole
skryteBarvy=[]
for sloupec in range(5):
    skryteBarvy.append( Canvas(okno, background='black', width=sirka, height=vyska) )
    skryteBarvy[-1].grid(column=sloupec,row=0)

#skryteBarvy[2].config(background='green')

### titulek
Label(okno, text=u"Logik").grid(columnspan=5)
Label(okno, text=u"barva/pozice").grid(row=1,column=6)

### pole s hádanou barvou
hadaneBarvy=[] # prázdný seznam udělátek
# k jednotlivým udělátkům mohu přistupovat: hadaneBarvy[sloupec][radek]
for sloupec in range(5):
    radekUdelatek = [] # prázdný seznam udělátek
    for radek in range(10):
        radekUdelatek.append( Canvas(okno, background='gray', width=sirka, height=vyska) )
        radekUdelatek[-1].grid(column=sloupec, row=radek+2)
    hadaneBarvy.append( radekUdelatek ) 

#hadaneBarvy[2][1].config(background='green')

### odpověď programu 
odpovedProgramu = []
for radek in range(10):
    odpovedProgramu.append( Label(okno,text="-/-") )
    odpovedProgramu[-1].grid(column=6,row=radek+2)

### oddělovací čára
Canvas( background='#777', 
        width=6*sirka, height=8).grid(column=0,row=12, columnspan=5)

### tlačítka
# tlačítka s barvou
# bitmap= "error" "gray75" "gray50" "gray25" "gray12" 
#         "hourglass" "info" "questhead" "question" "warning"

tlacitka = [] # prázdný seznam udělátek
for sloupec in range(5):
    radekUdelatek = [] # prázdný seznam udělátek
    for radek,barva in enumerate(barvy):
        def fce(s=sloupec, r=radek):
            barvaClick(s,r)
        b = Button(okno, width=sirka, height=vyska,  
                bg=barva, fg=barva, 
                activebackground=barva,activeforeground=barva,
                bitmap='gray12',
                command= fce )
#                command= eval('lambda: barvaClick({},{})'.format(sloupec,radek)) ) 
        b.grid(column=sloupec, row=radek+13)
        radekUdelatek.append(b)
    tlacitka.append(radekUdelatek)

#tlacitka[2][3].config(bg='#12ab67')

odeslatButton=Button(okno,text=u'Odeslat', command=odeslatPokus )
odeslatButton.grid(column=6,row=13)

Button(okno,text=u'Znovu', command=novaHra ).grid(column=6,row=14)
####################################################

`--> stáhnout

Pokud je seznam udělátek dvourozměrné pole (tlacitka a hadaneBarvy), vytváří se vždy ve vnitřním cyklu celý řádek a poté se tento celý řádek přidá do seznamu. Na jednotlivá udělátka se potom odvoláváme konstrukcí tlacitka[sloupec][radek].

Obsluha tlačítka

Funkce, která se zavolá při kliknutí na tlačítko je dána parametrem command. Je tu ale trochu potíž: Tato funkce nemá žádné parametry. My ale potřebujeme uvnitř této obslužné funkce poznat, na které tlačítko bylo kliknuto. Tato malá potíž se dá obejít pomocí anonymní funkce lambda.

    b=Button( ..., command=lambda: barvaClick(sloupec,radek) ) 

`--> stáhnout

Tento kód funguje docela dobře, ale nastává další potíž: On se totiž obsah proměnných sloupec a radek čte až při kliknutí na tlačítko a ne při vytváření tlačítka. Proto si musíme pomoci funkcí eval, která převádí řetězec na výraz a donutí tak Python přečíst obsah proměnných už při definici tlačítka.

    b=Button( ..., command= eval('lambda: barvaClick({},{})'.format(sloupec,radek)) ) 

`--> stáhnout

Stejný problém řeší i tento (kapánek složitější) kód, který využívá výchozích hodnot argumentů funkce. Funkce se v každé obrátce cyklu přepíše novou definicí s jinými výchozími hodnotami parametrů.

    def fce(s=sloupec, r=radek):
        barvaClick(s,r)
    b = Button(okno, ... , command= fce )

`--> stáhnout

Obslužné funkce

Nyní chybí už jen několik obslužných funkcí, které zajistí generování náhodných barev, odeslaní pokusu, odkrytí hádanky nebo vytvoření nové hry.

def barvaClick(sloupec,radek):
    hadaneBarvy[sloupec][aktualniRadek].config(background=barvy[radek])
    pokus[sloupec] = barvy[radek]

def odeslatPokus():
    global aktualniRadek
    global pokus
    ## kontrola a pozic barev
    spravnaBarva=0
    spravnaPozice = 0
    for i in range(len(pokus)):
        if pokus[i]==hadanka[i]:
            spravnaPozice += 1
        elif pokus[i] in hadanka:
            spravnaBarva += 1
    odpovedProgramu[aktualniRadek].config(text='{}/{}'.format(spravnaBarva,spravnaPozice))
    ## konec hry
    if aktualniRadek==0 or spravnaPozice == 5:
        odkryjHadanku()
        odeslatButton.config(state=DISABLED)
    aktualniRadek -= 1
    pokus=[None]*5

def generujHadanku():
    hadanka = []
    for _ in range(5):
        while 1:
            nahodnaBarva=barvy[random.randint(0,len(barvy)-1)]
            if not nahodnaBarva in hadanka:
                break
        hadanka.append(nahodnaBarva)
    return hadanka


def odkryjHadanku():
    for i,udelatko in enumerate(skryteBarvy):
        udelatko.config(bg=hadanka[i])

def novaHra():
    global hadanka
    global aktualniRadek
    global pokus
    hadanka=generujHadanku()
    aktualniRadek=9
    for radek in range(10):
        for sloupec in range(5):
            hadaneBarvy[sloupec][radek].config(background='gray')
    odeslatButton.config(state=NORMAL)
    for sloupec in range(5):
        skryteBarvy[sloupec].config(background='black')
    for udelatko in odpovedProgramu:
        udelatko.config(text='-/-')
    pokus=[None]*5

`--> stáhnout

Jsou samozřejmě zapotřebí globální proměnné, pomocí kterých se jednotlivé obslužné funkce komunikují.

aktualniRadek = 9 # začánám dole a při každém pokusu se posunu
                  # o jeno nahoru. Řádek 0 je poslední.
hadanka=None
pokus=[None]*5

`--> stáhnout

| navigace |

Licence Creative Commons Valid XHTML 1.0 Strict Valid CSS! Antispam.er.cz Blog: Tlapicka.net