Torpedó játék

A Torpedó (angol nyelvterületen Battleship) a legismertebb táblás játékok egyike, amely számítógépes formában is játszható gépi ellenféllel. Az alábbiakban egy tkinter alapú grafikus felületű megvalósítás kerül bemutatásra.

A működő játék elkészítésén túl a cél egy olyan, objektumorientált alkalmazás létrehozása volt, amely szemléltető példaként szolgálhat a Python számos nyelvi lehetőségének használatára, valamint az objektumorientált tervezés bemutatására, ahol világosan elkülönül a játéklogika és a felhasználói felület.

A teljes forráskód a cikk végén megtalálható, illetve GitHubon is elérhető itt: https://github.com/pythontudasepites/battleship

Elsőként a játék használatát ismertetjük, majd bemutatjuk az alkalmazás szerkezetét, működési logikáját és a főbb tervezési megfontolásokat. Ez utóbbi rész elsősorban azoknak szól, akik a program felépítése és a forráskód működése iránt is érdeklődnek.

A játék használata

A program a klasszikus Torpedójáték számítógépes változata, amelyben a játékos a számítógéppel mint gépi ellenféllel mérkőzik meg. A játék a hagyományos szabályokat követi: mindkét fél egy 10×10 mezős táblán helyezi el a flottáját, majd felváltva próbálják elsüllyeszteni egymás hajóit.

A játék indítása

A program elindítása után két játéktábla jelenik meg. A bal oldali tábla a játékos saját flottáját, a jobb oldali pedig az ellenfél rejtett hajóit reprezentálja. A játék kezdetén a program kiírja, hogy milyen méretű hajót kell éppen elhelyezni.

Hajók elhelyezése

A hajók elhelyezése egérkattintásokkal történik a saját táblán. A program mindig jelzi, hogy a következő hajónak hány mezőből kell állnia. A hajó részeit a kívánt mezőkre kattintva lehet kijelölni. A program minden kattintás után ellenőrzi, hogy az aktuális elrendezésből még kialakítható-e szabályos hajó, ezért érvénytelen mező nem választható ki.

A program csak a szabályos elhelyezést engedi meg:

  • a hajók kizárólag vízszintesen vagy függőlegesen helyezhetők el;
  • a hajótest nem tartalmazhat megszakítást;
  • a hajók egymással még a sarkukon sem érintkezhetnek.

Amikor egy hajó elkészült, hangjelzés hallható, majd a program automatikusan a következő hajó elhelyezésére kér.

Miután az összes hajó felkerült a táblára, a játék automatikusan átvált csata üzemmódba.

Csata

A játék során a jobb oldali táblán kell bal egérgombbal rákattintani arra a mezőre, amelyre lövést szeretnénk leadni.

A lövés eredménye azonnal megjelenik:

  • kis fekete pont: a lövés nem talált;
  • piros X szürke háttéren: találat érte a hajót, de az még nem süllyedt el;
  • fekete mező: a hajó teljes egészében elsüllyedt.

Az emberi játékos minden lövését automatikusan követi a számítógép válaszlépése, amely a saját táblán jelenik meg ugyanilyen jelölésekkel.

A játék vége

A játék addig tart, amíg valamelyik fél összes hajója el nem süllyed. A győztesről a program üzenetet jelenít meg.

Az ellenfél sértetlen hajóinak megjelenítése

A csata során a jobb egérgombbal a gép játékos még sértetlen hajóinak helye is megjeleníthető. Ez a funkció lehetővé teszi, hogy egy elvesztett játék után megtekintsük a még találatot nem kapott ellenséges hajók helyzetét, így könnyen visszanézhető, mely mezőket kellett volna még megtalálni a győzelemhez.

Képernyőképek

Az alábbi képek a játék néhány fontosabb állapotát mutatják be: a hajók elhelyezését, a csata közbeni megjelenítést és a játék végét.

Az alkalmazás szerkezete és működési logikája

A program fejlesztése során az elsődleges cél nem csupán egy működő Torpedó (Battleship) játék elkészítése volt, hanem egy olyan alkalmazás megvalósítása is, amelynek felépítése figyelembe veszi az objektumorientált tervezés alapelveit. Ennek megfelelően a játéklogika, a felhasználói felület és a játékot vezérlő eseménykezelés egymástól elkülönülő komponensekben kapott helyet.

A program felépítése

Az alkalmazás két modulból áll.

A model modul tartalmazza a játék teljes logikai modelljét: a játéktáblákat, a hajókat, a lövések feldolgozását, a gépi játékos működését és a játék állapotának kezelését. Ez a modul teljesen független a grafikus felülettől, ezért más – grafikus vagy konzolos – felhasználói felülettel is alkalmas a játék működtetésére.

A main modul valósítja meg a tkinter alapú grafikus felhasználói felületet. Feladata kizárólag a táblák megjelenítése, az egérkattintások kezelése és a modell által szolgáltatott eredmények megjelenítése. A játékszabályok ismerete kizárólag a modell feladata.

