Most folytatjuk az előző bejegyzésben elkezdett Wordle játék asztali változatának elkészítését.
Először a játékot megjelenítő grafikus felületet gondoljuk át, és kódoljuk le a tkinter modul szolgáltatásait igénybe véve.
A játékterület lényegében egy olyan táblázat megrajzolását igényli, amelynek 5 oszlopa és alapban 8 sora van, de ez utóbbi legyen beállítható. Ezt egy keret grafikus elemmel (Frame widget) valósítjuk meg, és helyezünk le a pack() metódussal a főablakban. E kereten belül hozzuk létre a táblázatot úgy, hogy 5*8 db kis keret elemet helyezünk le táblázatos elrendezésben a grid() metódussal. E kis keretek azért kellenek, hogy a cella méreteit tervezhetően be tudjuk négyzet alakra állítani. Az egyes cellákba kell, hogy tudjunk írni, ezért beviteli mezőket (Entry widget) hozunk létre és helyezünk le a kis keretekbe a pack() metódussal.
Minden egyes beviteli mezőhöz egy kontrollváltozót rendelünk, amelyeket egy szótár értékeiként tartunk nyilván, amely értékek kulcsai a táblázat megfelelő sor- és oszlopindexeiből képzett tuple objektumok.
/E grafikus felület kialakítása sok hasonlóságot mutat egy korábbi bejegyzésben szereplő Sudoku táblázatéval/
A játék elindítása után a beviteli mezők – az első sor kivételével – csak olvasható állapotban vannak, és a következő sor csak akkor válik írhatóvá, ha a tippelésünket egy nyomógomb lenyomásával rögzítettük, és a tippelt szó kiértékelése sikeresen megtörtént.
A mindenkor aktuális táblázatsort, vagyis amelybe a tippelt szót írhatjuk, egy, a sor indexét tároló változóval tartjuk nyilván.
A táblázatba bevitt tartalom – a tippelt betűk – feldolgozásra küldéséhez egy nyomógomb vezérlőelemet hozunk létre és helyezünk el a pack() metódussal a főablakban a táblázat alatt. E gomb megnyomására a játékmenet() nevű függvény lesz meghívva, amely kiértékeli a tippelt szót és ennek megfelelően vezérli a játékot.
Az alábbiakban láthatjuk a fenti elvek alapján megvalósított grafikus felület kódolását, illetve a teljes játékot:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
from itertools import product, count from pathlib import Path from random import randint import tkinter as tk import tkinter.messagebox as msgbox szavak = Path(r'D:\magyar_szavak_wordle.txt').read_text('utf8').split('\n') szavak5 = [szó.lower() for szó in szavak if len(szó) == 5] # Random módon kiválasztunk egy szót az ötbetűsekből. rejtett = szavak5[randint(0, len(szavak5))].lower() def találati_indexek(szó: str, betű: str) -> set: """Egy olyan halmazzal tér vissza, amely azon indexértékeket tartalmazza, ahol az adott szóban a megadott betű előfordul""" return {i for i in range(len(szó)) if szó[i] == betű} def tippelés_kiértékelése(rejtett_szó: str, tippelt_szó: str): # Meghatározzuk és eltároljuk a rejtett és tippelt szóban közös betűket. közös_betűk = set(rejtett_szó) & set(tippelt_szó) # Meghatározzuk és eltároljuk, hogy a rejtett szóban az egyes közös betűk milyen indexpozíciókban vannak. d_rejtett = {b: találati_indexek(rejtett_szó, b) for b in közös_betűk} # Meghatározzuk és eltároljuk, hogy a tippelt szóban az egyes közös betűk milyen indexpozíciókban vannak. d_tipp = {b: találati_indexek(tippelt_szó, b) for b in közös_betűk} # Alapban a tippelt szó minden pozíciójához a szürke színt rendeljük, amit utána módosítunk, ha # van zöld vagy sárga pozíció. tipp_színek = ['grey'] * 5 # A tippelt szóban a közös betűk zöld és sárga poziciók meghatározása. for közös_betű in közös_betűk: # A tippel szóban az adott közös betű zöld indexpozícióinak meghatározása. egyező_indexek_rejtett_tipp = d_tipp.get(közös_betű) & d_rejtett.get(közös_betű) for zöld_index in egyező_indexek_rejtett_tipp: tipp_színek[zöld_index] = 'light green' # Az egyező, zöld indexeket kivesszük az adott közös betűhöz tartozó indexhalmazokból. maradó_indexek_rejtett = d_rejtett.get(közös_betű) - egyező_indexek_rejtett_tipp maradó_indexek_tipp = d_tipp.get(közös_betű) - egyező_indexek_rejtett_tipp # A tippel szóban az adott közös betű sárga indexpozícióinak meghatározása. for _ in zip(maradó_indexek_rejtett, maradó_indexek_tipp): sárga_index = d_tipp.get(közös_betű).pop() tipp_színek[sárga_index] = 'yellow' return tipp_színek def játékmenet(wordle_keret, mezők_és_változók: dict, aktuális_sorindex, rejtett_szó, magyar_5betűs_szavak:list): # A beviteli mezőkbe írt betűkből a tippelt szó előállítása. tippelt_szó: str = ''.join([mezők_és_változók.get(t).get() for t in mezők_és_változók if t[0] == aktuális_sorindex]) # Ha az utolsó sorba írt szó helytelen, akkor egy tájékoztató üzenetablak jön fel, majd # annak bezárása után kilépünk az alkalmazásból. if aktuális_sorindex == 7 and rejtett_szó != tippelt_szó: msgbox.showinfo('SAJNOS NEM SIKERÜLT', f'A helyes megoldás ez lett volna: {rejtett_szó.upper()}') root.destroy() return # Ellenőrizzük, hogy a tipp érvényes ötbetűs magyar szó-e. Ha nem, akkor egy üzenetablak figyelmeztet erre. # Ennek bezárása után az aktuális tippsorban új szó beírásával próbálkozhatunk. if tippelt_szó.lower() not in magyar_5betűs_szavak: msgbox.showerror('ÉRVÉNYTELEN SZÓ', 'A megadott szó ragozott vagy nincs az érvényes szavak listájában. ' 'Ugyanebben a sorban próbálkozzon egy másik szóval!') return # A tippelt szó betűinek találati kiértékelése, és ennek megfelelő színek neveinek visszaadása egy listában. tippelt_betűk_színei = tippelés_kiértékelése(rejtett_szó, tippelt_szó) # Az aktuális tippsor cellaszíneinek beállítása. betűszínek_beállítása(wordle_keret, tippelt_betűk_színei, aktuális_sorindex) # Ha eltaláltuk a rejtett szót, akkor erről egy üzenetablak tájékoztat. # Ennek bezárása után kilépünk az alkalmazásból. if tippelt_szó.lower() == rejtett_szó.lower(): msgbox.showinfo('HELYES TIPP', 'Gratulálunk!') root.destroy() return # Ha nem sikeres a tipp, akkor a következő sort írhatóvá tesszük, majd # eggyel megnöveljük az aktuális sor indexét tartalmazó változó értékét. tippsor_feloldása(wordle_keret, aktuális_sorindex + 1) aktuális_sorindex_mutató.set(aktuális_sorindex + 1) def beviteli_mező(wordle_keret, sorindex, oszlopindex): """Az adott sor- és oszlopindexű beviteli mező kinyerése""" cella_keret = wordle_keret.grid_slaves(row=sorindex, column=oszlopindex)[0] return cella_keret.winfo_children()[0] def betűszínek_beállítása(wordle_keret, tipp_színek: list, tippsorindex: int): """Az adott indexű tippsor mezőszíneinek beállítása""" for oszlopindex in range(5): beviteli_mező(wordle_keret, tippsorindex, oszlopindex).config(bg=tipp_színek[oszlopindex]) def tippsor_feloldása(wordle_keret, tippsorindex: int): """Az adott indexű tippsor mezőinek írhatóvá tétele""" for oszlopindex in range(5): beviteli_mező(wordle_keret, tippsorindex, oszlopindex).config(state=tk.NORMAL) # ----- Magyar Wordle GUI készítése ------ root = tk.Tk() root.title('Wordle - magyar') root.geometry('300x450') wordle_hu_keret = tk.Frame(root, bg='grey75') sorok_száma = 8 # Ezzel változtathatjuk a tippelési sorok számát. # Az egyes cellák beviteli mezőinek kontrollváltozói. mezők_változói = {(si, oi): tk.StringVar(root) for si, oi in product(range(sorok_száma), range(5))} # Az írható cellákat tartalmazó táblázat elkészítése. for si, oi in product(range(sorok_száma), range(5)): cell_fr = tk.Frame(wordle_hu_keret, bg='white', width=40, height=40) cell_fr.grid(row=si, column=oi) cell_fr.propagate(False) ent = tk.Entry(cell_fr, textvariable=mezők_változói.get((si, oi)), font=('Consolas', 24, 'bold'), width=1, justify=tk.CENTER, state='readonly', readonlybackground='grey85') ent.pack(fill=tk.BOTH) # A mindenkor aktuális tippsor indexét tároló változó. aktuális_sorindex_mutató = tk.IntVar(root, 0) # Az első sor írhatóvá tétele kezdéskor. tippsor_feloldása(wordle_hu_keret, aktuális_sorindex_mutató.get()) # A tippelt szó beírt betűinek ellenőrzését és színek szerinti kiértékelését indító nyomógomb. tipp_btn = tk.Button(root, text='TIPP RÖGZÍTÉS', command=lambda: játékmenet(wordle_hu_keret, mezők_változói, aktuális_sorindex_mutató.get(), rejtett, szavak5)) # A táblázat és nyomógomb lepakolása. wordle_hu_keret.pack(pady=20) tipp_btn.pack(pady=20) root.mainloop() |
Mivel a játékmenet vezérlését végző függvénynek központi szerepet van a program működési logikájában, ezért az alábbiakban ennek működését részletezzük.
A játékmenet() függvény irányítja a bevitt adatok feldolgozását, és ennek alapján a grafikus elemek jellemzőinek változtatását. E feladatát több másik függvény hívásával teljesíti. Ebből kettőt felette, hármat alatta látunk. A két felette levő függvény szerepét az előző bejegyzésben láttuk. A három másik függvény közül a beviteli_mező() egy segédfüggvény, amelyet az alatt levő másik kettő használ, és amely a táblázat adott sor- és oszlopindexével azonosított cellájához tartozó beviteli mező típusú grafikus elemet adja vissza. A betűszínek_beállítása() függvény egy adott sorban szereplő beviteli mezők háttérszínét, a tippsor_feloldása() függvény pedig a mezők állapotát módosítja a csak olvasható állapotból normál állapotba váltással, ami lehetővé teszi, hogy írni lehessen a mezőbe.
A játékmenet() függvény a következőket végzi:
- A beviteli mezőkbe írt betűkből előállítja a tippelt szót karakterlánc formában.
- Ha a játéktábla rendelkezésre álló utolsó sorába írt szó helytelen, akkor nem sikerült megnyerni a játékot. Ekkor egy tájékoztató üzenetablakot dob fel, amely erről tájékoztat, és kiírja, hogy mi volt a kitalálandó szó. Az ablak bezárása után kilép az alkalmazásból.
- Ha még nem vagyunk az utolsó sorban, akkor ellenőrzi, hogy a tipp érvényes ötbetűs magyar szó-e. Ha nem, akkor egy üzenetablak figyelmeztet erre. Ennek bezárása után az aktuális tippsorban új szó beírásával próbálkozhatunk.
- Ha a tippelt szó szerepel az ötbetűs magyar szavakat tároló listában, akkor meghívja a tippelés_kiértékelése() függvényt, amelynek visszatérési értéke egy ötelemű lista, amelyben az egyes indexekhez tartozó elemek a betűk megfelelősége szerinti színek nevei. E nevek már olyan karakterláncok, amelyek közvetlenül alkalmazhatók a mezők háttérszínét meghatározó konfigurációs paraméter értékeként.
- A színek neveinek ismeretében beállítja az aktuális tippsor mezőinek háttérszíneit a betűszínek_beállítása() függvény meghívásával.
- Ha sikerült eltalálni a rejtett szót, akkor erről egy üzenetablak tájékoztat. Ennek bezárása után kilép az alkalmazásból.
- Ha nem sikeres a tipp, akkor a következő sort írhatóvá teszi a tippsor_feloldása() függvény meghívásával, majd eggyel megnöveli az aktuális sor indexét tartalmazó változó értékét.
Az így elkészített alkalmazás elindítása után lefutó játszma bizonyos fázisainak képernyőképeit láthatjuk az alábbi képen.

