Készítsünk HANGMAN játékot! - 4. rész

Programunk akkor lesz korszerű, ha nem konzolos módon működik, hiszen manapság már a tenyerünkben fogott telefonok is grafikus desktoppal rendelkeznek. Az egyszerűség kedvéért használjuk a már tárgyalt Tkinter modult, és készítsük el Hangman játékunk grafikus változatát.

Mivel az akasztófánk grafika lesz, az Akasztofa osztályunkat teljesen át kell szabnunk. A Szavak osztályunk viszont egy az egyben felhasználható az eddigi programunkból. Maga a Jatek osztály is erősen átalakul, bár nyomokban emlékeztetni fog a konzolosra.


A játék törzse a következőként fest:

if __name__=="__main__":
    try:
        optlist, args = getopt.getopt(sys.argv[1:], "hvf:", ["help", "version", "file="])
    except getopt.GetoptError:
        help()
        sys.exit(2)
    for opt,param in optlist:
        if opt in ('-h','--help'):
            help()
            sys.exit(0)
        elif opt in ('-v', '--version'):
            print _("Hangman game %s")+VERZIO
            sys.exit(0)
        elif opt in ('-f','--file'):
            SZOFAJL=param

    j=Jatek()
    j.frame.mainloop()

A --file paraméterrel továbbra is indíthatjuk saját, egyedi szó listával a játékot. A j változóban példányosítjuk a Jatek osztályunkat, majd a j.frame.mainloop() metódussal indítjuk a Tk feldolgozó hurkot.

Lássuk a Jatek osztályt, és részleteit:

class Jatek:
    def __init__(self):
        self.frame = Tk() # gyökér keret
        self.frame.title("Hangman") # ablak címe
        # létrehozzuk a felületet, ami egy canvas az akasztott embernek,
        # egy label a tippeknek, és egy másik a szókiírónak, meg egy indítás gomb,
        # egy label segédszövegnek, és egy a nyerési-vesztési szövegnek

        # a gyökérkeretünket két oszlopra osztjuk, a balba rakjuk a canvast
        # a jobb oldaliba pedig egy segédkeretet, amibe belepakoljuk a labeleket
        self.rajz= Canvas(self.frame,width=300,height=400,bg='#fff',relief=RAISED)
        self.rajz.grid(row=0,column=0)
        self.jobboszlop = Frame(self.frame,padx=5,pady=5)
        self.jobboszlop.grid(row=0,column=1,sticky=N+S)
        self.tipplabel= Label(self.jobboszlop)
        self.szokiirolabel = Label(self.jobboszlop,font=("Helvetica",16,"bold"),fg="#009800",pady=10)
        self.segedszoveglabel = Label(self.jobboszlop,text="A kezdéshez kattints az\n\n\"Start\"\n\ngombra.",padx=15,pady=15)
        self.nyertszoveg = Label(self.jobboszlop,pady=10,font=("Helvetica",14),fg="#F26F10")
        self.startgomb = Button(self.jobboszlop,padx=10,pady=5,text="Start",command=self.start)
        self.startgomb.grid(row=0,column=0)
        self.segedszoveglabel.grid(row=1,column=0)
        self.akasztofa = Akasztofa(self.rajz)
        self.szavak=Szavak()
        self.szokiirolabel.grid(row=2,column=0)
        self.tipplabel.grid(row=3,column=0)
        self.nyertszoveg.grid(row=4,column=0)

