Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace mestintApr15
- {
- // Az AbsztraktÁllpot osztályt ki kellett bővíteni a GetHeurisztika metódussal.
- // Egyébként megegyezik az előző fejezetben tárgyalt változattal.
- abstract class AbsztraktÁllapot : ICloneable
- {
- public abstract bool ÁllapotE();
- public abstract bool CélÁllapotE();
- public abstract int OperátorokSzáma();
- public abstract bool SzuperOperátor(int i);
- public virtual object Clone() { return MemberwiseClone(); }
- public override bool Equals(Object a) { return false; }
- public override int GetHashCode() { return base.GetHashCode(); }
- // Ez a metódus adja vissza, mennyire jó az adott állapot
- // Ez csak egy hook, felül kell írni, ha
- // kétszemélyes játékot vagy best-first algoritmus alkalmazunk,
- // vagy bármilyen más algoritmust, ami heurisztikán alapszik.
- // Más esetben, pl. backtrack esetén, nem kell felülírni.
- public virtual int GetHeurisztika() { return 0; }
- }
- class RokaKutyaJatek : AbsztraktÁllapot
- {
- int rX, rY;
- int[] kX, kY;
- bool aRokaLep;
- //konstruktorral feltöltöm a táblát
- public RokaKutyaJatek()
- {
- rX = 0;
- rY = 2;
- kX = new int[4];
- kX[0] = 7;
- kY[0] = 1;
- kX[1] = 7;
- kY[1] = 3;
- kX[2] = 7;
- kY[2] = 5;
- kX[3] = 7;
- kY[3] = 7;
- aRokaLep = true;
- }
- public override bool ÁllapotE()
- {
- return rX >= 0 && rX <= 7 && kX.Max() <= 7 && kX.Min() >= 0 && kY.Max() <= 7 && kY.Min() >= 0;
- }
- public override bool CélÁllapotE()
- {
- if (aRokaLep && rX >= kX.Max()) return true;
- //if(!a mikor nyernek a kutyák
- return false;
- }
- public override int OperátorokSzáma()
- {
- if (aRokaLep) return 4;
- else return 8;
- }
- public override bool SzuperOperátor(int i)
- {
- if (aRokaLep)
- {
- switch (i)
- {
- case 0: return rokaLepes(-1, -1);
- case 1: return rokaLepes(-1, 1);
- case 2: return rokaLepes(1, -1);
- case 3: return rokaLepes(1, 1);
- default: return false;
- }
- }
- else {
- switch(i)
- {
- case 0: return kutyaLepes(0,-1,-1);
- case 1: return kutyaLepes(0,-1,1);
- case 2: return kutyaLepes(1,-1,-1);
- case 3: return kutyaLepes(1,-1,1);
- case 4: return kutyaLepes(2, -1, -1);
- case 5: return kutyaLepes(2, -1, 1);
- case 6: return kutyaLepes(3, -1, -1);
- case 7: return kutyaLepes(3, -1, 1);
- default: return false;
- }
- }
- }
- private bool rokaLepes(int relX, int relY)
- {
- if(preRokalepes(relX, relY);
- {
- rX +=relX;
- rY += relY;
- aRokaLep = !aRokaLep;
- if(ÁllapotE()) return true;
- }
- return false;
- }
- public override object Clone() { return MemberwiseClone(); }
- public override bool Equals(Object a) { return false; }
- public override int GetHashCode() { return base.GetHashCode(); }
- public virtual int GetHeurisztika() { return 0; }
- }
- // Az előző fejezetben ismertetett Csúcs osztályt bővítettük
- // egy-két metódussal, amely a két személyes játékok megvalósításához kell.
- class Csúcs
- {
- AbsztraktÁllapot állapot;
- int mélység;
- Csúcs szülő;
- // A szülőkön túl a gyermekeket is tartalmazza a Csúcs osztály.
- List<Csúcs> gyermekek = new List<Csúcs>();
- // Ez a mező tartalmazza, hogy melyik operátor segítségével jutottunk ebbe a csúcsba a szülő csúcsból.
- // Ennek segítségével tudom megmondani, melyik az ajánlott lépés NegaMaxMódszer esetén.
- int melyikOperátorralJutottamIde = -1; // ha -1, akkor még nincs beállítva
- public Csúcs(AbsztraktÁllapot kezdőÁllapot)
- {
- állapot = kezdőÁllapot;
- mélység = 0;
- szülő = null;
- }
- public Csúcs(Csúcs szülő)
- {
- állapot = (AbsztraktÁllapot)szülő.állapot.Clone();
- mélység = szülő.mélység + 1;
- this.szülő = szülő;
- }
- // Erre a metódusra azért van szükség, hogy a kiterjesztés működjön a JátékCsúcsra is.
- protected virtual Csúcs createGyermekCsúcs(Csúcs szülő) { return new Csúcs(szülő); }
- public Csúcs GetSzülő() { return szülő; }
- public int GetMélység() { return mélység; }
- public bool TerminálisCsúcsE() { return állapot.CélÁllapotE(); }
- public int OperátorokSzáma() { return állapot.OperátorokSzáma(); }
- public bool SzuperOperátor(int i)
- {
- // megjegyzem, melyik operátorral jutottam ebbe az állapotba
- melyikOperátorralJutottamIde = i;
- return állapot.SzuperOperátor(i);
- }
- public override bool Equals(Object obj)
- {
- Csúcs cs = (Csúcs)obj;
- return állapot.Equals(cs.állapot);
- }
- public override int GetHashCode() { return állapot.GetHashCode(); }
- public override String ToString() { return állapot.ToString(); }
- // Alkalmazza az összes alkalmazható operátort.
- // Visszaadja az így előálló új csúcsokat.
- public List<Csúcs> Kiterjesztés()
- {
- gyermekek = new List<Csúcs>();
- for (int i = 0; i < OperátorokSzáma(); i++)
- {
- // Új gyermek csúcsot készítek.
- // Ezzel a sorral nem működik a Kiterjesztés a JátékCsúcsban.
- // --- Csúcs újCsúcs = new Csúcs(this); ---
- // Ezért ezt használjuk:
- Csúcs újCsúcs = createGyermekCsúcs(this);
- // Kipróbálom az i.-dik alapoperátort. Alkalmazható?
- if (újCsúcs.SzuperOperátor(i))
- {
- // Ha igen, hozzáadom az újakhoz.
- gyermekek.Add(újCsúcs);
- }
- }
- return gyermekek;
- }
- // Visszaadja a csúcs heurisztikáját.
- // Ha saját heurisztikát akarunk írni, akkor azt a saját állapot osztályunkba kell megírni.
- public int GetHeurisztika() { return állapot.GetHeurisztika(); }
- // Visszaadja melyik operátorral jutottunk ide.
- // Ezzel az int értékkel kell majd meghívni a SzuperOperátor-t.
- public int GetMelyikOperátorralJutottamIde() { return melyikOperátorralJutottamIde; }
- // Nyomkövetéshez hasznos.
- public void Kiir()
- {
- Console.WriteLine(this);
- foreach (Csúcs gyermek in gyermekek) { gyermek.Kiir(); }
- }
- } // Ajáték csúcs acsúcs osztály kibővítése egy heurisztika értékkel.
- // Ezt a fajta csúcsot fel lehet használni a best first algoritmushoz is.
- class JátékCsúcs : Csúcs
- {
- int mennyireJó = -1; // mennyire jó, ha -1, akkor még nincs beállítva
- // Konstruktor:
- // A belső állapotot beállítja a start csúcsra.
- // A hívó felelősége, hogy a kezdő állapottal hívja meg.
- // A start csúcs mélysége 0, szülője nincs.
- public JátékCsúcs(AbsztraktÁllapot kezdőÁllapot) :
- base(kezdőÁllapot) { }
- // Egy új gyermek csúcsot készít.
- // Erre még meg kell hívni egy alkalmazható operátor is, csak azután lesz kész.
- public JátékCsúcs(Csúcs szülő) : base(szülő) { }
- // Erre a metódusra azért van szükség, hogy a kiterjesztés
- // működjön a JátékCsúcsra is.
- protected override Csúcs createGyermekCsúcs(Csúcs szülő)
- {
- return new JátékCsúcs(szülő);
- }
- // Visszaadja a csúcshoz tartozó heurisztikát.
- // Ez a csúcsban lévő állapot heurisztikája
- // megszorozva a paraméterben megkapott szor értékkel.
- // NegaMax esetén a szor általában 1, ha az a játékos lép,
- // akinek jó lépést keresünk, -1, ha az ellenfél lép.
- // Mivel minden állapothoz csak egy heurisztika van, ami nem
- // veszi figyelembe, hogy ki lép, ezért a MiniMax-nak is
- // úgy kell használni ezt a metódust, mint a NegaMax-nak.
- public int GetMennyireJó(int szor)
- {
- if (mennyireJó == -1) mennyireJó = GetHeurisztika() * szor;
- return mennyireJó;
- }
- }
- // Ebből kell leszármaztatni a lépés ajánló algoritmusokat, mint a NegaMax módszer.
- abstract class Stratégia
- {
- // Ha a start csúcs zsákutca, akkor null-t ad vissza.
- // Egyébként azt a csúcsot, amibe a stratégia szerint érdemes lépni.
- public abstract JátékCsúcs MitLépjek(JátékCsúcs start);
- } // Egy lépést ajánl valamely játékosnak a NegaMax módszer alapján.
- // Ehhez előre tekint és a heurisztika meghatározása után megkeresi a legkedvezőbb utat a játék fában.
- // Ha a játékfa levelei terminális csúcsok, akkor a legkedvezőbb út a nyerő stratégia lesz.
- class NegaMaxMódszer : Stratégia
- {
- int maxMélység; // Ennyi lépésre tekintünk előre.
- // Minél több lépésre tekintünk előre, annál intelligensebbnek tűnik a gép, hiszen jobb lépést választ.
- // Ezt a TicTacToe esetén figyelhetjük meg.
- // Ugyanakkor minél több lépést generálunk, annál lassabb lesz az algoritmus.
- // Ennek egy megoldása az Alfabéta-vágás, de ezt nem programoztuk le.
- public NegaMaxMódszer(int intelligencia) { maxMélység = intelligencia; }
- // Egy játékcsúcsot ad vissza.
- // Ennek a GetMelyikOperátorralJutottamIde() függvénye mondja meg,
- // melyik lépést ajánlja a NegaMax módszer.
- // Ha zsákutcában van, akkor null-t ad vissza.
- public override JátékCsúcs MitLépjek(JátékCsúcs start)
- {
- Csúcs levél = MaxLépés(start, start.GetMélység() + maxMélység);
- if (levél == start) return null;
- while (levél.GetSzülő() != start) { levél = levél.GetSzülő(); }
- //levél.Kiir(); // Nyomkövetés esetén hasznos segítség
- return (JátékCsúcs)levél;
- }
- // Feltételezi, hogy a start csúcsban a kérdező játékos kérdezi, mit lépjen.
- // A kérdező játékos legjobb lépését, tehát a legnagyobb heurisztikájú
- // csúcs felé vezető lépést választja.
- // A gyermek csúcsok heurisztikáját NegaMax módszerrel számoljuk.
- private JátékCsúcs MaxLépés(JátékCsúcs start, int maxMélység)
- {
- JátékCsúcs akt = start;
- if (akt.GetMélység() == maxMélység) { return akt; }
- if (akt.TerminálisCsúcsE()) { return akt; }
- List<Csúcs> gyermekek = null;
- gyermekek = akt.Kiterjesztés();
- if (gyermekek.Count == 0) { return akt; }
- JátékCsúcs elsőGyermek = (JátékCsúcs)gyermekek[0];
- JátékCsúcs leg = MinLépés(elsőGyermek, maxMélység);
- int h = leg.GetMennyireJó(+1);
- for (int i = 1; i < gyermekek.Count; i++)
- {
- JátékCsúcs gyermek = (JátékCsúcs)gyermekek[i];
- JátékCsúcs legE = MinLépés(gyermek, maxMélység);
- int hE = legE.GetMennyireJó(+1);
- if (hE > h) { h = hE; leg = legE; }
- }
- return leg;
- }
- // Felételezi, hogy a start csúcsban a a kérdező játékos ellenfele lép.
- // Az ellenfél játékos legjobb lépését választja, tehát azt,
- // ami a kérdező játékosnak a legrosszabb.
- // Egybe lehetne vonni a MaxLépéssel, hiszen csak 5 helyen más.
- // Ezek a sorokat megjelöltük.
- private JátékCsúcs MinLépés(JátékCsúcs start, int maxMélység)
- {
- JátékCsúcs akt = start;
- if (akt.GetMélység() == maxMélység) { return akt; }
- if (akt.TerminálisCsúcsE()) { return akt; }
- List<Csúcs> gyermekek = null;
- gyermekek = akt.Kiterjesztés();
- if (gyermekek.Count == 0) { return akt; }
- JátékCsúcs elsőGyermek = (JátékCsúcs)gyermekek[0];
- JátékCsúcs leg = MaxLépés(elsőGyermek, maxMélység); //más
- int h = leg.GetMennyireJó(-1); // más
- for (int i = 1; i < gyermekek.Count; i++)
- {
- JátékCsúcs gyermek = (JátékCsúcs)gyermekek[i];
- JátékCsúcs legE = MaxLépés(gyermek, maxMélység); //más
- int hE = legE.GetMennyireJó(-1); //más
- if (hE < h) { h = hE; leg = legE; } //más
- }
- return leg;
- }
- }
- // Egy játékot vezényel le.
- // Ez egybevonható a főprogramból, hiszen abból emeltük ki.
- class Játék
- {
- AbsztraktÁllapot startÁllapot;
- Stratégia strat;
- public Játék(AbsztraktÁllapot startÁllapot, Stratégia strat)
- {
- this.startÁllapot = startÁllapot;
- this.strat = strat;
- }
- public void start()
- {
- JátékCsúcs akt = new JátékCsúcs(startÁllapot);
- Console.WriteLine("Játszhat a gép ellen!");
- while (!akt.TerminálisCsúcsE())
- {
- // Ellenfél lépése.
- Console.WriteLine("Jelenlegi állás: {0}", akt);
- akt = strat.MitLépjek(akt);
- int i = akt.GetMelyikOperátorralJutottamIde();
- Console.WriteLine("A gép ezt az operátort választotta: {0}", i);
- // Nyertes állás?
- if (akt.TerminálisCsúcsE()) break;
- // Saját lépésem.
- bool b = false;
- while (!b)
- {
- Console.WriteLine("Jelenlegi állás: {0}", akt);
- Console.WriteLine("Melyik operátort választja? (0,..,{0}): ", akt.OperátorokSzáma() - 1);
- int k = 0;
- try
- {
- k = int.Parse(Console.ReadLine());
- }
- catch (Exception e)
- {
- Console.WriteLine("Hiba: {0}", e.ToString());
- Console.WriteLine("Érvénytelen lépés. Újra!");
- continue;
- }
- b = akt.SzuperOperátor(k);
- if (!b) Console.WriteLine("Ez az operátor nem alkalmazható. Újra!");
- }
- }
- Console.WriteLine("Jelenlegi állás: {0}", akt);
- Console.WriteLine("Aki utoljára lépett, az nyert, vagy döntetlen.");
- }
- } // Főprogram.
- class Program
- {
- static void Main(string[] args)
- {
- Console.ReadLine();
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment