diff --git a/snippets/1001_MastermindTdd/__pycache__/logic.cpython-314.pyc b/snippets/1001_MastermindTdd/__pycache__/logic.cpython-314.pyc new file mode 100644 index 0000000..12cd8c9 Binary files /dev/null and b/snippets/1001_MastermindTdd/__pycache__/logic.cpython-314.pyc differ diff --git a/snippets/1001_MastermindTdd/__pycache__/test_logic.cpython-314-pytest-9.0.2.pyc b/snippets/1001_MastermindTdd/__pycache__/test_logic.cpython-314-pytest-9.0.2.pyc new file mode 100644 index 0000000..a16ce47 Binary files /dev/null and b/snippets/1001_MastermindTdd/__pycache__/test_logic.cpython-314-pytest-9.0.2.pyc differ diff --git a/snippets/1001_MastermindTdd/game.py b/snippets/1001_MastermindTdd/game.py new file mode 100644 index 0000000..fb3d93f --- /dev/null +++ b/snippets/1001_MastermindTdd/game.py @@ -0,0 +1,136 @@ +import random +from logic import tipp_kiszamolasa + +# Elérhető színek +SZINEK = ["szurke", "piros", "narancs", "sarga", "zold", "pink", "lila", "kek", "fekete", "feher"] +MAX_KOROK = 12 + +def welcome(): + """Üdvözlő üzenet és játékszabályok""" + print("=" * 60) + print(" " * 20 + "MASTERMIND JÁTÉK") + print("=" * 60) + print("\nÜdvözöllek a Mastermind játékban!") + print(f"\nElérhető színek ({len(SZINEK)} db):") + for i, szin in enumerate(SZINEK, 1): + print(f" {i}. {szin}", end=" " if i % 3 != 0 else "\n") + print("\n\nCélod: Találd ki a 4 színből álló titkos kombinációt!") + print(f"Neked {MAX_KOROK} körödön van tippelni.\n") + print("Válaszban kapod:") + print(" - FEKETE tüskék: hány szín van jó helyen") + print(" - FEHÉR tüskék: hány szín van rossz helyen\n") + print("Adj meg 4 színt szóközzel elválasztva!") + print("Például: piros kek zold sarga\n") + print("=" * 60 + "\n") + +def generalt_felallas(): + """Véletlenszerű felállás generálása""" + return [random.choice(SZINEK) for _ in range(4)] + +def beker_tippet(kor): + """Tipp bekérése a felhasználótól""" + while True: + try: + print(f"[{kor}. kör] Add meg a tipped: ", end="") + tipp_str = input().strip().lower() + + if not tipp_str: + print("❌ Üres tipp! Próbáld újra.") + continue + + tipp = tipp_str.split() + + if len(tipp) != 4: + print(f"❌ Pontosan 4 színt kell megadnod! Te {len(tipp)} színt adtál meg.") + continue + + # Ellenőrizzük, hogy minden szín érvényes-e + hibas_szinek = [szin for szin in tipp if szin not in SZINEK] + if hibas_szinek: + print(f"❌ Érvénytelen szín(ek): {', '.join(hibas_szinek)}") + print(f" Elérhető színek: {', '.join(SZINEK)}") + continue + + return tipp + except KeyboardInterrupt: + print("\n\n👋 Játék megszakítva. Viszlát!") + exit(0) + except Exception as e: + print(f"❌ Hiba: {e}") + continue + +def eredmeny_kiiras(fekete, feher): + """Eredmény szép megjelenítése""" + print(f" Eredmény: ", end="") + + # Fekete tüskék + if fekete > 0: + print(f"🔴 {fekete} fekete", end="") + + # Fehér tüskék + if feher > 0: + if fekete > 0: + print(", ", end="") + print(f"⚪ {feher} fehér", end="") + + # Ha egyik sincs + if fekete == 0 and feher == 0: + print("❌ Nincs találat", end="") + + print() + +def jatek(): + """Fő játék logika""" + welcome() + + # Titkos felállás generálása + felallas = generalt_felallas() + # print(f"[DEBUG] Titkos kód: {' '.join(felallas)}") # Debug célra + + # Játék ciklus + for kor in range(1, MAX_KOROK + 1): + tipp = beker_tippet(kor) + + try: + fekete, feher = tipp_kiszamolasa(felallas, tipp, SZINEK) + eredmeny_kiiras(fekete, feher) + + # Győzelem ellenőrzése + if fekete == 4: + print("\n" + "=" * 60) + print(f"🎉 GRATULÁLOK! Kitaláltad {kor} körből!") + print(f"A titkos kód: {' '.join(felallas).upper()}") + print("=" * 60) + return True + + print() # Üres sor a következő kör előtt + + except ValueError as e: + print(f"❌ Hiba: {e}") + continue + + # Ha elfogytak a körök + print("\n" + "=" * 60) + print("😞 Sajnos elfogytak a köreid!") + print(f"A titkos kód: {' '.join(felallas).upper()}") + print("=" * 60) + return False + +def main(): + """Főprogram - újrajátszás kezelése""" + while True: + jatek() + + print("\nSzeretnél újra játszani? (i/n): ", end="") + try: + valasz = input().strip().lower() + if valasz not in ['i', 'igen', 'y', 'yes']: + print("\n👋 Köszönöm a játékot! Viszlát!") + break + print("\n" + "=" * 60 + "\n") + except KeyboardInterrupt: + print("\n\n👋 Viszlát!") + break + +if __name__ == "__main__": + main() diff --git a/snippets/1001_MastermindTdd/jatekfelulet.png b/snippets/1001_MastermindTdd/jatekfelulet.png new file mode 100644 index 0000000..515feae Binary files /dev/null and b/snippets/1001_MastermindTdd/jatekfelulet.png differ diff --git a/snippets/1001_MastermindTdd/logic.py b/snippets/1001_MastermindTdd/logic.py new file mode 100644 index 0000000..b6fe0d6 --- /dev/null +++ b/snippets/1001_MastermindTdd/logic.py @@ -0,0 +1,59 @@ +def tipp_kiszamolasa(felallas, tipp, szinek): + """ + Mastermind játék logikája - a tipp kiértékelése. + + Args: + felallas: A titkos szín lista (4 szín) + tipp: A tippelt szín lista (4 szín) + szinek: Az érvényes színek listája + + Returns: + Tuple: (helyes_pozició_szám, helyes_szín_rossz_pozició_szám) + + Raises: + ValueError: Ha érvénytelen szín vagy rossz hosszú a tipp + """ + from collections import Counter + + # Validáció + if len(tipp) != 4: + raise ValueError("A tippnek pontosan 4 szín kell, hogy tartalmazzon") + + for szin in tipp: + if szin not in szinek: + raise ValueError(f"Érvénytelen szín: {szin}") + + # Szín előfordulások számolása + tipp_szamok = Counter(tipp) + felallas_szamok = Counter(felallas) + + # 1. lépés: Pontos pozíció egyezések keresése (fekete tüskék) + helyes_pozicio = 0 + felallas_maradek = [] + tipp_maradek = [] + + for i in range(4): + # Pontos egyezést csak akkor számítunk, ha: + # - a pozíció egyezik ÉS + # - a szín felállásban pontosan 1x szerepel VAGY tippben és felállásban ugyanannyiszor + if tipp[i] == felallas[i]: + if felallas_szamok[tipp[i]] == 1 or tipp_szamok[tipp[i]] == felallas_szamok[tipp[i]]: + helyes_pozicio += 1 + else: + # Ha nem számít fekete tüskének, maradékhoz adjuk + felallas_maradek.append(felallas[i]) + tipp_maradek.append(tipp[i]) + else: + # Nincs pozíció egyezés, maradékhoz adjuk + felallas_maradek.append(felallas[i]) + tipp_maradek.append(tipp[i]) + + # 2. lépés: Rossz pozícióban lévő szín egyezések keresése (fehér tüskék) + helyes_szin_rossz_pozicio = 0 + + for szin in tipp_maradek: + if szin in felallas_maradek: + helyes_szin_rossz_pozicio += 1 + felallas_maradek.remove(szin) + + return (helyes_pozicio, helyes_szin_rossz_pozicio) diff --git a/snippets/1001_MastermindTdd/mastermind_esettanulmany.md b/snippets/1001_MastermindTdd/mastermind_esettanulmany.md new file mode 100644 index 0000000..6559eb7 --- /dev/null +++ b/snippets/1001_MastermindTdd/mastermind_esettanulmany.md @@ -0,0 +1,43 @@ +--- +layout: post +title: Mastermind fejlesztés TDD módszerrel és AI segítségével +tags: ai python tdd mastermind logic +author: Szakszon Ádám +--- + +# Mastermind Esettanulmány + +## Bevezetés +Ebben a projektben egy klasszikus Mastermind játék motorját készítettem el. A célom az volt, hogy megtanuljam a **TDD (Test-Driven Development)** folyamatát AI (GitHub Copilot) támogatással. + +## Fejlesztési módszertan +A fejlesztés során nem a kódot írtam meg először, hanem a teszteseteket a `pytest` keretrendszerben. + +### Tesztek típusai: +* **Hibaágak:** Érvénytelen színek és rossz tipp-hossz kezelése. +* **Logikai ágak:** Fekete és fehér tüskék számolása, különös tekintettel a duplikált színekre. + + +## Algoritmus leírása +A `tipp_kiszamolasa` függvény két lépésben dolgozik: +1. Megkeresi a pontos egyezéseket (fekete tüskék). +2. A maradék színekből kiszámolja a benne lévő, de rossz helyen lévő színeket (fehér tüskék). + + + +## Érdekes Promptok + +A fejlesztés során az AI-t két lépcsőben irányítottam. Először megadtam neki a teljes tesztkészletet, hogy az alapján generálja le a logikát: + +> "Szia! Kérlek, írd meg a `logic.py` tartalmát a `test_logic.py`-ban található tesztjeim alapján. A feladat a `tipp_kiszamolasa(felallas, tipp, szinek)` függvény megírása. Fontos, hogy a kód minden tesztesetnek megfeleljen." + +Később, amikor az AI túl bonyolult algoritmust (felesleges feltételeket) próbált bevezetni, szükség volt egy pontosító, visszaterelő utasításra: + +> "Térjünk vissza az alapokhoz: először egy ciklussal számoljuk össze az összes pontos egyezést, majd a maradékból határozzuk meg a rossz helyen lévő színeket. Ne bonyolítsd túl a logikát!" + +## Eredmények +A tesztek lefuttatása után az alábbi eredményt kaptam: +![Passed Tests](teszt.png) + +Aztán kértem egy konzolon tesztelhető felületett is az AI-tól: +![Konzol](jatekfelulet.png) \ No newline at end of file diff --git a/snippets/1001_MastermindTdd/test_logic.py b/snippets/1001_MastermindTdd/test_logic.py new file mode 100644 index 0000000..78fd4c2 --- /dev/null +++ b/snippets/1001_MastermindTdd/test_logic.py @@ -0,0 +1,75 @@ +import pytest +from logic import tipp_kiszamolasa + +szinek = ["szurke", "piros", "narancs", "sarga", "zold", "pink", "lila", "kek", "fekete", "feher" ] + +def test_ervenytelen_szin(): + with pytest.raises(ValueError): + tipp = ["sotetkek", "kek", "kek", "kek"] + felalas = ["piros", "kek", "zold", "sarga"] + tipp_kiszamolasa(felalas, tipp, szinek) + +def test_tulsok_szin(): + with pytest.raises(ValueError): + tipp = ["zold", "kek", "kek", "kek", "piros"] + felalas = ["piros", "kek", "zold", "sarga"] + tipp_kiszamolasa(felalas, tipp, szinek) + + +def test_tulkeves_szin(): + with pytest.raises(ValueError): + tipp = ["zold", "kek", "kek",] + felalas = ["piros", "kek", "zold", "sarga"] + tipp_kiszamolasa(felalas, tipp, szinek) + + + +def test_nincs_talalat(): + tipp = ["szurke", "piros", "narancs", "sarga"] + felalas = ["lila", "kek", "fekete", "feher"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (0,0) + +def test_johely(): + tipp = ["zold", "kek", "szurke", "piros"] + felalas = ["lila", "kek", "fekete", "feher"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (1,0) + +def test_benne(): + tipp = ["zold", "szurke", "piros", "kek"] + felalas = ["lila", "kek", "fekete", "feher"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (0,1) + +def test_benne_johely(): + tipp = ["zold", "szurke", "piros", "kek"] + felalas = ["szurke", "lila", "piros", "kek"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (2,1) + +def test_duplikaltszin_johely(): + tipp = ["kek", "kek", "szurke", "piros"] + felalas = ["lila", "kek", "fekete", "feher"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (1,0) + +def test_duplikaltszin_benne(): + tipp = ["kek", "szurke", "piros", "kek"] + felalas = ["lila", "kek", "fekete", "feher"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (0,1) + +def test_duplikaltszin_benne_tobb(): + tipp = ["kek", "szurke", "kek", "kek"] + felalas = ["lila", "kek", "kek", "feher"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (0,2) + +def test_duplikaltszin_johely_tobb(): + tipp = ["kek", "kek", "szurke", "piros"] + felalas = ["kek", "kek", "fekete", "piros"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (3,0) + +def test_duplikaltszin_vegyes(): + tipp = ["kek", "kek", "szurke", "piros"] + felalas = ["kek", "kek", "piros", "piros"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (2,1) + +def test_siker(): + tipp = ["zold", "szurke", "piros", "kek"] + felalas = ["zold", "szurke", "piros", "kek"] + assert tipp_kiszamolasa(felalas, tipp, szinek) == (4,0) \ No newline at end of file diff --git a/snippets/1001_MastermindTdd/teszt.png b/snippets/1001_MastermindTdd/teszt.png new file mode 100644 index 0000000..0d246c2 Binary files /dev/null and b/snippets/1001_MastermindTdd/teszt.png differ