From fae209a9e93400c3a2072befda9c820634cf9278 Mon Sep 17 00:00:00 2001 From: Vegard Storheil Eriksen Date: Sat, 25 Dec 2010 12:54:59 +0100 Subject: Restructured repository. --- src/player.cpp | 595 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 src/player.cpp (limited to 'src/player.cpp') diff --git a/src/player.cpp b/src/player.cpp new file mode 100644 index 0000000..a267b1d --- /dev/null +++ b/src/player.cpp @@ -0,0 +1,595 @@ +#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; +} -- cgit v1.2.3