Mastermind
Számkitaláló játék
|
A népszerű MasterMind játékot két játékos: Rejtő és Fejtő játssza.
0342
).3245
), megmutatja azt Rejtőnek.Rejtő számjegyenként összehasonlítja a tippet a titkos számmal, és megad két eredményt:
Esetünkben a válasz (1,2)
, hiszen a 4-es jó helyen van, a 3-as meg a 2-es pedig rossz helyen.
(4,0)
.Írjuk meg Fejtőt, aki kitalálja a titkos számot!
Az órán lekódolt algortimusunk az alábbi volt
1. Fejtő előállítja a lehetséges megoldások S halmazát: {0000, 0001, ..., 9999} 2. Fejtő tippje egy véletlen elem S-ből, ezt jelölje g (guess) 3. Rejtő összehasonlítja g-t a titokkal, válaszát jelölje r (response) 4. Fejtő S-ből kitörli azokat az elemeket, melyek g-vel összevetve nem r-t adnak 5. ismétlés 2-től, amíg S egyeleművé nem válik
C-kóddal
Az algoritmushoz közösen megírt függvényeink az alábbiak:
További segédfüggvények:
A fenti algoritmus gyenge pontja a (2) véletlen tippválasztás; okosabb, ha kikeressük a lehető legjobb tippet az S halmazból.
Minden tipphez hozzárendelünk egy súlyt, ami azt mutatja meg, hogy azzal a tippel mekkorára csökkenthető az S megoldáshalmaz mérete. Értelemszerűen a legkisebb súlyú tippet választjuk.
Meg kell gondolnunk viszont, hogy egy tipphez nem tudunk egyértelműen súlyt rendelni, amíg nem ismerjük a tippre adott választ is. Ha ugyanis pl. kezdőtippünkre a (4,0) választ kapjuk, akkor azonnal 1 méretűre szűkíthetjük S-t. Ha ellenben a válasz (0,0), akkor S méretét sokkal kisebb mértékben csökkenthetjük csak. Ezért úgy rendelünk tipphez súlyt, hogy elképzeljük az összes lehetséges r választ, mindegyikkel meghatározzuk a tipp súlyát, és ezek közül a legnagyobbat (worst case) képezzük.
Végeredményben egy minimax módszerrel dolgozunk, melyben
minden lehetséges S-beli g tippre minden lehetséges r válaszra számítjuk a (g, r) páros w(g, r) súlyát képezzük ezek maximumát r-ben képezzük ezek minimumát g-ben
Az implementációban az algoritmust az alábbi függvények valósítják meg:
w(g,r)
súlyt számítja. A filter függvény algoritmusát valósítja meg, de nem töröl az S halmazból, hanem csak megszámolja a megfelelő elemeketA módosított főprogram pedig:
Az új algoritmus tízes számrendszerben (tíz színnel) és négy számjeggyel az alábbihoz hasonló kimenetet produkál.
9349 ------------ guess res N t[s] 1: 0123 | (0,1) 3048 29.00 2: 1445 | (1,0) 542 3.00 3: 3646 | (1,1) 59 0.00 4: 7348 | (2,0) 3 0.00 5: 6048 | (1,0) 1 0.00 I won: 9349
Érdekes, hogy a legtöbb időt (29 másodperc!) az első tipp kikeresése igényli. Ez természetesen mindig ugyanaz, a 0123
értéket el is lehet tárolni, mint optimális kezdő tipp tízes számrendszerben. Az erre adott válasz (0,1) természetesen már függ a véletlen titkos számtól is. A nem túl szerencsés titkos szám mellett a kezdőtipp a 10000 lehetőséget 3048-ra csökkenti. A második legjobb tipp kiválasztása már csak 3 mp, a továbbiak századmásodperc alatt megvannak. Az ötödik iterációra jut el a program az N=1 értékhez, vagyis öt rákérdezéssel meghatározta a megoldást.
A fenti algoritmusban triviálisnak vettük, hogy a következő tippet mindig S-ből vesszük, vagyis csak olyat tippelünk, ami megoldás is lehet. Ez nem feltétlenül a legjobb hozzáállás. Ha nem az a célunk, hogy véletlenül beletrafáljunk a megoldásba, hanem az, hogy minél jobban csökkentsük az S halmaz méretét, a következő tippet az összes olyan érték közül kell választanunk, melyekre korábban még nem tippeltünk.
Ez már házi feladat...
A program innen letölthető.
Fordításnál itt már fontos, hogy bekapcsoljuk a kódoptimalizálást (optimalizálás nélkül a fenti eset első tippje nem 29, hanem 190 másodperc alatt készül el, a második tipp 3 helyett 18 másodperc alatt).
gcc mastermind.c -o mastermind.exe -O3 cl mastermind.c /Femastermind.exe /Ox