diff options
author | Vegard Storheil Eriksen <zyp@jvnv.net> | 2010-12-25 12:54:59 +0100 |
---|---|---|
committer | Vegard Storheil Eriksen <zyp@jvnv.net> | 2010-12-25 12:54:59 +0100 |
commit | fae209a9e93400c3a2072befda9c820634cf9278 (patch) | |
tree | 2d69e2c75fff0e08468c168f773abbc939a2ff03 /src/game.cpp | |
parent | 94a1189d757f0269ac081ad2d750152e30564986 (diff) |
Diffstat (limited to 'src/game.cpp')
-rw-r--r-- | src/game.cpp | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..e791379 --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,456 @@ +#include "game.h" + +#include <boost/bind.hpp> + +#include <iostream> +#include <algorithm> +#include <map> + +#ifdef DEBUG +#include <ctime> +#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<std::string> 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<int, Actions> 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<int, Actions>::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<Message::RoundEnd>(); + + 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<Message::GameEnd>(); + 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())); + + +} |