A felépítés a Model–View–Controller (MVC) architektúra egyszerűsített változatának tekinthető. A modell és a nézet felelősségi köre jól elkülönül egymástól, míg a vezérlő szerepét külön Controller osztály helyett a BattleshipGame alkalmazásobjektum látja el. Ez az objektum kezeli a felhasználói eseményeket, továbbítja a kéréseket a modell felé, majd a modell eredményei alapján frissíti a megjelenítést.

A játék modellje

A modell központi eleme a Board osztály, amely egy játékos teljes játéktábláját reprezentálja. Nyilvántartja a rajta elhelyezett hajókat, a korábban leadott lövéseket, valamint biztosítja a hajók elhelyezéséhez és a lövések kiértékeléséhez szükséges műveleteket.

A hajók önálló objektumokként jelennek meg. Egy Ship példány kizárólag az általa elfoglalt cellákat tárolja, ebből azonban minden fontos tulajdonsága meghatározható: mérete, tájolása, a találatok állapota, valamint az is, hogy elsüllyedt-e. A hajó saját maga végzi a rá leadott lövések kiértékelését, valamint biztosítja a saját geometriai tulajdonságainak ellenőrzését. A táblán való elhelyezhetőség vizsgálatában a Board objektummal együttműködve vesz részt

A koordináták kezelésére egy külön Cell adatosztály szolgál. Ennek használata egységesíti a cellák reprezentációját, így a koordináták egyszerű számpárok helyett jól definiált objektumként jelennek meg. Mivel a Cell változtathatatlan (frozen=True), használható halmazok elemeként és szótárkulcsként is, ami a program több algoritmusát jelentősen leegyszerűsíti.

Hajók elhelyezése

A gépi játékos hajóinak elhelyezését a ComputerFleetFactory végzi. Az algoritmus véletlenszerűen generál vízszintes vagy függőleges hajókat, és addig próbálkozik új pozíciókkal, amíg azok szabályosan elhelyezhetők. A hajók sem egymást nem fedhetik át, sem egymás közvetlen szomszédságába nem kerülhetnek.

Az emberi játékos hajóépítése ettől eltérő megközelítést alkalmaz. Minden kattintás után a program előállítja az összes olyan lehetséges hajót, amely

  • a megfelelő méretű,
  • tartalmazza a már kijelölt cellákat,
  • és szabályosan elhelyezhető a táblán.

Ha a kiválasztott cellákból legalább egy ilyen hajó létrehozható, akkor a kijelölés érvényes, ellenkező esetben a kattintást a program figyelmen kívül hagyja. Ez a megoldás egyszerű felhasználói kezelést biztosít, miközben folyamatosan biztosítja a szabályos hajóelhelyezést.

Lövések feldolgozása

A lövések kiértékelése két lépésben történik. A játéktábla objektum először meghatározza, hogy a célcellán található-e hajó. Találat esetén a hajó saját maga dönti el, hogy a lövés egyszerű találatot vagy teljes elsüllyedést eredményezett.

A feldolgozás eredménye a ShotResult felsorolástípus egy tagja lesz, amely egyértelműen megkülönbözteti a három lehetséges állapotot: mellélövés, találat vagy süllyedés.

A gépi játékos stratégiája

A gépi játékos lövési algoritmusa két egymást követő stratégiát alkalmaz. Amíg nem talál hajót, addig véletlenszerűen választ célpontot azok közül a mezők közül, amelyekre még nem lőtt, illetve amelyek biztosan nem tartoznak már elsüllyedt hajóhoz.

Találat után a stratégia megváltozik. Egyetlen sérült hajórész esetén a négy szomszédos mező valamelyikét választja célpontnak. Amint két egymás melletti találat meghatározza a hajó irányát, a keresés már kizárólag a hajó két végpontja felé folytatódik mindaddig, amíg a hajó el nem süllyed.

Tervezési megfontolások összegzése

A fejlesztés során a cél nem csupán a játék működésének megvalósítása volt, hanem egy jól áttekinthető és könnyen karbantartható programstruktúra kialakítása is. Ennek érdekében a különböző felelősségi körök önálló osztályokba kerültek, a koordináták kezelését egy külön adatosztály egységesíti, míg az algoritmusok több helyen a Python beépített adatszerkezeteire, különösen a halmazműveletekre épülnek. Ez a felépítés nemcsak a kód olvashatóságát javítja, hanem lehetővé teszi az egyes komponensek egymástól független fejlesztését és módosítását is.

A program megírásához szükséges ismeretek megtalálhatók a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyvben.

A teljes forráskód

Az alábbiakban a program forráskódja olvasható. A teljes projekt fájljai a GitHubon is elérhetők, így a játék közvetlenül kipróbálható, illetve a forrásfájlok letölthetők.

GitHub link: https://github.com/pythontudasepites/battleship

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.