#include "game.h" #include "hand.h" void Game::Player::round_start() { // Reset contents. hand.clear(); open.clear(); pond.clear(); riichi = false; // Notify client of round start. client->round_start(); } PlayerState Game::Player::get_state() { Tilegroups h = open; h.insert(h.begin(), hand); PlayerState state = {h, pond}; return state; } PlayerState Game::Player::get_state_filtered() { Tilegroups h = open; h.insert(h.begin(), hand); PlayerState state = {h, pond}; return state; } Actions Game::Player::get_actions_draw() { Actions possible_actions; // Check if player can force a draw. // Check if player can tsumo. if(can_tsumo()) { possible_actions.push_back(Action(Action::Tsumo)); } // Check if player can declare a concealed kan or extend an open pon. List all possibilities. if(!riichi) { // Check if player can riichi. if(can_riichi()) { possible_actions.push_back(Action(Action::Riichi)); } // List all tiles that can be discarded. for(std::size_t i = 0; i < hand.size(); i++) { possible_actions.push_back(Action(Action::Discard, Action::Hand, i)); } } else { // Only tile that can be discarded is the last drawn one. possible_actions.push_back(Action(Action::Discard, Action::Hand, hand.size() - 1)); } return possible_actions; } Actions Game::Player::get_actions_discard(Tile tile, PlayerNum discarder) { Actions possible_actions; if(!riichi) { // Check if tile can be called for a chi. Enumerate all combinations. if(discarder == 3) { Targets targets = can_chi(tile); if(!targets.empty()) { for(Targets::iterator it = targets.begin(); it != targets.end(); it++) { possible_actions.push_back(Action(Action::Chi, Action::Hand, *it)); } } } // Check if tile can be called for a pon. int target = can_pon(tile); if(target > 0) { possible_actions.push_back(Action(Action::Pon, Action::Hand, target + 1)); // Check if tile can be called for a kan. if(can_kan(tile, target)) { possible_actions.push_back(Action(Action::Kan, Action::Hand, target + 2)); } } } // Check if tile can be ron-ed. if(can_ron(tile)) { possible_actions.push_back(Action(Action::Ron)); } // If any action is possible, add option to pass. if(possible_actions) { possible_actions.push_back(Action::Pass); } return possible_actions; } bool Game::Player::can_riichi() { if(open) { return false; } Tiles tiles = hand; tiles.sort(); // Take first tile out of the set. Tile out = tiles.front(); tiles.erase(tiles.begin()); // Iterate over the rest of the set, exchanging the tile until a tenpai is found or all is tested. for(Tiles::iterator it = tiles.begin(); it != tiles.end(); it++) { if(Hand::tenpai(tiles)) { return true; } // TODO: Skip unnecessary tests if player got several equal tiles on hand. std::swap(*it, out); } return false; } Game::Player::Targets Game::Player::can_chi(Tile tile) { Targets targets; Tile::Set set = tile.get_set(); int num = tile.get_num(); // Check if tile actually can be chi-ed. if(set == Tile::Honor) { return targets; } bool have_above = false; // Check if we have tile below. Tiles::iterator it = std::find(hand.begin(), hand.end(), Tile(set, num - 1)); if(num > 1 && it != hand.end()) { // Check if we also have tile below tile below. Tiles::iterator it2 = std::find(hand.begin(), hand.end(), Tile(set, num - 2)); if(num > 2 && it2 != hand.end()) { targets.push_back(it2 - hand.begin()); // (T-2 T-1 T) } // Check if we have tile above. it2 = std::find(hand.begin(), hand.end(), Tile(set, num + 1)); if(num < 9 && it2 != hand.end()) { targets.push_back(it - hand.begin()); // (T-1 T T+1) have_above = true; it = it2; } } // Check if we have tile above. if(have_above || (it = std::find(hand.begin(), hand.end(), Tile(set, num + 1))) != hand.end()) { // Check if we have tile above tile above. Tiles::iterator it2 = std::find(hand.begin(), hand.end(), Tile(set, num + 2)); if(num < 8 && it2 != hand.end()) { targets.push_back(it - hand.begin()); // (T T+1 T+2) } } return targets; } int Game::Player::can_pon(Tile tile) { Tiles::iterator it = std::find(hand.begin(), hand.end(), tile); if(it + 1 < hand.end() && it[1] == tile) { return it - hand.begin(); } return -1; } //Game::Player::Targets Game::Player::can_kan() { // return Targets(); // TODO //} bool Game::Player::can_kan(Tile tile, int target) { if(std::size_t(target + 2) < hand.size() && hand[target + 2] == tile) { return true; } return false; } bool Game::Player::can_tsumo() { Tiles tiles = hand; tiles.sort(); return Hand::agari(tiles); } bool Game::Player::can_ron(Tile tile) { Tiles tiles = hand; tiles.push_back(tile); tiles.sort(); return Hand::agari(tiles); } void Game::Player::draw(Tile tile) { hand.push_back(tile); } void Game::Player::discard(int target) { Tile tile = hand[target]; hand.erase(hand.begin() + target); hand.sort(); pond.push_back(tile); } Tile Game::Player::last_discard() { return pond.back(); } Tile Game::Player::claim() { Tile& t = pond.back(); t.invisible = true; return t; } void Game::Player::make_chi(Tile tile, int target) { Tiles chi; tile.rotated = true; chi.push_back(tile); Tile::Set set = tile.get_set(); int num = tile.get_num(); Tiles::iterator it = hand.begin(); switch(hand[target].get_num() - num) { case -2: it = std::find(it, hand.end(), Tile(set, num - 2)); chi.push_back(*it); it = hand.erase(it); case -1: it = std::find(it, hand.end(), Tile(set, num - 1)); chi.push_back(*it); it = hand.erase(it); case 1: if(chi.size() == 3) { break; } it = std::find(it, hand.end(), Tile(set, num + 1)); chi.push_back(*it); it = hand.erase(it); if(chi.size() == 3) { break; } it = std::find(it, hand.end(), Tile(set, num + 2)); chi.push_back(*it); hand.erase(it); } open.push_back(chi); } void Game::Player::make_pon(Tile tile, int target, PlayerNum discarder) { Tiles pon; tile.rotated = true; if(discarder == 3) { pon.push_back(tile); } pon.push_back(hand[target]); hand.del(target); if(discarder == 2) { pon.push_back(tile); } pon.push_back(hand[target]); hand.del(target); if(discarder == 1) { pon.push_back(tile); } open.push_back(pon); } void Game::Player::make_kan(Tile tile, int target, PlayerNum discarder) { Tiles kan; tile.rotated = true; if(discarder == 3) { kan.push_back(tile); } kan.push_back(hand[target]); hand.del(target); if(discarder == 2) { kan.push_back(tile); } kan.push_back(hand[target]); hand.del(target); kan.push_back(hand[target]); hand.del(target); if(discarder == 1) { kan.push_back(tile); } open.push_back(kan); }