#include "game.h" #include #include #include #include #ifdef DEBUG #include #endif Game::p Game::create(ClientBase::p player_1, ClientBase::p player_2, ClientBase::p player_3, ClientBase::p player_4) { Game::p p(new Game(player_1, player_2, player_3, player_4)); p->start(); return p; } Game::~Game() { std::cout << "Game destroyed." << std::endl; } Game::Game(ClientBase::p player_1, ClientBase::p player_2, ClientBase::p player_3, ClientBase::p player_4) { players[0].client = player_1; players[1].client = player_2; players[2].client = player_3; players[3].client = player_4; } void Game::handle_ready() { if(--awaiting_players) { return; } std::cout << "All ready!" << std::endl; round_start(); } void Game::start() { std::cout << "Started a game with " << players[0].client->nick() << ", " << players[1].client->nick() << ", " << players[2].client->nick() << " and " << players[3].client->nick() << "." << std::endl; round_wind = 0; round_num = 0; awaiting_players = 4; std::vector pl; pl.push_back(players[0].client->nick()); pl.push_back(players[1].client->nick()); pl.push_back(players[2].client->nick()); pl.push_back(players[3].client->nick()); players[0].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl); pl.erase(pl.begin()); pl.push_back(players[0].client->nick()); players[1].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl); pl.erase(pl.begin()); pl.push_back(players[1].client->nick()); players[2].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl); pl.erase(pl.begin()); pl.push_back(players[2].client->nick()); players[3].client->game_start(boost::bind(&Game::handle_ready, shared_from_this()), pl); } void Game::round_start() { std::cout << "Starting a new round." << std::endl; // Build a new wall. wall.build(); // Clear previous dora and draw a new. dora.clear(); dora.push_back(wall.take_one()); kan_dora_pending = false; // Notify players of round start. // TODO: Tell them where wall is broken. players[0].round_start(-round_num); players[1].round_start(-round_num + 1); players[2].round_start(-round_num + 2); players[3].round_start(-round_num + 3); // Draw hands. for(int i = 0; i < 13; i++) { players[0].draw(wall.take_one()); players[1].draw(wall.take_one()); players[2].draw(wall.take_one()); players[3].draw(wall.take_one()); } // Sort hands. players[0].hand.sort(); players[1].hand.sort(); players[2].hand.sort(); players[3].hand.sort(); // Give east an extra tile. players[0].draw(wall.take_one()); current_player = 0; round_update_draw(); } void Game::round_update_draw() { // Get possible actions for current player. Actions possible_actions = players[current_player].get_actions_draw(); // Construct and send state to each client. PlayerNum player = 0; do { PlayerState state[4]; state[0] = players[player].get_state(); state[1] = players[player + 1].get_state_filtered(); state[2] = players[player + 2].get_state_filtered(); state[3] = players[player + 3].get_state_filtered(); Actions a; if(player == current_player) { a = possible_actions; } GameState gstate = {dora, current_player - player, round_wind, round_num, 0, 0}; players[player].client->round_state(state[0], state[1], state[2], state[3], gstate, a); } while(++player); // Await action from client. players[current_player].client->get_action(boost::bind(&Game::handle_action_draw, shared_from_this(), _1), possible_actions); } void Game::round_update_discard() { Tile discarded_tile = players[current_player].last_discard(); std::map possible_actions; // Construct and send state to each client. PlayerNum player = 0; do { PlayerState state[4]; state[0] = players[player].get_state(); state[1] = players[player + 1].get_state_filtered(); state[2] = players[player + 2].get_state_filtered(); state[3] = players[player + 3].get_state_filtered(); Actions a; if(player != current_player) { if(a = players[player].get_actions_discard(discarded_tile, current_player - player)) { possible_actions[player] = a; } } GameState gstate = {dora, current_player - player, round_wind, round_num, 0, 0}; players[player].client->round_state(state[0], state[1], state[2], state[3], gstate, a); } while(++player); preceding_action = Action::Pass; if(possible_actions.empty()) { awaiting_players = 1; handle_action_discard(Action::Pass, 0); } else { awaiting_players = possible_actions.size(); for(std::map::iterator it = possible_actions.begin(); it != possible_actions.end(); it++) { players[it->first].client->get_action(boost::bind(&Game::handle_action_discard, shared_from_this(), _1, it->first), it->second); } } } void Game::handle_action_draw(Action action) { switch(action.type) { case Action::Discard: { players[current_player].discard(action.target_offset); if(kan_dora_pending) { kan_dora_pending = false; dora.push_back(wall.take_one()); } round_update_discard(); } break; case Action::Tsumo: { players[current_player].declare_tsumo(); round_end(Tsumo); } break; case Action::Riichi: { players[current_player].declare_riichi(); round_update_draw(); } break; case Action::Kan: { if(action.target_type == Action::Index) { players[current_player].make_kan(action.target_offset - 2); dora.push_back(wall.take_one()); } else { players[current_player].make_kan_extend(action.target_offset); kan_dora_pending = true; } players[current_player].draw(wall.take_one()); round_update_draw(); } break; case Action::Draw: // Not implemented yet. // Will never occur on draw: case Action::Pass: case Action::Chi: case Action::Pon: case Action::Ron: break; } } void Game::handle_action_discard(Action action, int player) { switch(preceding_action.type) { case Action::Pass: case Action::Chi: if(action.type == Action::Chi) { preceding_action = action; preceding_action_owner = player; break; } case Action::Kan: if(action.type == Action::Kan) { preceding_action = action; preceding_action_owner = player; break; } case Action::Pon: if(action.type == Action::Pon) { preceding_action = action; preceding_action_owner = player; break; } // First ron. if(action.type == Action::Ron) { preceding_action = action; preceding_action_owner = player; break; } // Multiple ron. case Action::Ron: if(action.type == Action::Ron && current_player - player > current_player - preceding_action_owner) { preceding_action = action; preceding_action_owner = player; break; } break; // Will never occur on discard: case Action::Discard: case Action::Riichi: case Action::Tsumo: case Action::Draw: break; } if(--awaiting_players) { return; } switch(preceding_action.type) { case Action::Pass: { // Tile not claimed, next player draws. // Check if the wall has run out. if(wall.remaining() <= 14) { round_end(Draw); break; } players[++current_player].draw(wall.take_one()); round_update_draw(); } break; case Action::Chi: { players[preceding_action_owner].make_chi(players[current_player].claim(), preceding_action.target_offset); current_player = preceding_action_owner; round_update_draw(); } break; case Action::Pon: { players[preceding_action_owner].make_pon(players[current_player].claim(), preceding_action.target_offset - 1, current_player - preceding_action_owner); current_player = preceding_action_owner; round_update_draw(); } break; case Action::Kan: { players[preceding_action_owner].make_kan(players[current_player].claim(), preceding_action.target_offset - 2, current_player - preceding_action_owner); current_player = preceding_action_owner; kan_dora_pending = true; players[current_player].draw(wall.take_one()); round_update_draw(); } break; case Action::Ron: { players[preceding_action_owner].declare_ron(players[current_player].claim()); round_end(Ron); } break; // Will never occur on discard: case Action::Discard: case Action::Riichi: case Action::Tsumo: case Action::Draw: break; } } Tiles merge_hand_open(const Tiles& hand, const Sets& open) { Tiles tiles = hand; for(Sets::const_iterator it = open.begin(); it != open.end(); it++) { tiles.insert(tiles.end(), it->tiles.begin(), it->tiles.end()); } return tiles; } void Game::round_end(Endcondition end) { Message::RoundEnd::p msg[4] = make_shared(); for(CyclicInt<4> count_player = 0; count_player < 4; count_player++) { msg[count_player]->total_han = msg[count_player]->total_fu = msg[count_player]->won = msg[count_player]->score[count_player].won = 0; for(int i = 0; i < 4; i++) { msg[count_player]->score[count_player + i].score = players[count_player + i].get_state().score; msg[count_player]->score[count_player + i].won = 0; } switch(end) { case Draw: //msg->total_han = msg->total_fu = msg->won = 0; break; case Tsumo: { Player& player = players[current_player]; msg[count_player]->hand = merge_hand_open(player.hand, player.open); msg[count_player]->yakus = player.won_han; msg[count_player]->total_han = player.won_value.han(); msg[count_player]->total_fu = player.won_value.fu_rounded(); if(current_player == round_num) { msg[count_player]->won = 3 * player.won_value.tsumo_east(); for(int y = 0; y < 4; y++) { msg[count_player]->score[current_player + y].won = -(msg[count_player]->won / 3); } msg[count_player]->score[count_player + current_player ].won = msg[count_player]->won; } else { msg[count_player]->won = player.won_value.tsumo_east() + 2 * player.won_value.tsumo(); for(int y = 0; y < 4; y++) { if(count_player + y == current_player) { msg[count_player]->score[y].won = msg[count_player]->won; } else if (count_player + y == round_num){ msg[count_player]->score[y].won = -(player.won_value.tsumo_east()); } else { msg[count_player]->score[y].won = -(player.won_value.tsumo()); } } } } break; case Ron: for(int i = 0; i < 4; i++) { if(players[current_player + i].won) { Player& player = players[current_player + i]; msg[count_player]->hand = merge_hand_open(player.hand, player.open); msg[count_player]->yakus = player.won_han; msg[count_player]->total_han = player.won_value.han(); msg[count_player]->total_fu = player.won_value.fu_rounded(); if(current_player + i == round_num) { msg[count_player]->won = player.won_value.ron_east(); msg[count_player]->score[current_player + i].won = player.won_value.ron_east(); msg[count_player]->score[current_player].won = -(player.won_value.ron_east()); } else { msg[count_player]->won = player.won_value.ron(); msg[count_player]->score[current_player + i].won = player.won_value.ron(); msg[count_player]->score[current_player].won = -(player.won_value.ron()); } // TODO: Support multiple wins. break; } } break; } //Send med score msg[count_player]->game_end = false; if(count_player == 3) break; } round_num++; if(!round_num) { round_wind++; //Do a 4-round game for now for(int i = 0; i < 4; ++i) { msg[i]->game_end = true; players[i].client->round_end(msg[i], 0); } game_end(); } else { awaiting_players = 4; players[0].client->round_end(msg[0], boost::bind(&Game::handle_ready, shared_from_this())); players[1].client->round_end(msg[1], boost::bind(&Game::handle_ready, shared_from_this())); players[2].client->round_end(msg[2], boost::bind(&Game::handle_ready, shared_from_this())); players[3].client->round_end(msg[3], boost::bind(&Game::handle_ready, shared_from_this())); } // Ferdig? game_end() } void Game::game_end() { Message::GameEnd::p msg = make_shared(); for(int i = 0; i < 4; i++) { msg->scores[i].score = players[i].score; msg->scores[i].won = players[i].score - 25000; } awaiting_players = 4; players[0].client->game_end(msg, boost::bind(&Game::handle_ready, shared_from_this())); players[1].client->game_end(msg, boost::bind(&Game::handle_ready, shared_from_this())); players[2].client->game_end(msg, boost::bind(&Game::handle_ready, shared_from_this())); players[3].client->game_end(msg, boost::bind(&Game::handle_ready, shared_from_this())); }