diff --git a/src/game.h b/src/game.h deleted file mode 100644 index 75e729e..0000000 --- a/src/game.h +++ /dev/null @@ -1,356 +0,0 @@ -#pragma once - -#include "shan.h" -#include "he.h" -#include "hule.h" - -#include -#include - -class Game { - public: - struct Model { - int qijia = 0; // 起家 - int zhuangfeng = 0; // 庄风(場風) - int jushu = 0; // 局数 - int changbang = 0; // 场棒(積み棒) - int lizhibang = 0; // 立直棒 - std::array defen = {0, 0, 0, 0}; // 得分(得点) - Shan shan; // 山 - std::array shoupai; // 手牌 - std::array he; // 河 - std::array player_id; - int lunban; // 轮班(手番) - }; - - struct Pingju { - enum Name { - NONE, - HUANGPAI, // 荒牌平局 - MANGUAN, // 流し満貫 - YAO9, // 九種九牌 - HULE3, // 三家和了 - LIZHI4, // 四家立直 - FENG4, // 四風連打 - GANG4, // 四槓散了 - } name; - std::array shoupai; - }; - - enum class Message { - NONE, - DAPAI, // 打牌 - FULOU, // 副露 - GANG, // 杠(槓) - HULE, // 和了 - DAOPAI, // 倒牌 - }; - - enum class Status { - NONE, - KAIJU, - QIPAI, - ZIMO, - DAPAI, - FULOU, - GANG, - GANGZIMO, - HULE, - PINGJU, - JIEJI - }; - - enum class HuleOption { NONE, QIANGGANG, LINGSHANG }; - - struct Reply { - Message msg; - std::string arg; - }; - - struct Paipu { - Rule rule; - struct Round { - Model model; - std::vector moves; - }; - std::vector rounds; - - Paipu() {} - Paipu(const std::string& paipu_str) { - extern std::istream& operator>>(std::istream& is, Game::Paipu& paipu); - std::stringstream ss(paipu_str); - ss >> *this; - } - }; - - Game(const Rule& rule, const bool paipu = false); - Game() : Game(Rule{}, false) {}; - - void set(const std::array& shoupai, const std::array& he, - const Shan& shan, const int lunban); - void set(const Paipu::Round& round); - - void call_players(const Status type); - void reply(const int id, const Message msg, const std::string& arg = {}); - void next(); - - // 手番のプレイヤーID - int lunban_player_id() const { return _model.player_id[_model.lunban]; } - // プレイヤーの手番 - int player_lunban(const int player_id) const { - if (_model.player_id[0] == player_id) return 0; - if (_model.player_id[1] == player_id) return 1; - if (_model.player_id[2] == player_id) return 2; - return 3; - } - - // 开局(半荘の開始) - void kaiju(const int qijia = -1); - // 起牌(配牌) - void qipai_(const Shan& shan); - void qipai(); - // 自摸 - void zimo(); - // 打牌 - void dapai(const std::string& p); - // 副露 - void fulou(const std::string& m); - // 杠(槓) - void gang(const std::string& m); - // 杠自摸(槓自摸) - void gangzimo(); - // 开杠(開槓) - void kaigang(); - // 和了 - void hule(); - // 流局 - void pingju(Pingju::Name name = Pingju::NONE, - std::array shoupai = {}); - // 終了 - void last(); - // 结局(終局) - void jieju(); - - // 応答取得 - const Reply& get_reply(const int l) const; - - // 结局(終局)の応答 - void reply_kaiju(); - // 起牌(配牌)の応答 - void reply_qipai(); - // 自摸の応答 - void reply_zimo(); - // 打牌の応答 - void reply_dapai(); - // 副露の応答 - void reply_fulou(); - // 杠(槓)の応答 - void reply_gang(); - // 和了の応答 - void reply_hule(); - // 流局 - void reply_pingju(); - - // 打牌取得 - std::vector get_dapai() const; - // 吃(チー)面子取得 - std::vector get_chi_mianzi(const int l) const; - // 碰(ポン)面子取得 - std::vector get_peng_mianzi(const int l) const; - // 杠(槓)面子取得 - std::vector get_gang_mianzi(const int l = -1) const; - - // 立直可能 - std::pair> allow_lizhi( - const std::string& p = {}) const; - // 和了可能 - bool allow_hule(const int l = -1) const; - // 流局可能 - bool allow_pingju() const; - - // 打牌取得 - static std::vector _get_dapai(const Rule& rule, - const Shoupai& shoupai); - // 吃(チー)面子取得 - static std::vector _get_chi_mianzi(const Rule& rule, - const Shoupai& shoupai, - const std::string& p, - const int paishu); - // 碰(ポン)面子取得 - static std::vector _get_peng_mianzi(const Rule& rule, - const Shoupai& shoupai, - const std::string& p, - const int paishu); - // 杠(槓)面子取得 - static std::vector _get_gang_mianzi(const Rule& rule, - const Shoupai& shoupai, - const std::string& p, - const int paishu, - const int n_gang = -1); - - // 立直可能 - static std::pair> _allow_lizhi( - const Rule& rule, const Shoupai& shoupai, const std::string& p = {}, - const int paishu = INT_MAX, const int defen = INT_MAX); - // 和了可能 - static bool _allow_hule(const Rule& rule, const Shoupai& shoupai, - const std::string& p, const int zhuangfeng, - const int menfeng, const bool hupai, - const bool neng_rong = true); - // 流局可能 - static bool _allow_pingju(const Rule& rule, const Shoupai& shoupai, - const bool diyizimo); - // ノーテン宣言可能 - static bool _allow_no_daopai(const Rule& rule, const Shoupai& shoupai, - const int paishu); - - int qijia() const { return _model.qijia; } - int zhuangfeng() const { return _model.zhuangfeng; } - int jushu() const { return _model.jushu; } - int changbang() const { return _model.changbang; } - int lizhibang() const { return _model.lizhibang; } - const std::array& defen() const { return _model.defen; } - const Shan& shan() const { return _model.shan; } - - const std::array& shoupai() const { return _model.shoupai; } - const Shoupai& shoupai_(const int l) const { return _model.shoupai[l]; } - - const std::array& he() const { return _model.he; } - const He& he_(const int l) const { return _model.he[l]; } - - const std::array& player_id() const { return _model.player_id; } - int lunban() const { return _model.lunban; } - - int max_jushu() const { return _max_jushu; } - - bool diyizimo() const { return _diyizimo; } - void set_diyizimo(const bool diyizimo_) { _diyizimo = diyizimo_; } - - const bool fengpai() const { return _fengpai; } - - const std::string& dapai_() const { return _dapai; } - - const std::array& lizhi() const { return _lizhi; } - int lizhi_(const int l) const { return _lizhi[l]; } - - const std::array& yifa() const { return _yifa; } - bool yifa_(const int l) const { return _yifa[l]; } - void set_yifa(const std::array& yifa_) { _yifa = yifa_; } - void set_yifa_(const int l, const bool yifa_) { _yifa[l] = yifa_; } - - void set_n_gang(const std::array& n_gang_) { _n_gang = n_gang_; } - - const std::array& neng_rong() const { return _neng_rong; } - bool neng_rong_(const int l) const { return _neng_rong[l]; } - void set_neng_rong(const std::array& neng_rong_) { - _neng_rong = neng_rong_; - } - void set_neng_rong_(const int l, const bool neng_rong_) { - _neng_rong[l] = neng_rong_; - } - - const std::string& fulou_() const { return _fulou; } - const std::string& gang_() const { return _gang; } - - const std::vector& hule_() const { return _hule; } - void set_hule(const std::vector& hule_) { _hule = hule_; } - const Defen& defen_() const { return _defen; } - const std::array& rank() const { return _rank; } - const std::array& point() const { return _point; } - const Pingju& pingju_() const { return _pingju; } - const std::array& fenpei() const { return _fenpei; } - - bool lianzhuang() const { return _lianzhuang; } - void set_lianzhuang(const bool lianzhuang_) { _lianzhuang = lianzhuang_; } - - bool no_game() const { return _no_game; } - void set_no_game(const bool no_game_) { _no_game = no_game_; } - - Model& model() { return _model; } - const Model& model() const { return _model; } - const Rule& rule() const { return _rule; } - - const Status status() const { return _status; } - - const Paipu& paipu() const { return *_paipu; } - Paipu& paipu() { return *_paipu; } - - private: - Rule _rule; - Model _model; - - std::unique_ptr _paipu; - - Status _status; - Defen _defen; - std::array _rank; - std::array _point; - Pingju _pingju; - std::array _reply; - - // 最大局数 - int _max_jushu; - // 第一ツモ - bool _diyizimo; - // 四風連打 - bool _fengpai; - // 打牌 - std::string _dapai; - // 副露 - std::string _fulou; - // 杠(槓) - std::string _gang; - // 立直 - std::array _lizhi; - // 一発 - std::array _yifa; - // // 杠(槓) - std::array _n_gang; - // 能荣(フリテンでない状態) - std::array _neng_rong; - // 和了 - std::vector _hule; - HuleOption _hule_option; - // 流局 - bool _no_game; - // 连庄(連荘) - bool _lianzhuang; - // 场棒(積み棒) - int _changbang; - // 分配(局単位に精算する得点) - std::array _fenpei; -}; - -template -bool find_if(const T& v, F f) { - return std::find_if(v.begin(), v.end(), f) != v.end(); -} - -template -bool find(const T& v, const V& value) { - return std::find(v.begin(), v.end(), value) != v.end(); -} - -template -size_t count_if(const T& v, F f) { - return std::count_if(v.begin(), v.end(), f); -} - -template -size_t max_index(const T& v) { - return std::max_element(v.begin(), v.end()) - v.begin(); -} - -template -std::array order_by_player_id(const T& arr, - std::array player_id) { - return {arr[player_id[0]], arr[player_id[1]], arr[player_id[2]], - arr[player_id[3]]}; -} - -template -T pop_front(std::vector& v) { - const T front = v.front(); - v.erase(v.begin()); - return front; -} diff --git a/src/paipu.h b/src/paipu.h deleted file mode 100644 index 5a64118..0000000 --- a/src/paipu.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "game.h" - -class PaipuReplay { - public: - PaipuReplay(const Game::Paipu& paipu); - - void next(); - - const Game& game() const { return _game; } - int round() const { return _round; } - int ply() const { return _ply; } - Game::Status status() const { return _game.status(); } - - private: - const Game::Paipu& _paipu; - Game _game; - int _round; - int _ply; - bool _skip; - - void qipai(); -}; - -std::ostream& operator<<(std::ostream& os, const Game::Paipu& paipu); -std::istream& operator>>(std::istream& is, Game::Paipu& paipu); diff --git a/src/shoupai.cpp b/src/shoupai.cpp index a6f1fef..2a05dba 100644 --- a/src/shoupai.cpp +++ b/src/shoupai.cpp @@ -7,60 +7,6 @@ #include -// 牌文字列検証 -bool Shoupai::valid_pai(const std::string& p) { - return std::regex_match(p, _re_valid_pai()); -} - -// 面子文字列検証 -std::string Shoupai::valid_mianzi(const std::string& m) { - if (std::regex_search(m, _re_valid_mianzi1())) return {}; - const auto h = std::regex_replace(m, re_ling(), "5"); - if (std::regex_match(h, _re_valid_mianzi3())) { - return std::regex_replace(m, _re_valid_mianzi4(), "$0150"); - } else if (std::regex_match(h, _re_valid_mianzi5())) { - std::vector matches; - for (std::sregex_iterator it(m.begin(), m.end(), _re_valid_mianzi6()), end; it != end; ++it) { - matches.emplace_back(it->str()); - } - std::sort(matches.rbegin(), matches.rend()); - std::ostringstream os; - os << m[0]; - std::copy(matches.begin(), matches.end(), - std::ostream_iterator(os)); - std::smatch match; - if (std::regex_search(m, match, _re_valid_mianzi7())) { - os << match.str(); - } - return os.str(); - } else if (std::regex_match(h, _re_valid_mianzi8())) { - std::vector nn; - for (std::sregex_iterator it(h.begin(), h.end(), re_digit()), end; it != end; ++it) { - nn.emplace_back(it->str()); - } - std::sort(nn.begin(), nn.end()); - if (nn.size() != 3) return {}; - if (nn[0][0] + 1 != nn[1][0] || nn[1][0] + 1 != nn[2][0]) return {}; - std::vector matches; - for (std::sregex_iterator it(h.begin(), h.end(), _re_valid_mianzi9()), end; it != end; ++it) { - matches.emplace_back(it->str()); - } - std::sort(matches.begin(), matches.end()); - std::ostringstream os; - os << h[0]; - std::copy(matches.begin(), matches.end(), - std::ostream_iterator(os)); - // 红牌(赤牌) - const auto hongpai = std::regex_search(m, re_ling()); - if (hongpai) { - return std::regex_replace(os.str(), re_wu(), "0"); - } else { - return os.str(); - } - } - return {}; -} - void Shoupai::_set(const std::vector& qipai) { for (const auto& p : qipai) { if (p == "_") { diff --git a/src/shoupai.h b/src/shoupai.h index 16f64e6..1f5ab01 100644 --- a/src/shoupai.h +++ b/src/shoupai.h @@ -10,13 +10,122 @@ #include #include +inline const std::regex& re_ling() { + static const std::regex v{R"(0)"}; + return v; +} +inline const std::regex& re_wu() { + static const std::regex v{R"(5)"}; + return v; +} +inline const std::regex& re_digit() { + static const std::regex v{R"(\d)"}; + return v; +} +inline const std::regex& re_menqian() { + static const std::regex v{R"([\+\=\-])"}; + return v; +} +inline const std::regex& re_menqian_end() { + static const std::regex v{R"([\+\=\-]$)"}; + return v; +} + +inline const std::regex& re_angang() { + static const std::regex v{R"(^[mpsz](\d)\1\1\1$)"}; + return v; +} +inline const std::regex& re_peng_gang() { + static const std::regex v{R"(^[mpsz](\d)\1\1\1?[\+\=\-]\1?$)"}; + return v; +} +inline const std::regex& re_jiagang() { + static const std::regex v{R"([\+\=\-]\d$)"}; + return v; +} +inline const std::regex& re_chi1() { + static const std::regex v{R"(\d(?=\-))"}; + return v; +} +inline const std::regex& re_chi2() { + static const std::regex v{R"(\d(?!\-))"}; + return v; +} +inline const std::regex& re_gang() { + static const std::regex v{R"(^[mpsz]\d\d\d[\+\=\-]?\d[\+\=\-]?$)"}; + return v; +} + +inline const std::vector& zipai_n() { + static const std::vector v{1, 2, 3, 4, 5, 6, 7}; + return v; +} +inline const std::vector& yaojiu_n() { + static const std::vector v{1, 9}; + return v; +} + // 手牌 class Shoupai { public: // 牌文字列検証 - static bool valid_pai(const std::string& p); + static inline bool valid_pai(const std::string& p) { + return std::regex_match(p, _re_valid_pai()); + } + // 面子文字列検証 - static std::string valid_mianzi(const std::string& m); + static inline std::string valid_mianzi(const std::string& m) { + if (std::regex_search(m, _re_valid_mianzi1())) return {}; + const auto h = std::regex_replace(m, re_ling(), "5"); + if (std::regex_match(h, _re_valid_mianzi3())) { + return std::regex_replace(m, _re_valid_mianzi4(), "$0150"); + } else if (std::regex_match(h, _re_valid_mianzi5())) { + std::vector matches; + for (std::sregex_iterator it(m.begin(), m.end(), _re_valid_mianzi6()), + end; + it != end; ++it) { + matches.emplace_back(it->str()); + } + std::sort(matches.rbegin(), matches.rend()); + std::ostringstream os; + os << m[0]; + std::copy(matches.begin(), matches.end(), + std::ostream_iterator(os)); + std::smatch match; + if (std::regex_search(m, match, _re_valid_mianzi7())) { + os << match.str(); + } + return os.str(); + } else if (std::regex_match(h, _re_valid_mianzi8())) { + std::vector nn; + for (std::sregex_iterator it(h.begin(), h.end(), re_digit()), end; + it != end; ++it) { + nn.emplace_back(it->str()); + } + std::sort(nn.begin(), nn.end()); + if (nn.size() != 3) return {}; + if (nn[0][0] + 1 != nn[1][0] || nn[1][0] + 1 != nn[2][0]) return {}; + std::vector matches; + for (std::sregex_iterator it(h.begin(), h.end(), _re_valid_mianzi9()), + end; + it != end; ++it) { + matches.emplace_back(it->str()); + } + std::sort(matches.begin(), matches.end()); + std::ostringstream os; + os << h[0]; + std::copy(matches.begin(), matches.end(), + std::ostream_iterator(os)); + // 红牌(赤牌) + const auto hongpai = std::regex_search(m, re_ling()); + if (hongpai) { + return std::regex_replace(os.str(), re_wu(), "0"); + } else { + return os.str(); + } + } + return {}; + } Shoupai(); Shoupai(const std::vector& qipai); @@ -232,58 +341,3 @@ inline std::string to_string(const char s, const int n1, const int n2, inline int to_int(const char s) { return s - '0'; } inline char to_char(const int n) { return (char)(n + '0'); } - -inline const std::regex& re_ling() { - static const std::regex v{R"(0)"}; - return v; -} -inline const std::regex& re_wu() { - static const std::regex v{R"(5)"}; - return v; -} -inline const std::regex& re_digit() { - static const std::regex v{R"(\d)"}; - return v; -} -inline const std::regex& re_menqian() { - static const std::regex v{R"([\+\=\-])"}; - return v; -} -inline const std::regex& re_menqian_end() { - static const std::regex v{R"([\+\=\-]$)"}; - return v; -} - -inline const std::regex& re_angang() { - static const std::regex v{R"(^[mpsz](\d)\1\1\1$)"}; - return v; -} -inline const std::regex& re_peng_gang() { - static const std::regex v{R"(^[mpsz](\d)\1\1\1?[\+\=\-]\1?$)"}; - return v; -} -inline const std::regex& re_jiagang() { - static const std::regex v{R"([\+\=\-]\d$)"}; - return v; -} -inline const std::regex& re_chi1() { - static const std::regex v{R"(\d(?=\-))"}; - return v; -} -inline const std::regex& re_chi2() { - static const std::regex v{R"(\d(?!\-))"}; - return v; -} -inline const std::regex& re_gang() { - static const std::regex v{R"(^[mpsz]\d\d\d[\+\=\-]?\d[\+\=\-]?$)"}; - return v; -} - -inline const std::vector& zipai_n() { - static const std::vector v{1, 2, 3, 4, 5, 6, 7}; - return v; -} -inline const std::vector& yaojiu_n() { - static const std::vector v{1, 9}; - return v; -} diff --git a/src/xiangting.cpp b/src/xiangting.cpp index 61fa56f..3ae92d1 100644 --- a/src/xiangting.cpp +++ b/src/xiangting.cpp @@ -3,7 +3,7 @@ #include namespace { -std::vector shoupai_to_hand(const Shoupai& shoupai) { +std::vector shoupai_to_table(const Shoupai& shoupai) { Rcpp::IntegerVector m = Rcpp::wrap(shoupai.m()); Rcpp::IntegerVector p = Rcpp::wrap(shoupai.p()); Rcpp::IntegerVector s = Rcpp::wrap(shoupai.s()); @@ -22,7 +22,7 @@ std::vector shoupai_to_hand(const Shoupai& shoupai) { // 一般形 int xiangting_yiban(const Shoupai& shoupai, const Calsht& calsht) { auto shoupai_ = shoupai; - const std::vector hand = shoupai_to_hand(shoupai); + const std::vector hand = shoupai_to_table(shoupai); auto [sht, mode] = calsht(hand, std::accumulate(hand.begin(), hand.end(), 0) / 3, 1); return sht; @@ -31,7 +31,7 @@ int xiangting_yiban(const Shoupai& shoupai, const Calsht& calsht) { // 国士無双形 int xiangting_guoshi(const Shoupai& shoupai, const Calsht& calsht) { if (shoupai.fulou_().size()) return INT_MAX; - const std::vector hand = shoupai_to_hand(shoupai); + const std::vector hand = shoupai_to_table(shoupai); auto [sht, mode] = calsht(hand, std::accumulate(hand.begin(), hand.end(), 0) / 3, 4); return sht; @@ -40,7 +40,7 @@ int xiangting_guoshi(const Shoupai& shoupai, const Calsht& calsht) { // 七対子形 int xiangting_qidui(const Shoupai& shoupai, const Calsht& calsht) { if (shoupai.fulou_().size()) return INT_MAX; - const std::vector hand = shoupai_to_hand(shoupai); + const std::vector hand = shoupai_to_table(shoupai); auto [sht, mode] = calsht(hand, std::accumulate(hand.begin(), hand.end(), 0) / 3, 2); return sht; @@ -48,7 +48,7 @@ int xiangting_qidui(const Shoupai& shoupai, const Calsht& calsht) { // 実際のシャンテン数(一般形、国士無双形、七対子形の最小値) int xiangting(const Shoupai& shoupai, const Calsht& calsht) { - std::vector hand = shoupai_to_hand(shoupai); + std::vector hand = shoupai_to_table(shoupai); auto [sht, mode] = calsht(hand, std::accumulate(hand.begin(), hand.end(), 0) / 3, 7); // 副露直後なら1を足す