Skip to content
Snippets Groups Projects
game_state.cpp 10.02 KiB
//
// Created by Manuel on 27.01.2021.
//

#include "game_state.h"

#include "../exceptions/WizardException.h"
#include "../serialization/vector_utils.h"


game_state::game_state() : unique_serializable() {
    this->_players = std::vector<player*>();
    this->_round_state = nullptr; //round state only initialized when round is set up
    this->_is_started = new serializable_value<bool>(false);
    this->_is_finished = new serializable_value<bool>(false);
    this->_current_player_idx = new serializable_value<int>(0);
    this->_round_number = new serializable_value<int>(0);
    this->_starting_player_idx = new serializable_value<int>(0);
}

game_state::game_state(std::string id, std::vector<player *> &players, round_state* round_state, serializable_value<bool> *is_started,
                       serializable_value<bool> *is_finished, serializable_value<int> *current_player_idx,
                       serializable_value<int>* round_number, serializable_value<int> *starting_player_idx)
        : unique_serializable(id),
          _players(players),
      	  _round_state(round_state),
          _is_started(is_started),
          _is_finished(is_finished),
          _current_player_idx(current_player_idx),
          _round_number(round_number),
          _starting_player_idx(starting_player_idx)
{ }

game_state::game_state(std::string id) : unique_serializable(id) {
    this->_players = std::vector<player*>();
    this->_round_state = nullptr;
    this->_is_started = new serializable_value<bool>(false);
    this->_is_finished = new serializable_value<bool>(false);
    this->_current_player_idx = new serializable_value<int>(0);
    this->_round_number = new serializable_value<int>(0);
    this->_starting_player_idx = new serializable_value<int>(0);
}

game_state::~game_state() {
    if (_is_started != nullptr) {
        // for some reason players doesn't have to be deleted in Lama
        delete _round_state;
        delete _is_started;
        delete _is_finished;
        delete _current_player_idx;
        delete _starting_player_idx;
        delete _round_number;

        _round_state = nullptr;
        _is_started = nullptr;
        _is_finished = nullptr;
        _current_player_idx = nullptr;
        _starting_player_idx = nullptr;
        _round_number = nullptr;
    }
}

// accessors
player* game_state::get_current_player() const {
    if(_current_player_idx == nullptr || _players.size() == 0) {
        return nullptr;
    }
    return _players[_current_player_idx->get_value()];
}
round_state* round_state::get_round_state() const {
  return _round_state;
}

bool game_state::is_full() const {
    return _players.size() == _max_nof_players;
}

bool game_state::is_started() const {
    return _is_started->get_value();
}

bool game_state::is_finished() const {
    return _is_finished->get_value();
}

int game_state::get_round_number() const {
    return _round_number->get_value();
}

int game_state::get_max_round_number() const {
    return 60 / _players.size();
}

int game_state::get_player_index(player *player) const {
    auto it = std::find(_players.begin(), _players.end(), player);
    if (it == _players.end()) {
        return -1;
    } else {
        return it - _players.begin();
    }
}

bool game_state::is_player_in_game(player *player) const {
    return std::find(_players.begin(), _players.end(), player) < _players.end();
}

std::vector<player*>& game_state::get_players() {
    return _players;
}





#ifdef WIZARD_SERVER

// state modification functions without diff

void game_state::increase_round_number() {
  int number = get_round_number() + 1;
  _round_number->set_value(number);
}

void game_state::update_current_player() {
    int nof_players = _players.size();
    int current_player_idx = _current_player_idx->get_value();
    ++current_player_idx %= nof_players;
    this->_current_player_idx->set_value(current_player_idx);
}


void game_state::setup_round(std::string &err) {

    increase_round_number();

    _round_state->setup_round(err, this->get_round_number(), _current_player_idx->get_value());

    //TODO: how does new round manage to send task to players to estimate tricks / play card / ...
    update_current_player();

    // Provide the callback to be called when the round ends
    _round_state->set_on_round_end([this]() {  // [this] captures the game_state instance
        if (get_round_number() < get_max_round_number()) {
            setup_round(err);  // Start a new round
        } else {
            //TODO: possibly more complicated finish game logic
            this->_is_finished->set_value(true);  // End the game
            finish_game(err);

        }
    });
}

