//--------ZAD2--------------------------------------------------------------------------

//Każdy z graczy wykonuje ruchy na planszy, reprezentowanej przez obiekt typu Board w niezależnym watku.
//Niestety klasa ta zawiera bład, gracz typu PlayerExtra powinien wykonywać dwa ruchy jeden po drugim,
//a obserwuje się sytuacje, w której pomiędzy tymi ruchami tworzony jest ruch gracza typu PlayerNormal.
// Popraw klasę Board.


#include <thread>
#include <mutex>
#include <chrono>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;


// rekurencja i 'zwykly' muteks

class Board {
public:
 void make_move(int player_id) {
     lock_guard<mutex> guard(m_);
     history_.push_back(player_id);
 }

 void make_two_moves(int player_id) {
     make_move(player_id);
     this_thread::sleep_for(chrono::milliseconds(20));
     make_move(player_id);
 }
 void print(std::ostream& os) {
     for_each(history_.begin(), history_.end(), [&](int id){ os << id; });
 }
private:
 mutex m_;
 vector<int> history_;
};


// odpowiedź - recursive_mutex

class Board {
public:
    void make_move(int player_id) {
        lock_guard<recursive_mutex> guard(m_);
        history_.push_back(player_id);
    }

    void make_two_moves(int player_id) {
        lock_guard<recursive_mutex> guard(m_);
        make_move(player_id);
        this_thread::sleep_for(chrono::milliseconds(20));
        make_move(player_id);
    }
    void print(std::ostream& os) {
        for_each(history_.begin(), history_.end(), [&](int id){ os << id; });
    }
private:
    recursive_mutex m_;
    vector<int> history_;
};


class PlayerNormal {
public:
    PlayerNormal(int id, Board& board) : id_(id), board_(board) {}

    void operator()() {
        for(int i=0;i<100;++i) {
            board_.make_move(id_);
            this_thread::sleep_for(chrono::milliseconds(20));
        }
    }
private:
    int id_;
    Board& board_;
};

class PlayerExtra {
public:
    PlayerExtra(int id, Board& board) : id_(id), board_(board) {}

    void operator()() {
        for(int i=0;i<100;++i) {
            board_.make_two_moves(id_);
            this_thread::sleep_for(chrono::milliseconds(20));
        }
    }
private:
    int id_;
    Board& board_;
};

int main() {
    Board board;
    PlayerNormal player1(1,board);
    PlayerExtra player2(2,board);
    thread thrd1( ref(player1) );
    thread thrd2( ref(player2) );
    thrd1.join();
    thrd2.join();
    board.print(cout);
    cout << endl;
    return 0;
}