Aki kedveli ezt a játékot, és szeretné többször egymás után játszani, annak érdemes megírni ilyen asztali változatban, mert az online webes változatokban többnyire korlátozott a napi játékok száma.
A programozási feladatoknak általában több megoldása lehetséges. A most bemutatott programkód is csak egy a lehetséges megvalósítások közül. Érdemes lehet elgondolkodni más kódstruktúrán (pl. osztályba helyezni az alkalmazást és metódusokként definiálni a függvényeket), illetve más feldolgozási logikát kigondolni (pl. nem halmazműveleteken alapulót). Lehet, hogy találunk egyszerűbbet vagy jobbat, de ha mégsem, amit mindenképp nyerünk az a problémamegoldó képességünk és a nyelvi jártasságunk fejlődése.
Ugyanezeket erősíthetjük, ha továbbfejlesztjük a programot. Csak példaként: nyerés esetén ne záródjon be, hanem egy gomb megnyomásával új játék legyen indítható; legyen lehetőség a játékidő mérésére; lehessen az ötbetűs magyar szavak listáját tartósan bővíteni a beírt, de nem elfogadott szóval, stb.
Kellemes játékot!
Ha a programban alkalmazott valamely nyelvi elem és szerkezet nem lenne ismert, vagy használata pontos módjában bizonytalanok lennénk, ismertetésük a Python tudásépítés lépésről lépésre című e-könyvben példákkal együtt megtalálható.