//: C10:PaperScissorsRock.cpp
// Z knihy "Myslíme v jazyce C++, část 2" od Bruce Eckela a Chucka Allisona.
// (c) 1995-2004 MindView, Inc. Všechna práva vyhrazena.
// Přečtěte si podmínky používání zdrojových kódů umístěné v souboru 
// 'Licence.txt' dodávaném spolu s balíčkem kódu dostupným na www.MindView.net.
// Ukázka vícenásobného odesílání
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <ctime>
#include <cstdlib>
#include "../purge.h"
using namespace std;

class Papir;
class Nuzky;
class Kamen;

enum Vysledek { VYHRA, PROHRA, REMIZA };

ostream& operator<<(ostream& os, const Vysledek vysledek) {
  switch(vysledek) {
    default:
    case VYHRA: return os << "vyhra";
    case PROHRA: return os << "prohra";
    case REMIZA: return os << "remiza";
  }
}

class Volba {
public:
  virtual Vysledek soutezit(const Volba*) = 0;
  virtual Vysledek vyhodnotit(const Papir*) const = 0;
  virtual Vysledek vyhodnotit(const Nuzky*) const= 0;
  virtual Vysledek vyhodnotit(const Kamen*) const = 0;
  virtual ostream& tisknout(ostream& os) const = 0;
  virtual ~Volba() {}
  friend ostream& operator<<(ostream& os, const Volba* volba) {
    return volba->tisknout(os);
  }
};

class Papir : public Volba {
public:
  Vysledek soutezit(const Volba* volba) { return volba->vyhodnotit(this);}
  Vysledek vyhodnotit(const Papir*) const { return REMIZA; }
  Vysledek vyhodnotit(const Nuzky*) const { return VYHRA; }
  Vysledek vyhodnotit(const Kamen*) const { return PROHRA; }
  ostream& tisknout(ostream& os) const {
    return os << "Papir   ";
  }
};

class Nuzky : public Volba {
public:
  Vysledek soutezit(const Volba* volba) { return volba->vyhodnotit(this);}
  Vysledek vyhodnotit(const Papir*) const { return PROHRA; }
  Vysledek vyhodnotit(const Nuzky*) const { return REMIZA; }
  Vysledek vyhodnotit(const Kamen*) const { return VYHRA; }
  ostream& tisknout(ostream& os) const {
    return os << "Nuzky";
  }
};

class Kamen : public Volba {
public:
  Vysledek soutezit(const Volba* volba) { return volba->vyhodnotit(this);}
  Vysledek vyhodnotit(const Papir*) const { return VYHRA; }
  Vysledek vyhodnotit(const Nuzky*) const { return PROHRA; }
  Vysledek vyhodnotit(const Kamen*) const { return REMIZA; }
  ostream& tisknout(ostream& os) const {
    return os << "Kamen    ";
  }
};

struct GeneratorVolby {
  Volba* operator()() {
    switch(rand() % 3) {
      default:
      case 0: return new Nuzky;
      case 1: return new Papir;
      case 2: return new Kamen;
    }
  }
};

struct Soutezit {
  Vysledek operator()(Volba* a, Volba* b) {
    cout << a << "\t" << b << "\t";
    return a->soutezit(b);
  }
};

int main() {
  srand(time(0)); // Inicializovat generátor náhodných čísel
  const int sz = 20;
  vector<Volba*> v(sz*2);
  generate(v.begin(), v.end(), GeneratorVolby());
  transform(v.begin(), v.begin() + sz,
    v.begin() + sz,
    ostream_iterator<Vysledek>(cout, "\n"),
    Soutezit());
  purge(v);
} ///:~