A konstruktorban létrehozzuk mindazokat az elemeket, amelyek a megjelenítéshez szükségesek, valamint a konzolos Hangman-hez hasonlóan a Szavak és Akasztofa egy-egy példányát (ne feledjük, itt az Akasztofa osztályunk nem azonos a régivel).
A következő metódusok képezik még a Jatek osztályunk részét:

    def kilepes(self):
        self.frame.quit()

    def start(self):
        self.segedszoveglabel.configure(text="Gondoltam egy szóra, próbáld kitalálni!")
        self.startgomb.configure(command=self.ujrakezdes,text="Új játék")
        self.vege=False
        self.nyert=False
        self.szo=unicode(self.szavak.valaszt(),'utf-8')
        self.eredmeny=[0]*len(self.szo)
        self.hiba=0
        self.maxhiba=7
        self.tippek=[]
        self.szokiir()
        self.tippkiir()
        self.frame.bind_all('',self.beker)

    def beker(self,event):
        tipp=unicode(event.char,'utf-8')
        if tipp in self.szo:
            for c in range(len(self.szo)):
                if tipp==self.szo[c]:
                    self.eredmeny[c]=1
            if self.eredmeny.count(1)==len(self.szo):
                self.vege=self.nyert=True
        else:
            self.hiba+=1
            self.akasztofa.rajzol(self.hiba)
            if self.hiba>=self.maxhiba:
                self.vege=True
        self.tippek.append(tipp)
        self.szokiir()
        self.tippkiir()
        if self.vege:
            self.frame.unbind_all('')
            if self.nyert:
                self.nyertszoveg.configure(text="Gratulálok! Kitaláltad!")
            else:
                self.nyertszoveg.configure(text=u"Vesztettél!\nÉrezd magad felakasztva :D\nA megfejtés: "+self.szo)

    def szokiir(self):
        szo=" "
        for c in range(len(self.szo)):
            if self.eredmeny[c]==0:
                szo+="_ "
            else:
                szo+=self.szo[c]+" "
        self.szokiirolabel.configure(text=szo)

    def tippkiir(self):
        self.tipplabel.configure(text="Eddigi tippjeid: %s" % ", ".join(self.tippek))

    def ujrakezdes(self):
        self.segedszoveglabel.configure(text="A kezdéshez kattints az\n\n\"Start\"\n\ngombra.")
        self.startgomb.configure(command=self.start,text="Start")
        self.szokiirolabel.configure(text="")
        self.tipplabel.configure(text="")
        self.nyertszoveg.configure(text="")
        self.akasztofa.reset()

A kilepes metódust a miheztartás végett tettem be, ha netán teszek egy külön kilépés gombot a felületre, de végül nem tettem, így végülis ez feleslegesen maradt benn.
A start metódus átírja a "Start" gombot "Új játék"-ra, és az ujrakezdes metódust rendeli hozzá. Beállít pár változót, és választ egy szót a szókészletből, majd hozzárendeli a beker metódust a gombnyomás eseményhez.
A beker metódusunk aktivizálódik tehát minden gombnyomáskor. Megnézi a kapott karaktert, majd a tippnek megfelelően módosítja a kiírnivalókat (a szokiir és tippkiir metódusok segítségével), és az akasztófarajzot (self.akasztofa.rajzol(self.hiba) ). Ha a játék véget ér, kiírással értesíti a játékost, és eltávolítja a gombnyomás esemény kezelését.
Ezután a játék már csak az "Új játék" gombbal kezdhető újra. Az ujrakezdes metódusunk az alaphelyzetbe állítja az ablakot, és indulhat az egész újra.

És most lássuk az Akasztofa osztályunkat a Tkinter megoldáshoz igazítva:

class Akasztofa:
    def __init__(self,canvas):
        self.canvas = canvas
        self.elemek = [ [],
                        ['line',150,25,150,50,'black'],
                        ['circle',125,50,175,100,'#f00'],
                        ['line',150,100,150,160,'brown'],
                        ['line',150,115,110,140,'#003700'],
                        ['line',150,115,190,140,'#003700'],
                        ['line',150,160,125,220,'blue'],
                        ['line',150,160,175,220,'blue'],
                        ]
        self.alap()

    def reset(self):
        elemek=self.canvas.find_all()
        for i in elemek:
            self.canvas.delete(i)
            self.alap()

    def alap(self):
        self.canvas.create_line(150,25,275,25,275,350,width=8,fill="#4E1B01",capstyle=ROUND,joinstyle=ROUND)
        self.canvas.create_line(285,350,50,350,width=8,fill="#4E1B01",capstyle=ROUND,joinstyle=ROUND)
        self.canvas.create_line(245,25,275,55,width=8,fill="#4E1B01",capstyle=ROUND,joinstyle=ROUND)

    def rajzol(self,level):
        e=self.elemek[level]
        if e[0]=='line':
            self.canvas.create_line(e[1],e[2],e[3],e[4],fill=e[5],width=4,capstyle=ROUND,joinstyle=ROUND)
        elif e[0]=='circle':
            self.canvas.create_oval(e[1],e[2],e[3],e[4],outline=e[5],width=4)

Konstruktorunk osztályváltozóvá teszi a kapott canvast, és az elemek listába felsorolja a kirajzolandó elemek adatait, majd meghívja az alap metódust az akasztófa kirajzolására (egyszerű vonalakról van szó). A reset metódus feladata a játék újrakezdésekor a canvas törlése. A rajzol metódusunk a kapott hibaszámnak megfelelő elemet rajzolja a vászonra, az elemek listában lévő adatok szerint.

Hát ennyi. Remélem érthető, ha mégsem, tegyétek fel a kérdéseiteket bátran.

Nincsenek megjegyzések:

Megjegyzés küldése