Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.
177/480
Obsah:
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í:
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.
#!/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
### 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
bitmap
. Kdyby tato vlastnost nebyla použita
zadávala by se šířka width
a výška height
ve znacích a nikoli v pixelech.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.
skryteBarvy
budou pole, která má hráč uhodnouthadaneBarvy
budou pole, do kterého hráč vkládá svůj pokusodpovedProgramu
budou nápisy s odpovědí programu, kterou hráči v každém kole dávátlacitka
budou tlačítka, která určují hádanou barvu### 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]
.
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
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