#include "game.h" #include "hand.h" #include using boost::assign::list_of; void Player::round_start(int w) { // Reset contents. hand.clear(); open.clear(); pond.clear(); tenpai_indexes.clear(); riichi = false; score = 25000; wind = w; won = 0; // Notify client of round start. client->round_start(); } PlayerState Player::get_state() { Tilegroups h; h.push_back(hand); for(Sets::iterator it = open.begin(); it != open.end(); it++) { h.push_back(it->tiles); } PlayerState state = {h, pond, riichi, score, wind}; return state; } PlayerState Player::get_state_filtered() { Tiles hand_filtered; for(int i = 0; i < hand.size(); i++) { hand_filtered.push_back(Tile::Back); } Tilegroups h; h.push_back(hand_filtered); for(Sets::iterator it = open.begin(); it != open.end(); it++) { h.push_back(it->tiles); } PlayerState state = {h, pond, riichi, score, wind}; return state; } Actions Player::get_actions_draw() { Actions possible_actions; // Check if player can force a draw. // TODO: Implementation. (Low priority) // Check if player can tsumo. if(can_tsumo()) { possible_actions.push_back(Action(Action::Tsumo)); } // Check if player can declare a concealed kan. Targets targets = can_kan(); for(Targets::iterator it = targets.begin(); it != targets.end(); it++) { possible_actions.push_back(Action(Action::Kan, Action::Index, *it + 2)); } // Check if player can extend an open pon. targets = can_kan_extend(); for(Targets::iterator it = targets.begin(); it != targets.end(); it++) { possible_actions.push_back(Action(Action::Kan, Action::Group, *it)); } 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++) { // Skip tiles already used to call kan. if(possible_actions.contains(Action(Action::Kan, Action::Index, i))) { continue; } possible_actions.push_back(Action(Action::Discard, Action::Index, i)); } } else { if(tenpai_indexes) { for(List::iterator it = tenpai_indexes.begin(); it != tenpai_indexes.end(); it++) { possible_actions.push_back(Action(Action::Discard, Action::Index, *it)); } tenpai_indexes.clear(); } else { // Only tile that can be discarded is the last drawn one. possible_actions.push_back(Action(Action::Discard, Action::Index, hand.size() - 1)); } } return possible_actions; } Actions 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::Index, *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::Index, target + 1)); // Check if tile can be called for a kan. if(can_kan(tile, target)) { possible_actions.push_back(Action(Action::Kan, Action::Index, 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 Player::can_riichi() { if(open) { return false; } // Iterate over the hand, testing to remove each tile and see if it gives tenpai. for(Tiles::iterator it = hand.begin(); it != hand.end(); it++) { Tiles tiles = hand; tiles.del(it - hand.begin()); tiles.sort(); if(Hand::tenpai(tiles)) { tenpai_indexes.push_back(it - hand.begin()); } } return bool(tenpai_indexes); } Player::Targets 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 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; } Player::Targets Player::can_kan() { Targets targets; for(Tiles::iterator it = hand.begin(); it != hand.end(); it++) { Tiles::iterator it_s = it; int i = 1; do { it_s = std::find(it_s + 1, hand.end(), *it); } while(it_s != hand.end() && i++); if(i >= 4) { targets.push_back(it - hand.begin()); } } return targets; } Player::Targets Player::can_kan_extend() { Targets targets; for(Sets::iterator it = open.begin(); it != open.end(); it++) { if(it->type == Set::Pon && hand.contains(it->tiles.front())) { targets.push_back(it - open.begin() + 1); } } return targets; } bool Player::can_kan(Tile tile, int target) { if(std::size_t(target + 2) < hand.size() && hand[target + 2] == tile) { return true; } return false; } bool Player::can_tsumo() { Tiles tiles = hand; tiles.sort(); return Hand::agari(tiles); } bool Player::can_ron(Tile tile) { // Check furiten. if(pond.contains(tile)) { return false; } // TODO: Furiten due to unclaimed discard in last go-around. Tiles tiles = hand; tiles.push_back(tile); tiles.sort(); return Hand::agari(tiles); } void Player::draw(Tile tile) { hand.push_back(tile); } void Player::discard(int target) { Tile tile = hand[target]; hand.erase(hand.begin() + target); hand.sort(); pond.push_back(tile); } Tile Player::last_discard() { return pond.back(); } Tile Player::claim() { Tile& t = pond.back(); t.invisible = true; return t; } void Player::declare_riichi() { riichi = true; } void 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(Set(Set::Chi, chi, true)); } void 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(Set(Set::Pon, pon, true)); } void 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(Set(Set::Kan, kan, true)); } void Player::make_kan(int target) { Tiles kan; Tiles::iterator it = hand.begin() + target; Tile t = *it; kan.push_back(Tile::Back); it = hand.erase(it); it = std::find(it, hand.end(), t); kan.push_back(*it); it = hand.erase(it); it = std::find(it, hand.end(), t); kan.push_back(*it); it = hand.erase(it); it = std::find(it, hand.end(), t); kan.push_back(Tile::Back); hand.erase(it); open.push_back(Set(Set::Kan, kan, false)); } void Player::make_kan_extend(int target) { Set& set = open[target - 1]; Tiles::iterator it = std::find(hand.begin(), hand.end(), set.tiles.front()); Tile t = *it; hand.erase(it); t.rotated = true; for(it = set.tiles.begin(); it != set.tiles.end(); it++) { if(it->rotated) { set.tiles.insert(it + 1, t); break; } } set.type = Set::Kan; } void Player::declare_ron(Tile tile) { hand.push_back(tile); // TODO: Mark winning tile. hand.sort(); List hands = Hand::get_breakdowns(hand); Sets hand = hands.front(); hand.insert(hand.end(), open.begin(), open.end()); List > han; Score score = calculate_score(hand, han, false); won = true; won_han = han; won_value = score; } void Player::declare_tsumo() { // TODO: Mark winning tile. hand.sort(); List hands = Hand::get_breakdowns(hand); Sets hand = hands.front(); hand.insert(hand.end(), open.begin(), open.end()); List > han; Score score = calculate_score(hand, han, true); won = true; won_han = han; won_value = score; } Score Player::calculate_score(const Sets& hand, List >& han, bool tsumo) { Tiles yakuhai = list_of(Tile::Chun)(Tile::Hatsu)(Tile::Haku); // TODO: Add seat and prevalent wind. Tiles dora; // TODO: Fill this. Tiles uradora; // TODO: Fill this. Score score(0, 20, 0); // Always 20 fu for winning. // Check riichi. if(riichi) { score.yaku += 1; han.push_back(std::make_pair("Riichi", 1)); // TODO: Check ippatsu. } // Check menzen tsumo. if(tsumo && !open) { score.yaku += 1; han.push_back(std::make_pair("Menzen tsumo", 1)); } bool possible_tanyao = true; bool possible_toitoi = true; int dora_n = 0; int uradora_n = 0; int akadora_n = 0; for(Sets::const_iterator set = hand.begin(); set != hand.end(); set++) { switch(set->type) { case Set::Pair: // Check for pair-related fu. score.fu += 2 * yakuhai.count(set->tiles.front()); break; case Set::Chi: // Toitoi can't contain chi. possible_toitoi = false; break; case Set::Pon: case Set::Kan: // Calculate fu. score.fu += 2 << (!set->open + !set->tiles.front().is_simple() + 2 * (set->type == Set::Kan)); // Check yakuhai. if(int yakuhai_n = yakuhai.count(set->tiles.front())) { score.yaku += yakuhai_n; han.push_back(std::make_pair("Yakuhai", yakuhai_n)); // TODO: Specify which tile. } break; } for(Tiles::const_iterator tile = set->tiles.begin(); tile != set->tiles.end(); tile++) { // Check tanyao. if(!tile->is_simple()) { possible_tanyao = false; } // Check dora. dora_n += dora.count(*tile); // Check uradora. uradora_n += uradora.count(*tile); // Check akadora. if(tile->red) { akadora_n += 1; } } } // Check tanyao. if(possible_tanyao) { score.yaku += 1; han.push_back(std::make_pair("Tanyao", 1)); } // Check toitoi. if(possible_toitoi) { score.yaku += 2; han.push_back(std::make_pair("Toitoi", 2)); } // Check pinfu. if(score.fu == 20) { if(open) { score.fu += 2; } else { score.yaku += 1; han.push_back(std::make_pair("Pinfu", 1)); } } else if(tsumo) { score.fu += 2; } // Check fu for ron. if(!tsumo && !open) { // 10 fu for ron. score.fu += 10; } // Check dora. if(dora_n) { score.dora += dora_n; han.push_back(std::make_pair("Dora", dora_n)); } // Check uradora. if(uradora_n) { score.dora += uradora_n; han.push_back(std::make_pair("Uradora", uradora_n)); } // Check akadora. if(akadora_n) { score.dora += akadora_n; han.push_back(std::make_pair("Akadora", akadora_n)); } return score; }