Írni fogunk egy leegyszerűsített Monopoly szerűséget. Ebben a példában már használni fogunk öröklődést, és polimorfizmust is! ---------------------------------------------------------------------- Ahol csak lehet, használd fel az előzőleg megírt osztályaidat! Ha szükséged van egy függvényre, amely nem található meg még a már kész osztályodban, akkor azt add hozzá. Például ha a Math osztályodba a te megoldásodhoz hiányzik valamilyen függvény, akkor oda írd meg, ne csak direktbe felhasználd valamelyik beépített header-ből! Ha a vektorod remove() függvénye nem tartja meg a sorrendet, akkor adj hozzá egy remove_keep_order() függvényt. Egyéb: Kicsit kiegyszerűsítettem az UML diagramokat, már gyakorlatilag c++ kódként néznek ki. Az előző feladatokban a tárgy által elvárt módon voltak írva, de mostantól nem fogom annyira átszerkeszteni. ---------------------------------------------------------------------- A játékszabályok, amiket implementálni fogunk: A játék n db ját;kossal indul. Mindenkinek van valamennyi pénze (legyen egyenként megadható). És van egy játékmező. (Mint a valódi monopolyban kb). A játékmezőn vannak mezők. Ezeknek a típusai: - TaxTile - Amikor a játékos rálép ennyi adót kell fizetnie. - OwnableTile - Ez olyan mező, amit meg lehet venni. Van vátelára, illetve egy belépési költsége. Ha egy játékos rálép, és még senkié, akkor az a játékos megveheti a vételárért cserébe. Ha nem veszi meg, nem kell fizetnie semmit. Ha valaki megveszi, akkor mindenki másnak, aki rálép meg kell fizetnie a tulajdonosnak a belépési díjat. Ha a tulajdonos kiesik a játékból, akkor az összes tulajdona felszabadul. - GainTile - Aki rálép az egy meghatározott összeget kap. - LuckTile - Aki rálép kaphat megadott %-nyi eséllyel, két megadott érték közötti pénzt. - JailTile - Aki rálép, a megadott környi ideig börtönbe kerül, azaz annzi körig nem léphet. Egy játékos akkor esik ki, ha elfogyott minden pénze. Az nyer, aki a legutoljára bent marad. A játékosok előre meghatározott, fix sorrendben jönnek egymás után, a körük elején dobnak 1db 6 oldalú dobókockával, majd annyit lépnek, amennyit dobtak, és a mező amire érkeztek hattásal lesz rájuk. A játékosok a játékmezőn körbe-körbe haladnak. A játékot úgy fogjuk megírni, hogy legyenek különböző személyiségü gépi játékosok: Agresszív: Mindent megvesz, amíg van rá pénze. Konzervatív: Ha a mező ára kevesebb, mint a pénzének a fele, akkor megveszi, Ügyeskedő: 50% eséllyel megveszi a mezőt, ha van rá lehetősége. Emberi: Megkérdezi a felhasználót, hogy mit akar tenni. Csaló: Mindent megvesz, amit csak tud. (csak 4, 5, 6-okat dobhat) A játékosok beállításait, és a mezőket is fájlból töltsük be. Konzolos program lesz, szóval mindent ami fontos, a konzolra kell majd kiírnunk, és a konzolról kell beolvasni, ha kérdezni szeretnénk a felhasználótól. A feladat, hogy ezt implementáljuk. Kezdjük a játékos osztályokkal: |--------------------------------------------------------------------------| | class Player | |--------------------------------------------------------------------------| | + String get_name(); | | + void set_name(const String &name); | | | | + int get_tile_index() const; | | + void set_tile_index(const int val); | | | | + int get_money() const; | | + void set_money(const int val); | | | | + int get_jail_time() const; | | + void set_jail_time(const int val); | | | | + bool get_lost() const; | | + void set_lost(const bool val); | | | | + virtual bool want_buy(const String &tile_name, int price); | | + virtual int throw_dice(); | | + virtual void on_lose(); | | | | + virtual void print(); | | | | + virtual String get_class_name(); | | | | + Player(); | | + virtual ~Player(); | | | | - String _name; | | - int _tile_index; | | - int _money; | | - int _jail_time; | | - bool _lost; | |--------------------------------------------------------------------------| Amik nem nyilvánvalók: set_money, set_jail_time -> írjon üzenetet a konzolra. set_lost(val) -> ha a val true, hívja meg az on_lost() függvényt. want_buy() függvénnyel fogja megkérdezni a rendszer, hogy meg akarja-e venni a játékos a jelenlegi mezőt. Megj.: nagy programban valószínűleg érdemes lenne a magát a Tile osztályt odaadni pointerkénkt, viszont a Tile osztálynak is kelleni fog majd, a Player osztály. Ezt aműgy meg lehet oldani, méghozzá úgy, hogy a Tile osztály a Player headerje tetején előre van deklarálva (class Tile;), a Player osztály a Tile headerje tetején van előre deklarálva (class Player;), és csak a .cpp fájlokoban vannak maguk a headerek beincludeolva. Hogy könnyítsek a dolgotokon, ezt kihagytam, viszont ha valaki elég erőt érez magában, az mindenképp csinálja így! Ekkor így fog kinézni a függvény: virtual bool want_buy(Tile *tile); (A volt paraméterek elérhetők a Tile osztály gettereivel.) int throw_dice(); Kockadobás 1-6 ig. on_lose() A játékos kiírja a konzolra, hogy vesztett. Mindegyik típus kicsit máshogy. print() Kiírja magát a játékos a konzolra. get_class_name() Visszaadja a játékos osztályának a nevét. pl itt return "Player"; A kényelem miatt van bent, a print()-hez. |--------------------------------------------------------------------------| | class AgressivePlayer : public Player | |--------------------------------------------------------------------------| | + bool want_buy(const String &tile_name, int price); | | + void on_lose(); | | | | + String get_class_name(); | | | | + AgressivePlayer(); | |--------------------------------------------------------------------------| Agresszív: Mindent megvesz, amíg van rá pénze. |--------------------------------------------------------------------------| | class ConservativePlayer : public Player | |--------------------------------------------------------------------------| | + bool want_buy(const String &tile_name, int price); | | + void on_lose(); | | | | + String get_class_name(); | | | | + ConservativePlayer(); | |--------------------------------------------------------------------------| Konzervatív: Ha a mező ára kevesebb, mint a pénzének a fele, akkor megveszi, |--------------------------------------------------------------------------| | class TrickyPlayer : public Player | |--------------------------------------------------------------------------| | + bool want_buy(const String &tile_name, int price); | | + void on_lose(); | | | | + String get_class_name(); | | | | + TrickyPlayer(); | |--------------------------------------------------------------------------| Ügyeskedő: 50% eséllyel megveszi a mezőt, ha van rá lehetősége. |--------------------------------------------------------------------------| | class HumanPlayer : public Player | |--------------------------------------------------------------------------| | + bool want_buy(const String &tile_name, int price); | | + void on_lose(); | | | | + String get_class_name(); | | | | + HumanPlayer(); | |--------------------------------------------------------------------------| Emberi: Megkérdezi a felhasználót, hogy mit akar tenni. |--------------------------------------------------------------------------| | class CheatingPlayer : public Player | |--------------------------------------------------------------------------| | + bool want_buy(const String &tile_name, int price); | | + int throw_dice(); | | + void on_lose(); | | | | + String get_class_name(); | | | | + CheatingPlayer(); | |--------------------------------------------------------------------------| Csaló: Mindent megvesz, amit csak tud. (és csak 4, 5, 6-okat dobhat) |--------------------------------------------------------------------------| | class PlayerLoader | |--------------------------------------------------------------------------| | + static Vector load_player_file(const String &file_name); | |--------------------------------------------------------------------------| Segít betölteni egy játékosok leírását tartalmaző filet. (Java-ban valószínűleg PlayerFactory lenne a neve.) Mindegy, hogy a file hogy néz ki. De itt egy példa: Player 10000 AA AgressivePlayer 20000 BC ConservativePlayer 20000 DA TrickyPlayer 20000 FA HumanPlayer 20000 RR CheatingPlayer 20000 AF |--------------------------------------------------------------------------| | class Tile | |--------------------------------------------------------------------------| | + String get_name(); | | + void set_name(const String &name); | | | | + virtual void on_player_arrived(Player *p); | | | | + virtual void reset(); | | | | + virtual void print(); | | + virtual String get_class_name(); | | | | + Tile(); | | + virtual ~Tile(); | | | | - String _name; | |--------------------------------------------------------------------------| A Mező osztály. A rendszer majd az on_player_arrived() függvényt hívja meg, miután egy játékos dobott, és rálép a következő mezőre. Ez a függvény mindent releváns információt írjon a konzolra. Itt maja a Mező fogja pl növelni a játékos pénzét, vagy börtönbe tenni, vagy megkérdezni, hogy meg akarja-e venni meg át a mezőt. reset() csak a OwnableTile fog itt valamit csinálni, ugyanis ebben ki fogja nullázni a tulajdonosát. (Ez két játék közötti kinullázásra való) A többit, ami esetleg nem triviális, lásd a player-nél. |--------------------------------------------------------------------------| | class TaxTile : public Tile | |--------------------------------------------------------------------------| | + int get_tax() const; | | + void set_tax(const int tax); | | | | + void on_player_arrived(Player *p); | | + void print(); | | + String get_class_name(); | | | | + TaxTile(); | | | | - int _tax; | |--------------------------------------------------------------------------| - TaxTile - Amikor a játékos rálép ennyi adót kell fizetnie. |--------------------------------------------------------------------------| | class OwnableTile : public Tile | |--------------------------------------------------------------------------| | + int get_price() const; | | + void set_price(const int val); | | | | + int get_enter_price() const; | | + void set_enter_price(const int val); | | | | + Player *get_owner() const; | | + void set_owner(Player *val); | | | | + void on_player_arrived(Player *p); | | + void print(); | | + void reset(); | | | | + String get_class_name(); | | | | + OwnableTile(); | | | | - int _price; | | - int _enter_price; | | - Player *_owner; | |--------------------------------------------------------------------------| - OwnableTile - Ez olyan mező, amit meg lehet venni. Van vételára, illetve egy belépési költsége. Ha egy játékos rálép, és még senkié, akkor az a játékos megveheti a vételárért cserébe. Ha nem veszi meg, nem kell fizetnie semmit. Ha valaki megveszi, akkor mindenki másnak, aki rálép meg kell fizetnie a tulajdonosnak a belépési díjat. Ha a tulajdonos kiesik a játékból, akkor az összes tulajdona felszabadul. |--------------------------------------------------------------------------| | class GainTile : public Tile | |--------------------------------------------------------------------------| | + int get_gain() const; | | + void set_gain(const int val); | | | | + void on_player_arrived(Player *p); | | + void print(); | | + String get_class_name(); | | | | + GainTile(); | | | | - int _gain; | |--------------------------------------------------------------------------| - GainTile - Aki rálép az egy meghatározott összeget kap. |--------------------------------------------------------------------------| | class LuckTile : public Tile | |--------------------------------------------------------------------------| | + int get_chance() const; | | + void set_chance(const int val); | | | | + int get_gain_min() const; | | + void set_gain_min(const int val); | | | | + int get_gain_max() const; | | + void set_gain_max(const int val); | | | | + void on_player_arrived(Player *p); | | + void print(); | | + String get_class_name(); | | | | + LuckTile(); | | | | - int _chance; | | - int _gain_min; | | - int _gain_max; | |--------------------------------------------------------------------------| - LuckTile - Aki rálép kaphat megadott %-nyi eséllyel, két megadott érték közötti pénzt. |--------------------------------------------------------------------------| | class JailTile : public Tile | |--------------------------------------------------------------------------| | + int get_jail_time() const; | | + void set_jail_time(const int val); | | | | + void on_player_arrived(Player *p); | | + void print(); | | + String get_class_name(); | | | | + JailTile(); | | | | - int _jail_time; | |--------------------------------------------------------------------------| - JailTile - Aki rálép, a megadott környi ideig börtönbe kerül, azaz annyi körig nem léphet. |--------------------------------------------------------------------------| | class TileLoader | |--------------------------------------------------------------------------| | + static Vector load_tile_file(const String &file_name); | |--------------------------------------------------------------------------| Ugyanolyan betöltő osztály, mint a játékos estében. Példa file szerkezet: Tile A TaxTile B 3433 TaxTile C 3433 OwnableTile D 222 22 OwnableTile E 222 22 OwnableTile F 222 22 GainTile G 100 TaxTile H 3433 LuckTile I 50 100 200 TaxTile J 3433 JailTile K 3 TaxTile L 3433 TaxTile M 3433 TaxTile N 3433 |--------------------------------------------------------------------------| | class Board | |--------------------------------------------------------------------------| | + Player *get_current_player(); | | + Player *get_previous_player(); | | | | + int get_turn() const; | | + void set_turn(const int turn); | | | | + Vector get_tiles() const; | | + void set_tiles(const Vector &tiles); | | | | + Vector get_active_players() const; | | + void set_active_players(const Vector &players); | | | | + Vector get_lost_players() const; | | + void set_lost_players(const Vector &players); | | | | + void load(const String &tile_file, const String &player_file); | | + void load_players(const String &file); | | + void load_tiles(const String &file); | | | | + void step(); | | + void run(); | | | | + void on_game_finished(); | | | | + void clear(); | | + void clear_players(); | | + void reset(); | | | | + void print(); | | | | + Board(); | | + Board(const String &tile_file, const String &player_file); | | + virtual ~Board(); | | | | - Vector _tiles; | | - Vector _active_players; | | - Vector _lost_players; | | - | | - Player *_previous_player; | | - int _current_player_index; | | - int _turn; | |--------------------------------------------------------------------------| on_game_finished() a játékról kiír a konzolra mindenféle információt, miután véget ért. run() a fő ciklus, a step() függvényt hívogatja, amíg több mint 1 aktív játékos van. Esetleg kiléphet nagy _turn szám után is. Ha a ciklus kilépett, hívja meg az on_game_finished()-et. clear() Minden vektort kiürít. clear_players() csak a 2 játékos vektort üríti ki. ne felejtsétek ez felszabadítani a memóriát! (delete). step(): Egy játékos körének a kezelése. Az algoritmus: 1. Fogjuk meg az aktív játékost. 2. Ha börtönben van, vonjunk le egy kört a börtönének számából, és kész. 3. Dobassunk vele kockát. 4. Számoljuk ki (modulo), hogy melyik mezőre érkezett. (player get_tile_index()) 5. Állítsuk be az új mező indexét neki (set_tile_index()). 6. kérjük ki a megfelelő mezőt a _tiles vektorból, és hívjuk meg a on_player_arrived(p); függvényét, a játékost adjuk be paraméternek. 7. Állítsuk be a _previous_player osztályváltozót a jelenlegi játékosra. 8. Ha ajátékos vesztett (get_lost()), akkor rakjuk ár a _lost_players veektorba, majd ellenőrizzük, hogy a _current_player_index nem indexel-e túl a játékosok vektorán. Ha igen, akkor állítsuk a _current_player_index-et 0-ra. (ugye eggyel csökkent a vektor mérete.) 9. Egyébként növeljük meg a _current_player_index-et 1-el, és ugyanúgy ellenűrizzük, hogy a _current_player_index nem indexel-e túl a játékosok vektorán, ha igen, akkor állítsuk a _current_player_index-et 0-ra. main.cpp + int main() : A main függvénybe csak ennyi legyen: Math::randomize(); Board b; b.load("tiles.config", "players.config"); b.run(); b.print(); return 0; Nyilvánvalóan a "tiles.config", "players.config" fájlokat hozzátok létre.