bool game_state::start_game(std::string &err) {
    if (_players.size() < _min_nof_players) {
        err = "You need at least " + std::to_string(_min_nof_players) + " players to start the game.";
        return false;
    }

    if (!_is_started->get_value()) {
        this->_is_started->set_value(true);
        // TODO: think about whether this will cause a problem since setup_round iteratively calls it self and start_game only ends when game has ended
        if (_round_state) {
        	delete _round_state;  // Clean up previous round state if it exists
        	_round_state = nullptr;
    	}

    	_round_state = new round_state(players = this->_players, starting_player_idx = this->_current_player_idx,
                                   round_number = this->_round_number);
        this->setup_round(err); //iteratively starts all rounds until game is over
        return true;
    } else {
        err = "Could not start game, as the game was already started";
        return false;
    }
}

bool game_state::remove_player(player *player_ptr, std::string &err) {
    int idx = get_player_index(player_ptr);
    if (idx != -1) {
        if (idx < _current_player_idx->get_value()) {
            // reduce current_player_idx if the player who left had a lower index
            _current_player_idx->set_value(_current_player_idx->get_value() - 1);
        }
        _players.erase(_players.begin() + idx);
        return true;
    } else {
        err = "Could not leave game, as the requested player was not found in that game.";
        return false;
    }
}


bool game_state::add_player(player* player_ptr, std::string& err) {
    if (_is_started->get_value()) {
        err = "Could not join game, because the requested game is already started.";
        return false;
    }
    if (_is_finished->get_value()) {
        err = "Could not join game, because the requested game is already finished.";
        return false;
    }
    if (_players.size() >= _max_nof_players) {
        err = "Could not join game, because the max number of players is already reached.";
        return false;
    }
    if (std::find(_players.begin(), _players.end(), player_ptr) != _players.end()) {
        err = "Could not join game, because this player is already subscribed to this game.";
        return false;
    }

    _players.push_back(player_ptr);
    return true;
}

bool game_state::finish_game(std::string &err) {
  // TODO: part to determine winner of the game, show game over message and send players their final score
  return true;
}

#endif


// Serializable interface
void game_state::write_into_json(rapidjson::Value &json,
                                 rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> &allocator) const {
    unique_serializable::write_into_json(json, allocator);

    rapidjson::Value is_finished_val(rapidjson::kObjectType);
    _is_finished->write_into_json(is_finished_val, allocator);
    json.AddMember("is_finished", is_finished_val, allocator);

    rapidjson::Value is_started_val(rapidjson::kObjectType);
    _is_started->write_into_json(is_started_val, allocator);
    json.AddMember("is_started", is_started_val, allocator);

    rapidjson::Value current_player_idx_val(rapidjson::kObjectType);
    _current_player_idx->write_into_json(current_player_idx_val, allocator);
    json.AddMember("current_player_idx", current_player_idx_val, allocator);

    rapidjson::Value starting_player_idx_val(rapidjson::kObjectType);
    _starting_player_idx->write_into_json(starting_player_idx_val, allocator);
    json.AddMember("starting_player_idx", starting_player_idx_val, allocator);

    rapidjson::Value round_number_val(rapidjson::kObjectType);
    _round_number->write_into_json(round_number_val, allocator);
    json.AddMember("round_number", round_number_val, allocator);

    rapidjson::Value round_state_val(rapidjson::kObjectType);
    _round_state->write_into_json(round_state_val, allocator);
    json.AddMember("round_state", round_state_val, allocator);

    json.AddMember("players", vector_utils::serialize_vector(_players, allocator), allocator);
}


game_state* game_state::from_json(const rapidjson::Value &json) {
    if (json.HasMember("id")
        && json.HasMember("is_finished")
        && json.HasMember("is_started")
        && json.HasMember("current_player_idx")
        && json.HasMember("round_number")
        && json.HasMember("starting_player_idx")
        && json.HasMember("players")
        && json.HasMember("round_state"))
    {
        std::vector<player*> deserialized_players;
        for (auto &serialized_player : json["players"].GetArray()) {
            deserialized_players.push_back(player::from_json(serialized_player.GetObject()));
        }
        return new game_state(json["id"].GetString(),
                              deserialized_players,
                              round_state::from_json(json["round_state"].GetObject()),
                              serializable_value<bool>::from_json(json["is_started"].GetObject()),
                              serializable_value<bool>::from_json(json["is_finished"].GetObject()),
                              serializable_value<int>::from_json(json["current_player_idx"].GetObject()),
                              serializable_value<int>::from_json(json["round_number"].GetObject()),
                              serializable_value<int>::from_json(json["starting_player_idx"].GetObject()));


    } else {
        throw WizardException("Failed to deserialize game_state. Required entries were missing.");
    }
}