Lekce 7 - Aréna s bojovníky v C++

C a C++ C++ Objektově orientované programování Aréna s bojovníky v C++

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Bojovník do arény - Zapouzdření, jsme si vytvořili třídu bojovníka. Hrací kostku máme hotovou z prvních lekcí objektově orientovaného programování. Dnes dáme vše dohromady a vytvoříme funkční arénu. C++ tutoriál bude spíše oddechový a pomůže nám zopakovat si práci s objekty a algoritmické myšlení.

Potřebujeme napsat nějaký kód pro obsluhu bojovníků a výpis zpráv uživateli. Nejdříve si vyřešíme, kde vlastně budou bojovníci. V našem případě by bylo vhodné dát je do třídy Hrac. Pro začátek předpokládejme, že každý hráč má pouze jednoho bojovníka.

Hrac.h

#include <string>
#include "Kostka.h"
#include "Bojovnik.h"
using namespace std;

class Hrac
{
private:
    Bojovnik bojovnik;
    string jmeno;
public:
    Hrac(string jmeno, Kostka &kostka);
    string getJmeno();
    Bojovnik& getBojovnik();
};

Hrac.cpp

#include "Hrac.h"
Hrac::Hrac(string jmeno, Kostka &kostka): bojovnik(100,8,5,kostka)
{
    this->jmeno = jmeno;
}
string Hrac::getJmeno()
{
    return this->jmeno;
}
Bojovnik Hrac::getBojovnik()
{
    return this->bojovnik;
}

Nyní se podíváme na arénu. Určitě budeme chtít, aby simulovala souboj bojovníků. Pro zjednodušení budeme pracovat s tím, že útočník zaútočí na náhodného jiného bojovníka. Na začátku jsme si také řekli, že se bude jednat o tahovou hru - potřebujeme si pamatovat počet provedených tahů. Dále budeme chtít vypsat informace o bojovnících a útoku. Všechny tyto metody bude potřebovat třída Arena. Nejdříve si ve třídě Arena vytvoříme atribut typu int, pojmenujeme jej tah a v konstruktoru mu nastavíme hodnotu 1. Poté si napíšeme metodou pro výpis informací o aréně - vypis().

void Arena::vypis()
{
    cout << "-------------- Arena --------------" << endl;
    cout << "Tah: " << this->tah << endl;
    cout << "Zdravi bojovniku:" << endl;
    for (int i = 0; i < this->pocet_hracu; i++)
    {
        cout << "\t" << this->hraci[i]->getJmeno() << ": "; // vypíšeme jméno bojovníka
        if (!this->hraci[i]->getBojovnik().nazivu()) // zjistíme, že bojovník ještě není mrtev
        {
            cout << "mrtev" << endl;  // pokud je bojovník mrtev tak o tom informujeme
            continue; // a pokračujeme na dalšího bojovníka
        }
        cout << "["; // začátek baru se životy
        // vypočítáme kolik procent života bojovník má
        float pocet_zivotu_procent = this->hraci[i]->getBojovnik().getZivot() / this->hraci[i]->getBojovnik().getMaxZivot();
        for (double z = 0; z < 1.0; z += 0.1)
            cout << (z < pocet_zivotu_procent ? '#' : ' '); // vypisujeme procenta života
        // ukončíme bar se životy a vypíšeme info o útoku a obraně
        cout << "] (utok: " << this->hraci[i]->getBojovnik().getUtok() << ", obrana: " << this->hraci[i]->getBojovnik().getObrana() << ")" << endl;
    }
}

Životy budeme zobrazovat pouze graficky a to procentuálně. Proto si nejdříve vypočítáme, kolik procent života bojovník má (proměnná pocet_zivotu_procent) a poté iterujeme po 10-ti procentech a vypisujeme # (pokud bojovník životy má) nebo mezeru (pokud životy ztratil) - to zajišťuje ternární operátor ve výpisu.

