Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions snippets/1001_MastermindTdd/game.py
Original file line number Diff line number Diff line change
@@ -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()
48 changes: 48 additions & 0 deletions snippets/1001_MastermindTdd/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
layout: post
title: Mastermind fejlesztés Test First módszerrel és AI segítségével
tags: python testfirst mieset
author: Szakszon Ádám Dániel
---

# 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 **Test First** módszertan folyamatát AI (GitHub Copilot és LLM modellek) támogatással, megvizsgálva, hogyan alakítja át az AI a tesztvezérelt megközelítést.

## Fejlesztési módszertan
A fejlesztés során nem a produkciós kódot írtam meg először, hanem a teljes tesztkészletet határoztam meg a `pytest` keretrendszerben. Ez a gyakorlatban tökéletes specifikációként szolgált az AI számára.

### 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ületet is az AI-tól:
![Konzol](jatekfelulet.png)

## Tanulságok: AI asszisztált Test First vs. TDD

A projekt legfőbb tanulsága a klasszikus **TDD (Test-Driven Development)** és az AI-jal támogatott **Test First** megközelítés közötti markáns különbség megtapasztalása volt.

A klasszikus TDD kis lépésekben halad (Red-Green-Refactor): írunk egyetlen bukó tesztet, megírjuk hozzá a minimális kódót, ami átviszi, majd refaktorálunk és jöhet a következő teszt. Ezzel szemben az AI asszisztens bevonásakor sokkal hatékonyabbnak bizonyult a teljes tesztkészlet előzetes megírása. Az AI számára ez a tesztkészlet egy rendkívül egzakt specifikációként szolgált, amiből egyetlen nagy lépésben ("megkértem rá, megoldotta, kész") képes volt legenerálni a működő üzleti logikát.

Bár így a fejlesztés sebessége drasztikusan megnőtt, a módszer rávilágított egy veszélyre is: elvész a hagyományos TDD által biztosított inkrementális kontroll. Ha az AI elsőre rossz, túlbonyolított irányba indul el (ahogy ez a promptoknál is látható volt), sokkal nehezebb egy nagy blokknyi komplex logikát utólag kibogozni és refaktorálni, mint lépésről lépésre haladni. Mérnökként a feladat így a szintaxis írásáról áttevődött a nagyon precíz teszt-specifikációk elkészítésére és az AI által generált kód kritikus validálására.
Binary file added snippets/1001_MastermindTdd/jatekfelulet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions snippets/1001_MastermindTdd/logic.py
Original file line number Diff line number Diff line change
@@ -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)
75 changes: 75 additions & 0 deletions snippets/1001_MastermindTdd/test_logic.py
Original file line number Diff line number Diff line change
@@ -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)



Comment on lines +24 to +26

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

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)
Binary file added snippets/1001_MastermindTdd/teszt.png

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A screenshot alján ha lehet, ne legyen félig ott maradt sor. Vagy látszódjon teljesen, vagy vágd le teljesen.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions snippets/1011_cashregisterTdd/TddShop.Core/Costumer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace TddShop.Core;

public class Costumer
{
public string Id { get; }
public double wallet { get; set; }

public Costumer(string id)
{
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentException("Costumer ID must not be empty.");

Id = id;
wallet = 0;
}

public void setwallet(double amount)
{
if (amount < 0)
throw new ArgumentException("Wallet amount must be non-negative.");

wallet = amount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TddShop.Core;

public interface IIllegalPurchaseNotifier
{
void NotifyIllegalPurchase(char product);
}
6 changes: 6 additions & 0 deletions snippets/1011_cashregisterTdd/TddShop.Core/IInventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TddShop.Core;

public interface IInventory
{
void Update(char product, int delta);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TddShop.Core;

public interface IMinorCustomerNotifier
{
void NotifyMinorCustomer();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TddShop.Core;

public interface IPaymentNotifier
{
void NotifySuccessfulPayment(double amount);
}
6 changes: 6 additions & 0 deletions snippets/1011_cashregisterTdd/TddShop.Core/IPosTerminal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TddShop.Core;

public interface IPosTerminal
{
void StartPayment(double amount);
}
6 changes: 6 additions & 0 deletions snippets/1011_cashregisterTdd/TddShop.Core/IScale.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TddShop.Core;

public interface IScale
{
double GetCurrentWeight();
}
Loading