Konzolová aplikace
-------------- Arena --------------
Tah: 1
Zdravi bojovniku:
        Karel: [##########] (utok: 8, obrana: 5)
        Pavel: [##########] (utok: 8, obrana: 5)
        Honza: [##########] (utok: 8, obrana: 5)

Nyní se přesuneme na metodu zapas() - hlavní smyčku naší hry. Metoda zapas() nebude mít žádné parametry a nebude ani nic vracet. Uvnitř bude cyklus, který bude na střídačku volat útoky bojovníků a vypisovat informační obrazovku a zprávy. Nejdříve si uděláme pomocné metody (budou privátní), které později použijeme:

Arena.cpp

bool Arena::existujeVitez()
{
    return this->pocetZivych() == 1;
}

int Arena::pocetZivych()
{
    int zivych = 0;
    // pro každého živého hráče
    for (int i = 0; i < this->pocet_hracu; i++)
        if (this->hraci[i]->getBojovnik().nazivu())
            zivych++; // zvyš počet živých o jeden
    return zivych;
}

Metoda nám zjistí, kolik bojovníků přežilo. Pokud bude živý jenom jeden z nich, potom je vítěz a hra může skončit. A teď ke slibované metodě zapas().

Arena.cpp

void Arena::zapas()
{
    // dokud nezůstane pouze jeden hráč
    while (!this->existujeVitez())
    {
        this->vypis(); // vypíšeme informace o hráčích
        // zkontroluj všechny hráče
        for (int i = 0; i < this->pocet_hracu; i++)
        {
            // pokud není bojovník naživu, potom ho přeskoč
            if (!this->hraci[i]->getBojovnik().nazivu())
                continue;
            // mohlo se stát, že v předchozím kole někoho zabili a tak zůstal poslední bojovník
            // pokud se to stalo, potom hra končí
            if (this->existujeVitez())
                break;
            // spočtení index nejbližšího živého hráče, na kterého budeme útočit
            int utok_na = (i + 1) % this->pocet_hracu;
            while (!this->hraci[utok_na]->getBojovnik().nazivu())
                utok_na = (utok_na + 1) % this->pocet_hracu;
            // útok
            float zraneni = this->hraci[i]->getBojovnik().utoc(this->hraci[utok_na]->getBojovnik());
            // vypsání výsledku souboje
            cout << this->hraci[i]->getJmeno() << " utoci na "
                << this->hraci[utok_na]->getJmeno() << " za " << zraneni << " zraneni" << endl;
        }
        // přesuneme se do dalšího tahu
        this->tah++;
    }
}

Kroky jsem se snažil popsat v komentářích, proto je nemá smysl popisovat znovu v textu. Nejproblematičtější je zřejmě spočtení indexu hráče, na kterého budeme útočit. Začneme na bojovníkovi na vyšším indexu než je aktuální bojovník (aby hráč neútočil sám na sebe). Poté zjišťujeme, jestli je bojovník živý. Pokud není, pak se posuneme na dalšího bojovníka v pořadí. Co ale dělat, když dojdeme na konec pole? Chtěli bychom se vrátit zpět na počátek (index 0) - to nám zajistí modulo. Pokud se dostaneme na hodnotu 3 (a 3 je počet hráčů), potom modulo automaticky index sníží na 0.

Teď již to stačí jen celé spustit:

main.cpp

#include <iostream>
#include "Kostka.h"
#include "Arena.h"
#include "Bojovnik.h"
using namespace std;


int main()
{
    Kostka kostka;
    Arena arena(3, kostka);
    arena.zapas();
    arena.vypis();
    cin.get(); cin.get();
    return 0;
}

Pokud jste program zkusili spustit, bude běžet v nekonečném cyklu, ale nic se nebude měnit. Jak již bylo řečeno, při volání metody se parametry a návratová hodnota překopírují. To je velice důležité. V metodě hráče getBojovnik() vracíme typ Bojovnik. To ale znamená, že volající program nedostane našeho skutečného bojovníka, ale pouze jeho kopii. Opravíme to tak, že změníme návratovou hodnotu na referenci nebo ukazatel - v našem případě poslouží lépe reference.

Hrac.h

class Hrac
{
private:
        Bojovnik bojovnik;
        string jmeno;
public:
        Hrac(string jmeno, Kostka &kostka);
        string getJmeno();
        Bojovnik& getBojovnik();
};

Hrac.cpp

Bojovnik& Hrac::getBojovnik()
{
        return this->bojovnik;
}

Nyní by měl program fungovat podle našich představ.

Konzolová aplikace
Zadejte jmeno hrace: Karel
Zadejte jmeno hrace: Pavel
Zadejte jmeno hrace: Honza
-------------- Arena --------------
Tah: 1
Zdravi bojovniku:
        Karel: [###########] (utok: 8, obrana: 5)
        Pavel: [###########] (utok: 8, obrana: 5)
        Honza: [###########] (utok: 8, obrana: 5)
Karel utoci na Pavel
Pavel utoci na Honza
Honza utoci na Karel
-------------- Arena --------------
Tah: 2
Zdravi bojovniku:
        Karel: [########## ] (utok: 8, obrana: 5)
        Pavel: [########## ] (utok: 8, obrana: 5)
        Honza: [########## ] (utok: 8, obrana: 5)
Karel utoci na Pavel
Pavel utoci na Honza
Honza utoci na Karel

Možná bychom chtěli při útoku zobrazit, kolik životů byla protihráči ubráno. To provedeme jednoduše, z metody Bojovnik.utoc() si můžeme vrátit zranění, které bylo způsobeno. Dále bychom na konci programu chtěli zobrazit informace o vítězi. Pro provedeme opět jednoduše, přidáme si do třídy Arena další metodu, která vypíše informace o vítězi. Jak je vidět, OOP přístup je velice praktický a lehce se rozšiřuje.

// Bojovnik.h
class Bojovnik
{
private:
    float zivot;
    float max_zivot;
    float utok;
    float obrana;
    Kostka &kostka;
public:
    Bojovnik(float zivot, float utok, float obrana, Kostka &kostka);
    bool nazivu();
    float utoc(Bojovnik &druhy);
    float getZivot();
    float getMaxZivot();
    float getUtok();
    float getObrana();
};

// Bojovnik.cpp
float Bojovnik::utoc(Bojovnik & druhy)
{
    float obrana_druhy = druhy.obrana + druhy.kostka.hod();
    float utok_prvni = this->utok + this->kostka.hod();
    float zraneni = utok_prvni - obrana_druhy;
    if (zraneni < 0)
        zraneni = 0;
    druhy.zivot -= zraneni;
    return zraneni;
}

// Arena.cpp
void Arena::vypisViteze()
{
    if (!this->existujeVitez())
        return;

    for (int i = 0; i < this->pocet_hracu; i++)
        if (this->hraci[i]->getBojovnik().nazivu())
        {
            cout << endl << "-------------- VITEZ --------------" << endl;
            cout << "Vitezem se stal: " << this->hraci[i]->getJmeno() << " s " << this->hraci[i]->getBojovnik().getZivot() << " zivoty" << endl;
            return;
        }
}

// main.cpp
int main()
{
    Kostka kostka;
    Arena arena(6, kostka);
    arena.zapas();
    arena.vypis();
    arena.vypisViteze();
    return 0;
}

Gratuluji vám, pokud jste se dostali až sem a tutoriály opravdu četli a pochopili, máte základy objektového programování a dokážete tvořit rozumné aplikace :)

Tím máme (alespoň ze základu) hotovou naši arénu a příště, v lekci Konstantní metody v C++, se podíváme na konstantní metody v C++.


 

Stáhnout

Staženo 52x (9.41 kB)
Aplikace je včetně zdrojových kódů v jazyce C++

 

 

Článek pro vás napsal patrik.valkovic
Avatar
Jak se ti líbí článek?
1 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Miniatura
Následující článek
Konstantní metody v C++
Aktivity (7)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!