diff --git a/CMakeLists.txt b/CMakeLists.txt index 1db40fb1..d35b4f52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,19 +94,17 @@ if(libmahjong_build_tests) add_subdirectory(tests) endif() -if(libmahjong_use_clang_utils) - find_program(clang_format_path NAMES clang-format) - if(clang_format_path) - set(clang_format_flags -i) - get_target_property(libmahjong_sources libmahjong SOURCES) - get_target_property(libmahjong_includes libmahjong HEADER_SET) - set(format_files ${libmahjong_sources} ${libmahjong_includes} - ${test_sources} ${tool_sources}) - add_custom_target( - format - COMMAND ${clang_format_path} ${clang_format_flags} ${format_files} - WORKING_DIRECTORY ${libmahjong_SOURCE_DIR} - VERBATIM) - set_target_properties(format PROPERTIES FOLDER code_utils) - endif() +find_program(clang_format_path NAMES clang-format) +if(clang_format_path) + set(clang_format_flags -i) + get_target_property(libmahjong_sources libmahjong SOURCES) + get_target_property(libmahjong_includes libmahjong HEADER_SET) + set(format_files ${libmahjong_sources} ${libmahjong_includes} ${test_sources} + ${tool_sources}) + add_custom_target( + format + COMMAND ${clang_format_path} ${clang_format_flags} ${format_files} + WORKING_DIRECTORY ${libmahjong_SOURCE_DIR} + VERBATIM) + set_target_properties(format PROPERTIES FOLDER code_utils) endif() diff --git a/src/analysis/analysis.cc b/src/analysis/analysis.cc index e648f56e..5cde3e4b 100644 --- a/src/analysis/analysis.cc +++ b/src/analysis/analysis.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -24,7 +25,7 @@ void countPieces(Breakdown* b) { std::vector AnalyzeHand(const Hand& hand, const bool only_complete) { Branch base_branch; base_branch.open = !hand.melds.empty(); - for (const Meld& meld : hand.melds) { + for (const Meld& meld : hand.melds_range()) { switch (meld.type) { case SetType::kChi: base_branch.chis.emplace_back(meld.start); @@ -41,7 +42,7 @@ std::vector AnalyzeHand(const Hand& hand, const bool only_complete) { } } std::vector branches; - const std::unique_ptr root = breakdownHand(hand.live); + const std::unique_ptr root = breakdownHand(hand.live_range()); for (const auto& branch_vec : Node::AsBranchVectors(root.get())) { branches.push_back(base_branch); Branch& branch = branches.back(); @@ -73,11 +74,11 @@ std::vector AnalyzeHand(const Hand& hand, const bool only_complete) { return branches; } -std::unique_ptr breakdownHand(const std::vector& pieces) { +std::unique_ptr breakdownHand(const std::span& pieces) { Breakdown b; b.rootNode = std::make_unique(/*id=*/b.id++); b.currentNode = b.rootNode.get(); - b.pieces = pieces; + b.pieces = std::vector(pieces.begin(), pieces.end()); countPieces(&b); std::ranges::sort(b.pieces); auto [begin, end] = std::ranges::unique(b.pieces); diff --git a/src/analysis/analysis.h b/src/analysis/analysis.h index fd2b100f..aa16f4dd 100644 --- a/src/analysis/analysis.h +++ b/src/analysis/analysis.h @@ -17,6 +17,6 @@ struct Branch { bool complete = true; }; -std::unique_ptr breakdownHand(const std::vector& pieces); +std::unique_ptr breakdownHand(const std::span& pieces); std::vector AnalyzeHand(const Hand& hand, bool only_complete = false); } // namespace mahjong diff --git a/src/analysis/util.cc b/src/analysis/util.cc index 930df1b3..683ef5d3 100644 --- a/src/analysis/util.cc +++ b/src/analysis/util.cc @@ -54,7 +54,7 @@ std::vector completeSet(const Piece a, const Piece b) { std::vector getWaits(const Hand& hand) { // 13, 10, 7, 4, 1 are possible piece counts for non-melded tiles - if (hand.live.empty() || hand.live.size() % 3 != 1) { + if (hand.live_count == 0 || hand.live_count % 3 != 1) { return {}; } constexpr int kMaxSingles = 2; @@ -75,13 +75,17 @@ std::vector getWaits(const Hand& hand) { std::vector getWaits(const Hand& hand, const Piece& piece) { Hand new_hand = hand; - new_hand.live.erase(std::ranges::find(new_hand.live, piece)); + // This will move all pieces matching piece to the end of the array, but + // we will only decrease the count by 1 so only a single piece will be + // removed. + std::ranges::remove(new_hand.live, piece); + new_hand.live_count--; return getWaits(new_hand); } std::map> getPossibleWaits(const Hand& hand) { // 14, 11, 8, 5, 2 are the possible piece counts for non-melded tiles. - if (hand.live.empty() || hand.live.size() % 3 != 2) { + if (hand.live_count == 0 || hand.live_count % 3 != 2) { return {}; } constexpr int kMaxSingles = 3; diff --git a/src/api/gamestate.cc b/src/api/gamestate.cc index 6a5003d4..b4966d0b 100644 --- a/src/api/gamestate.cc +++ b/src/api/gamestate.cc @@ -8,6 +8,7 @@ #include "controllers/controllermanager.h" #include "statefunctions/statecontroller.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/settings.h" #include "types/statefunction.h" @@ -140,23 +141,23 @@ CObservedGameState ObserveGameState(mahjong::GameState* state) { // Player data for (int i = 0; i < 4; i++) { - observed.scores[i] = state->scores[i]; - observed.points[i] = state->players[i].points; - observed.hasRonned[i] = state->hasRonned[i]; + observed.scores[i] = state->hands[i].score; + observed.points[i] = state->hands[i].points; + observed.hasRonned[i] = state->hands[i].hasRonned; // Convert Hand to CHand const auto& cpp_hand = state->hands[i]; CHand& c_hand = observed.hands[i]; // Live pieces - const int live_piece_count = static_cast(cpp_hand.live.size()); + const int live_piece_count = static_cast(cpp_hand.live_count); c_hand.livePieceCount = std::min(live_piece_count, kMaxLiveHandSize); for (int j = 0; j < c_hand.livePieceCount; j++) { c_hand.livePieces[j] = static_cast(cpp_hand.live[j].toUint8_t()); } // Melds - const int meld_count = static_cast(cpp_hand.melds.size()); + const int meld_count = cpp_hand.meld_count; c_hand.meldCount = std::min(meld_count, kMaxMeldsPerHand); for (int j = 0; j < c_hand.meldCount; j++) { c_hand.melds[j].type = static_cast(cpp_hand.melds[j].type); @@ -166,8 +167,7 @@ CObservedGameState ObserveGameState(mahjong::GameState* state) { // Discards const auto& discards = cpp_hand.discards; - const int discards_size = static_cast(discards.size()); - c_hand.discardCount = std::min(discards_size, kMaxDiscardsPerPlayer); + c_hand.discardCount = cpp_hand.discards_count; for (auto j = 0; j < c_hand.discardCount; j++) { c_hand.discards[j] = static_cast(discards[j].toUint8_t()); } diff --git a/src/api/types.h b/src/api/types.h index 3b99198d..2683306f 100644 --- a/src/api/types.h +++ b/src/api/types.h @@ -9,7 +9,8 @@ extern "C" { static const int kMaxLiveHandSize = 14; // Maximum 14 pieces (13 + 1 drawn) static const int kMaxMeldsPerHand = 4; // Maximum 4 melds possible static const int kMaxDiscardsPerPlayer = - 21; // Maximum 21 discards per player ((136 - 13*4 hands) / 4) + 27; // Flow interruption from calls can allow the max discards to go up + // to 27. using CPiece = int; using CWind = int; diff --git a/src/controllers/playercontroller.h b/src/controllers/playercontroller.h index c287b7f9..8b9f9ed9 100644 --- a/src/controllers/playercontroller.h +++ b/src/controllers/playercontroller.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include diff --git a/src/scoring/scoring.cc b/src/scoring/scoring.cc index 4a0016c4..3e9343cd 100644 --- a/src/scoring/scoring.cc +++ b/src/scoring/scoring.cc @@ -10,10 +10,12 @@ #include "scoring/yakus/sevenpairs.h" #include "scoring/yakus/thirteenorphans.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" #include "types/piecetype.h" #include "types/score.h" #include "types/sets.h" +#include "types/walls.h" namespace mahjong { @@ -57,13 +59,13 @@ const int kFuRounding = 10; } // namespace -Score scoreHand(const GameState& state, int player) { - auto root = breakdownHand(state.hands.at(player).live); +Score scoreHand(const GameState& state, const Hand& hand) { + auto root = breakdownHand(hand.live_range()); Score s; s.han = 0; s.yakuman = 0; s.fu = 0; - if (!root->IsComplete() && !yaku::isThirteenOrphans(state, player)) { + if (!root->IsComplete() && !yaku::isThirteenOrphans(state, hand)) { return s; } for (const auto& branch : Node::AsBranchVectors(root.get())) { @@ -75,10 +77,10 @@ Score scoreHand(const GameState& state, int player) { } for (const auto& yaku : Yakus::Instance().GetYakus()) { - if (yaku.type == Yaku::kClosed && state.hands[player].open) { + if (yaku.type == Yaku::kClosed && hand.open) { continue; } - if (!yaku.is_yaku_func(state, player, branch)) { + if (!yaku.is_yaku_func(state, hand, branch)) { continue; } switch (yaku.type) { @@ -87,7 +89,7 @@ Score scoreHand(const GameState& state, int player) { branchscore.han += yaku.value; break; case Yaku::kBonusWhenClosed: - branchscore.han += yaku.value + (state.hands[player].open ? 0 : 1); + branchscore.han += yaku.value + (hand.open ? 0 : 1); break; case Yaku::kYakuman: branchscore.yakuman++; @@ -95,13 +97,13 @@ Score scoreHand(const GameState& state, int player) { } } - for (const auto& dora : state.walls.GetDoras()) { - for (const auto& p : state.hands.at(player).live) { + for (const auto& dora : Walls::GetDoras(state)) { + for (const auto& p : hand.live_range()) { if (p == dora) { branchscore.han++; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.start == dora) { branchscore.han++; } @@ -115,7 +117,7 @@ Score scoreHand(const GameState& state, int player) { } } } - branchscore.fu = getFu(state, player, branch); + branchscore.fu = getFu(state, hand, branch); if (getBasicPoints(branchscore) > getBasicPoints(s)) { s = branchscore; } @@ -154,30 +156,30 @@ int getBasicPoints(Score s) { const int kSevenpairs = 25; -int getFu(const GameState& state, int player, +int getFu(const GameState& state, const Hand& hand, const std::vector& branch) { - if (yaku::isSevenPairs(state, player, branch)) { + if (yaku::isSevenPairs(state, hand, branch)) { return kSevenpairs; } - if (yaku::isPinfu(state, player, branch)) { - if (state.hasRonned.at(player)) { + if (yaku::isPinfu(state, hand, branch)) { + if (hand.hasRonned) { return kPinfuDiscard; } return kPinfuSelfdraw; } int fu = 0; - if (!state.hands.at(player).open && state.hasRonned.at(player)) { + if (!hand.open && hand.hasRonned) { fu = kConcealedDiscard; } else { fu = kOpenOrSelfdraw; } - const bool open = state.hands.at(player).open; - if (isOpenPinfu(state, player, branch)) { + const bool open = hand.open; + if (isOpenPinfu(state, hand, branch)) { fu += kOpenpinfu; - } else if (!state.hasRonned.at(player)) { + } else if (!hand.hasRonned) { fu += kSelfdraw; } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type == SetType::kKan) { if (!meld.start.isHonor() && !meld.start.isTerminal()) { fu += open ? kSimplekan : kCsimplekan; @@ -231,7 +233,7 @@ int getFu(const GameState& state, int player, (kFuRounding - (fu % kFuRounding)); // rounding up to multiple of ten } -bool isOpenPinfu(const GameState& state, int player, +bool isOpenPinfu(const GameState& state, const Hand& hand, const std::vector& branch) { for (const auto* node : branch) { if (node->type() == SetType::kPon) { @@ -250,17 +252,17 @@ bool isOpenPinfu(const GameState& state, int player, } } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type > SetType::kChi) { return false; } } - return getWaits(state.hands[player], state.pendingPiece).size() >= 2; + return getWaits(hand, state.pendingPiece).size() >= 2; } -bool isComplete(const GameState& state, int player) { - auto root = breakdownHand(state.hands.at(player).live); - if (!root->IsComplete() && !yaku::isThirteenOrphans(state, player)) { +bool isComplete(const GameState& state, const Hand& hand) { + auto root = breakdownHand(hand.live_range()); + if (!root->IsComplete() && !yaku::isThirteenOrphans(state, hand)) { return false; } for (const auto& branch : Node::AsBranchVectors(root.get())) { @@ -270,8 +272,8 @@ bool isComplete(const GameState& state, int player) { continue; } if (std::ranges::any_of(Yakus::Instance().GetYakus(), - [&state, player, &branch](const auto& yaku) { - return yaku.is_yaku_func(state, player, branch); + [&state, hand, &branch](const auto& yaku) { + return yaku.is_yaku_func(state, hand, branch); })) { return true; } diff --git a/src/scoring/scoring.h b/src/scoring/scoring.h index 5cbb3b68..84fed5bb 100644 --- a/src/scoring/scoring.h +++ b/src/scoring/scoring.h @@ -3,18 +3,19 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/score.h" namespace mahjong { -Score scoreHand(const GameState& state, int player); +Score scoreHand(const GameState& state, const Hand& hand); int getBasicPoints(Score s); -int getFu(const GameState& state, int player, +int getFu(const GameState& state, const Hand& hand, const std::vector& branch); -bool isOpenPinfu(const GameState& state, int player, +bool isOpenPinfu(const GameState& state, const Hand& hand, const std::vector& branch); -bool isComplete(const GameState& state, int player); +bool isComplete(const GameState& state, const Hand& hand); } // namespace mahjong diff --git a/src/scoring/yakus/afterakan.cc b/src/scoring/yakus/afterakan.cc index fdd0f218..f14f48c6 100644 --- a/src/scoring/yakus/afterakan.cc +++ b/src/scoring/yakus/afterakan.cc @@ -5,13 +5,14 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/statefunction.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isAfterAKan(const GameState& state, int player, +bool isAfterAKan(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - if (state.currentPlayer != player) { + if (state.currentPlayer != hand.id) { return false; } if (state.prevState == StateFunctionType::kReplacement) { diff --git a/src/scoring/yakus/afterakan.h b/src/scoring/yakus/afterakan.h index 2df33c1a..0e969bcd 100644 --- a/src/scoring/yakus/afterakan.h +++ b/src/scoring/yakus/afterakan.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isAfterAKan(const GameState& state, int player, +bool isAfterAKan(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/allgreen.cc b/src/scoring/yakus/allgreen.cc index edb7633f..51f4605f 100644 --- a/src/scoring/yakus/allgreen.cc +++ b/src/scoring/yakus/allgreen.cc @@ -5,17 +5,18 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isAllGreen(const GameState& state, int player, +bool isAllGreen(const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (!piece.isGreen()) { return false; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (!meld.start.isGreen()) { return false; } diff --git a/src/scoring/yakus/allgreen.h b/src/scoring/yakus/allgreen.h index e1136307..a1c38259 100644 --- a/src/scoring/yakus/allgreen.h +++ b/src/scoring/yakus/allgreen.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isAllGreen(const GameState& state, int player, +bool isAllGreen(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/allhonors.cc b/src/scoring/yakus/allhonors.cc index 12831fe8..1135e327 100644 --- a/src/scoring/yakus/allhonors.cc +++ b/src/scoring/yakus/allhonors.cc @@ -5,17 +5,18 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isAllHonors(const GameState& state, int player, +bool isAllHonors(const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (!piece.isHonor()) { return false; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (!meld.start.isHonor()) { return false; } diff --git a/src/scoring/yakus/allhonors.h b/src/scoring/yakus/allhonors.h index b894731c..9f93e1dd 100644 --- a/src/scoring/yakus/allhonors.h +++ b/src/scoring/yakus/allhonors.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isAllHonors(const GameState& state, int player, +bool isAllHonors(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/allpons.cc b/src/scoring/yakus/allpons.cc index 341d70b4..3cec08ff 100644 --- a/src/scoring/yakus/allpons.cc +++ b/src/scoring/yakus/allpons.cc @@ -5,11 +5,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isAllPons(const GameState& state, int player, +bool isAllPons(const GameState& /* state */, const Hand& hand, const std::vector& branch) { int pons = 0; for (const auto* node : branch) { @@ -17,7 +18,7 @@ bool isAllPons(const GameState& state, int player, pons++; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type == SetType::kKan || meld.type == SetType::kPon || meld.type == SetType::kConcealedKan) { pons++; diff --git a/src/scoring/yakus/allpons.h b/src/scoring/yakus/allpons.h index fb245568..695236bd 100644 --- a/src/scoring/yakus/allpons.h +++ b/src/scoring/yakus/allpons.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isAllPons(const GameState& state, int player, +bool isAllPons(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/allsimples.cc b/src/scoring/yakus/allsimples.cc index 7e114557..2c24ea6e 100644 --- a/src/scoring/yakus/allsimples.cc +++ b/src/scoring/yakus/allsimples.cc @@ -5,17 +5,18 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isAllSimples(const GameState& state, int player, +bool isAllSimples(const GameState& /* state */, const Hand& hand, const std::vector& /*branch*/) { - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (piece.isHonor() || piece.isTerminal()) { return false; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.start.isHonor() || meld.start.isTerminal()) { return false; } diff --git a/src/scoring/yakus/allsimples.h b/src/scoring/yakus/allsimples.h index ef1d27cc..e9c8de63 100644 --- a/src/scoring/yakus/allsimples.h +++ b/src/scoring/yakus/allsimples.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isAllSimples(const GameState& state, int player, +bool isAllSimples(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/allterminals.cc b/src/scoring/yakus/allterminals.cc index 026b0aed..021f4eba 100644 --- a/src/scoring/yakus/allterminals.cc +++ b/src/scoring/yakus/allterminals.cc @@ -5,17 +5,18 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isAllTerminals(const GameState& state, int player, +bool isAllTerminals(const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (!piece.isTerminal()) { return false; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (!meld.start.isTerminal()) { return false; } diff --git a/src/scoring/yakus/allterminals.h b/src/scoring/yakus/allterminals.h index b99e84a7..1efa4270 100644 --- a/src/scoring/yakus/allterminals.h +++ b/src/scoring/yakus/allterminals.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isAllTerminals(const GameState& state, int player, +bool isAllTerminals(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/allterminalsandhonors.cc b/src/scoring/yakus/allterminalsandhonors.cc index d03bf8e1..98cc9a2d 100644 --- a/src/scoring/yakus/allterminalsandhonors.cc +++ b/src/scoring/yakus/allterminalsandhonors.cc @@ -5,18 +5,19 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { bool isAllTerminalsAndHonors( - const GameState& state, int player, + const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (!piece.isHonor() && !piece.isTerminal()) { return false; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (!meld.start.isHonor() && !meld.start.isTerminal()) { return false; } diff --git a/src/scoring/yakus/allterminalsandhonors.h b/src/scoring/yakus/allterminalsandhonors.h index 230ee418..87b33340 100644 --- a/src/scoring/yakus/allterminalsandhonors.h +++ b/src/scoring/yakus/allterminalsandhonors.h @@ -3,10 +3,11 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { bool isAllTerminalsAndHonors( - const GameState& state, int player, + const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/bigfourwinds.cc b/src/scoring/yakus/bigfourwinds.cc index fdb83e61..e3d7cfd3 100644 --- a/src/scoring/yakus/bigfourwinds.cc +++ b/src/scoring/yakus/bigfourwinds.cc @@ -5,11 +5,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isBigFourWinds(const GameState& state, int player, +bool isBigFourWinds(const GameState& /*state*/, const Hand& hand, const std::vector& branch) { int pons = 0; for (const auto* node : branch) { @@ -27,7 +28,7 @@ bool isBigFourWinds(const GameState& state, int player, } pons++; } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { switch (meld.start.toUint8_t()) { case Piece::Type::kEastWind: case Piece::Type::kSouthWind: diff --git a/src/scoring/yakus/bigfourwinds.h b/src/scoring/yakus/bigfourwinds.h index 67a01a20..4c74242f 100644 --- a/src/scoring/yakus/bigfourwinds.h +++ b/src/scoring/yakus/bigfourwinds.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isBigFourWinds(const GameState& state, int player, +bool isBigFourWinds(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/bigthreedragons.cc b/src/scoring/yakus/bigthreedragons.cc index 29520a3f..e2e03486 100644 --- a/src/scoring/yakus/bigthreedragons.cc +++ b/src/scoring/yakus/bigthreedragons.cc @@ -5,11 +5,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isBigThreeDragons(const GameState& state, int player, +bool isBigThreeDragons(const GameState& /*state*/, const Hand& hand, const std::vector& branch) { int pons = 0; for (const auto* node : branch) { @@ -26,7 +27,7 @@ bool isBigThreeDragons(const GameState& state, int player, } pons++; } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { switch (meld.start.toUint8_t()) { case Piece::Type::kRedDragon: case Piece::Type::kGreenDragon: diff --git a/src/scoring/yakus/bigthreedragons.h b/src/scoring/yakus/bigthreedragons.h index 8198d106..a484fc69 100644 --- a/src/scoring/yakus/bigthreedragons.h +++ b/src/scoring/yakus/bigthreedragons.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isBigThreeDragons(const GameState& state, int player, +bool isBigThreeDragons(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/blessingofearth.cc b/src/scoring/yakus/blessingofearth.cc index 11997813..e15ed01f 100644 --- a/src/scoring/yakus/blessingofearth.cc +++ b/src/scoring/yakus/blessingofearth.cc @@ -5,12 +5,13 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isBlessingOfEarth(const GameState& state, int player, +bool isBlessingOfEarth(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } if (state.turnNum > 3) { @@ -19,7 +20,7 @@ bool isBlessingOfEarth(const GameState& state, int player, if (state.lastCall >= 0) { return false; } - if (state.hasRonned.at(player)) { + if (hand.hasRonned) { return false; } return true; diff --git a/src/scoring/yakus/blessingofearth.h b/src/scoring/yakus/blessingofearth.h index a4956c3f..527b61e1 100644 --- a/src/scoring/yakus/blessingofearth.h +++ b/src/scoring/yakus/blessingofearth.h @@ -3,10 +3,11 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { bool isBlessingOfEarth( - const GameState& state, int player, + const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/blessingofheaven.cc b/src/scoring/yakus/blessingofheaven.cc index 47fb97f5..dccda98f 100644 --- a/src/scoring/yakus/blessingofheaven.cc +++ b/src/scoring/yakus/blessingofheaven.cc @@ -5,18 +5,19 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isBlessingOfHeaven(const GameState& state, int player, +bool isBlessingOfHeaven(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } - if (player != 0) { + if (hand.id != 0) { return false; } - if (state.hasRonned[0]) { + if (state.hands[0].hasRonned) { return false; } if (state.turnNum != 0) { diff --git a/src/scoring/yakus/blessingofheaven.h b/src/scoring/yakus/blessingofheaven.h index b9454c47..e8c4cbeb 100644 --- a/src/scoring/yakus/blessingofheaven.h +++ b/src/scoring/yakus/blessingofheaven.h @@ -3,10 +3,11 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { bool isBlessingOfHeaven( - const GameState& state, int player, + const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/blessingofman.cc b/src/scoring/yakus/blessingofman.cc index 2a532c99..e38a69a2 100644 --- a/src/scoring/yakus/blessingofman.cc +++ b/src/scoring/yakus/blessingofman.cc @@ -5,21 +5,22 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isBlessingOfMan(const GameState& state, int player, +bool isBlessingOfMan(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } - if (state.turnNum > player) { + if (state.turnNum > hand.id) { return false; } if (state.lastCall >= 0) { return false; } - if (state.hasRonned.at(player)) { + if (hand.hasRonned) { return true; } return false; diff --git a/src/scoring/yakus/blessingofman.h b/src/scoring/yakus/blessingofman.h index 7f0912f0..6cc05573 100644 --- a/src/scoring/yakus/blessingofman.h +++ b/src/scoring/yakus/blessingofman.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isBlessingOfMan(const GameState& state, int player, +bool isBlessingOfMan(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/bottomofthesea.cc b/src/scoring/yakus/bottomofthesea.cc index df20a5d9..7277fc26 100644 --- a/src/scoring/yakus/bottomofthesea.cc +++ b/src/scoring/yakus/bottomofthesea.cc @@ -5,12 +5,14 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" +#include "types/walls.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isBottomOfTheSea(const GameState& state, int /*player*/, +bool isBottomOfTheSea(const GameState& state, const Hand& /*hand*/, const std::vector& /*branch*/) { - return state.walls.GetRemainingPieces() == 0; + return Walls::GetRemainingPieces(state) == 0; } REGISTER_YAKU({ diff --git a/src/scoring/yakus/bottomofthesea.h b/src/scoring/yakus/bottomofthesea.h index 756c4637..f6327bfc 100644 --- a/src/scoring/yakus/bottomofthesea.h +++ b/src/scoring/yakus/bottomofthesea.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isBottomOfTheSea(const GameState& state, int player, +bool isBottomOfTheSea(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/doubleriichi.cc b/src/scoring/yakus/doubleriichi.cc index a31dbf02..4fd220ba 100644 --- a/src/scoring/yakus/doubleriichi.cc +++ b/src/scoring/yakus/doubleriichi.cc @@ -5,13 +5,13 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isDoubleRiichi(const GameState& state, int player, +bool isDoubleRiichi(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - return state.hands.at(player).riichi && !state.hands.at(player).open && - (state.turnNum < 4 && state.lastCall < 0); + return hand.riichi && !hand.open && (state.turnNum < 4 && state.lastCall < 0); } REGISTER_YAKU({ diff --git a/src/scoring/yakus/doubleriichi.h b/src/scoring/yakus/doubleriichi.h index 768c3ee3..1903703b 100644 --- a/src/scoring/yakus/doubleriichi.h +++ b/src/scoring/yakus/doubleriichi.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isDoubleRiichi(const GameState& state, int player, +bool isDoubleRiichi(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/fourconcealedpon.cc b/src/scoring/yakus/fourconcealedpon.cc index b9a86725..8e554932 100644 --- a/src/scoring/yakus/fourconcealedpon.cc +++ b/src/scoring/yakus/fourconcealedpon.cc @@ -5,13 +5,14 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isFourConcealedPon(const GameState& state, int player, +bool isFourConcealedPon(const GameState& /*state*/, const Hand& hand, const std::vector& branch) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } int concealed_pons = 0; @@ -20,7 +21,7 @@ bool isFourConcealedPon(const GameState& state, int player, concealed_pons++; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type == SetType::kConcealedKan) { concealed_pons++; } diff --git a/src/scoring/yakus/fourconcealedpon.h b/src/scoring/yakus/fourconcealedpon.h index d32f1456..df74846a 100644 --- a/src/scoring/yakus/fourconcealedpon.h +++ b/src/scoring/yakus/fourconcealedpon.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isFourConcealedPon(const GameState& state, int player, +bool isFourConcealedPon(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/fourkans.cc b/src/scoring/yakus/fourkans.cc index 096b73bd..a6d0abc8 100644 --- a/src/scoring/yakus/fourkans.cc +++ b/src/scoring/yakus/fourkans.cc @@ -5,14 +5,15 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isFourKans(const GameState& state, int player, +bool isFourKans(const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { int kans = 0; - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type >= SetType::kKan) { kans++; } diff --git a/src/scoring/yakus/fourkans.h b/src/scoring/yakus/fourkans.h index 0af7ca72..a563274b 100644 --- a/src/scoring/yakus/fourkans.h +++ b/src/scoring/yakus/fourkans.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isFourKans(const GameState& state, int player, +bool isFourKans(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/fullflush.cc b/src/scoring/yakus/fullflush.cc index 1a41fcc6..7e86a392 100644 --- a/src/scoring/yakus/fullflush.cc +++ b/src/scoring/yakus/fullflush.cc @@ -5,21 +5,22 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isFullFlush(const GameState& state, int player, +bool isFullFlush(const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { - if (state.hands.at(player).live.front().isHonor()) { + if (hand.live[0].isHonor()) { return false; } - const int suit = state.hands.at(player).live.front().getSuit(); - for (const auto& piece : state.hands.at(player).live) { + const int suit = hand.live[0].getSuit(); + for (const auto& piece : hand.live_range()) { if (piece.getSuit() != suit) { return false; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.start.getSuit() != suit) { return false; } diff --git a/src/scoring/yakus/fullflush.h b/src/scoring/yakus/fullflush.h index e1d726f9..517362eb 100644 --- a/src/scoring/yakus/fullflush.h +++ b/src/scoring/yakus/fullflush.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isFullFlush(const GameState& state, int player, +bool isFullFlush(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/fullyconcealedhand.cc b/src/scoring/yakus/fullyconcealedhand.cc index 342a22f3..199346af 100644 --- a/src/scoring/yakus/fullyconcealedhand.cc +++ b/src/scoring/yakus/fullyconcealedhand.cc @@ -5,13 +5,15 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" +#include "types/walls.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isFullyConcealedHand(const GameState& state, int player, +bool isFullyConcealedHand(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - return state.currentPlayer == player && !state.hands.at(player).open && - state.walls.GetRemainingPieces() > 0; + return state.currentPlayer == hand.id && !hand.open && + Walls::GetRemainingPieces(state) > 0; } REGISTER_YAKU({ diff --git a/src/scoring/yakus/fullyconcealedhand.h b/src/scoring/yakus/fullyconcealedhand.h index 73801979..2321abd5 100644 --- a/src/scoring/yakus/fullyconcealedhand.h +++ b/src/scoring/yakus/fullyconcealedhand.h @@ -3,10 +3,11 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { bool isFullyConcealedHand( - const GameState& state, int player, + const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/halfflush.cc b/src/scoring/yakus/halfflush.cc index 2996cbc8..f6a3453e 100644 --- a/src/scoring/yakus/halfflush.cc +++ b/src/scoring/yakus/halfflush.cc @@ -6,14 +6,15 @@ #include "scoring/yakus.h" #include "scoring/yakus/fullflush.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isHalfFlush(const GameState& state, int player, +bool isHalfFlush(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - const int suit = state.hands.at(player).live.front().getSuit(); + const int suit = hand.live[0].getSuit(); bool honors = false; - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (piece.isHonor()) { honors = true; continue; @@ -22,7 +23,7 @@ bool isHalfFlush(const GameState& state, int player, return false; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.start.isHonor()) { honors = true; continue; @@ -32,7 +33,7 @@ bool isHalfFlush(const GameState& state, int player, } } // Full Flush scores instead of Half Flush. - return honors && !isFullFlush(state, player); + return honors && !isFullFlush(state, hand); } REGISTER_YAKU({ diff --git a/src/scoring/yakus/halfflush.h b/src/scoring/yakus/halfflush.h index 3395c32d..a361fdad 100644 --- a/src/scoring/yakus/halfflush.h +++ b/src/scoring/yakus/halfflush.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isHalfFlush(const GameState& state, int player, +bool isHalfFlush(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/honorpon.cc b/src/scoring/yakus/honorpon.cc index 1f76759c..db7e9db0 100644 --- a/src/scoring/yakus/honorpon.cc +++ b/src/scoring/yakus/honorpon.cc @@ -6,6 +6,7 @@ #include "scoring/yakus.h" #include "statefunctions/stateutilities.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" #include "types/piecetype.h" #include "types/sets.h" @@ -14,7 +15,7 @@ namespace mahjong::yaku { namespace { -bool findPon(const GameState& state, int player, +bool findPon(const GameState& /* state */, const Hand& hand, const std::vector& branch, const Piece& piece) { for (const auto* node : branch) { @@ -22,7 +23,7 @@ bool findPon(const GameState& state, int player, return true; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type >= SetType::kPon && meld.start == piece) { return true; } @@ -32,31 +33,31 @@ bool findPon(const GameState& state, int player, } // namespace -bool isSeatWind(const GameState& state, int player, +bool isSeatWind(const GameState& state, const Hand& hand, const std::vector& branch) { - return findPon(state, player, branch, - Piece::fromWind(GetSeat(state.roundNum, player))); + return findPon(state, hand, branch, + Piece::fromWind(GetSeat(state.roundNum, hand.id))); } -bool isPrevalentWind(const GameState& state, int player, +bool isPrevalentWind(const GameState& state, const Hand& hand, const std::vector& branch) { - return findPon(state, player, branch, + return findPon(state, hand, branch, Piece::fromWind(state.roundNum > 3 ? kSouth : kEast)); } -bool isGreenDragon(const GameState& state, int player, +bool isGreenDragon(const GameState& state, const Hand& hand, const std::vector& branch) { - return findPon(state, player, branch, kGreenDragon); + return findPon(state, hand, branch, kGreenDragon); } -bool isRedDragon(const GameState& state, int player, +bool isRedDragon(const GameState& state, const Hand& hand, const std::vector& branch) { - return findPon(state, player, branch, kRedDragon); + return findPon(state, hand, branch, kRedDragon); } -bool isWhiteDragon(const GameState& state, int player, +bool isWhiteDragon(const GameState& state, const Hand& hand, const std::vector& branch) { - return findPon(state, player, branch, kWhiteDragon); + return findPon(state, hand, branch, kWhiteDragon); } REGISTER_YAKUS({ diff --git a/src/scoring/yakus/honorpon.h b/src/scoring/yakus/honorpon.h index 49b0871b..31dad3c2 100644 --- a/src/scoring/yakus/honorpon.h +++ b/src/scoring/yakus/honorpon.h @@ -3,20 +3,21 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isSeatWind(const GameState& state, int player, +bool isSeatWind(const GameState& state, const Hand& hand, const std::vector& branch); -bool isPrevalentWind(const GameState& state, int player, +bool isPrevalentWind(const GameState& state, const Hand& hand, const std::vector& branch); -bool isGreenDragon(const GameState& state, int player, +bool isGreenDragon(const GameState& state, const Hand& hand, const std::vector& branch); -bool isRedDragon(const GameState& state, int player, +bool isRedDragon(const GameState& state, const Hand& hand, const std::vector& branch); -bool isWhiteDragon(const GameState& state, int player, +bool isWhiteDragon(const GameState& state, const Hand& hand, const std::vector& branch); } // namespace mahjong::yaku diff --git a/src/scoring/yakus/ippatsu.cc b/src/scoring/yakus/ippatsu.cc index 10eea80f..6c368131 100644 --- a/src/scoring/yakus/ippatsu.cc +++ b/src/scoring/yakus/ippatsu.cc @@ -7,14 +7,15 @@ #include "scoring/yakus/doubleriichi.h" #include "scoring/yakus/riichi.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isIppatsu(const GameState& state, int player, +bool isIppatsu(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - return (isRiichi(state, player) || isDoubleRiichi(state, player)) && - (state.turnNum - state.hands.at(player).riichiRound <= 4 && - state.lastCall < state.hands.at(player).riichiRound); + return (isRiichi(state, hand) || isDoubleRiichi(state, hand)) && + (state.turnNum - hand.riichiRound <= 4 && + state.lastCall < hand.riichiRound); } REGISTER_YAKU({ diff --git a/src/scoring/yakus/ippatsu.h b/src/scoring/yakus/ippatsu.h index 9d3bb36f..b3bc9a6f 100644 --- a/src/scoring/yakus/ippatsu.h +++ b/src/scoring/yakus/ippatsu.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isIppatsu(const GameState& state, int player, +bool isIppatsu(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/littlefourwinds.cc b/src/scoring/yakus/littlefourwinds.cc index 2ca76dc8..3250efe7 100644 --- a/src/scoring/yakus/littlefourwinds.cc +++ b/src/scoring/yakus/littlefourwinds.cc @@ -5,11 +5,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isLittleFourWinds(const GameState& state, int player, +bool isLittleFourWinds(const GameState& /*state*/, const Hand& hand, const std::vector& branch) { bool pair = false; int pons = 0; @@ -35,7 +36,7 @@ bool isLittleFourWinds(const GameState& state, int player, if (!pair) { return false; } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { switch (meld.start.toUint8_t()) { case Piece::Type::kEastWind: case Piece::Type::kSouthWind: diff --git a/src/scoring/yakus/littlefourwinds.h b/src/scoring/yakus/littlefourwinds.h index dcdb76be..65603ecd 100644 --- a/src/scoring/yakus/littlefourwinds.h +++ b/src/scoring/yakus/littlefourwinds.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isLittleFourWinds(const GameState& state, int player, +bool isLittleFourWinds(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/littlethreedragons.cc b/src/scoring/yakus/littlethreedragons.cc index b73f9cf4..a4e2674c 100644 --- a/src/scoring/yakus/littlethreedragons.cc +++ b/src/scoring/yakus/littlethreedragons.cc @@ -5,11 +5,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isLittleThreeDragons(const GameState& state, int player, +bool isLittleThreeDragons(const GameState& /*state*/, const Hand& hand, const std::vector& branch) { bool pair = false; int pons = 0; @@ -32,7 +33,7 @@ bool isLittleThreeDragons(const GameState& state, int player, if (!pair) { return false; } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { switch (meld.start.toUint8_t()) { case Piece::Type::kRedDragon: case Piece::Type::kGreenDragon: diff --git a/src/scoring/yakus/littlethreedragons.h b/src/scoring/yakus/littlethreedragons.h index e81b2157..c01a308b 100644 --- a/src/scoring/yakus/littlethreedragons.h +++ b/src/scoring/yakus/littlethreedragons.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isLittleThreeDragons(const GameState& state, int player, +bool isLittleThreeDragons(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/maxbranches.cc b/src/scoring/yakus/maxbranches.cc index c591ed07..7bb71fdf 100644 --- a/src/scoring/yakus/maxbranches.cc +++ b/src/scoring/yakus/maxbranches.cc @@ -5,16 +5,17 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" namespace mahjong::yaku { -bool isMaxBranches(const GameState& state, int player, +bool isMaxBranches(const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { - const int start = state.hands.at(player).live[0].getPieceNum(); - const int suit = state.hands.at(player).live[0].getSuit(); + const int start = hand.live[0].getPieceNum(); + const int suit = hand.live[0].getSuit(); std::array sets = {}; const std::array final = {3, 3, 2, 2, 2, 2}; - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (piece.getSuit() != suit) { return false; } @@ -23,7 +24,7 @@ bool isMaxBranches(const GameState& state, int player, } sets.at(piece.getPieceNum() - start)++; } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.start.getSuit() != suit) { return false; } diff --git a/src/scoring/yakus/maxbranches.h b/src/scoring/yakus/maxbranches.h index fa4ee698..7eecefeb 100644 --- a/src/scoring/yakus/maxbranches.h +++ b/src/scoring/yakus/maxbranches.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isMaxBranches(const GameState& state, int player, +bool isMaxBranches(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/mixedtriplechi.cc b/src/scoring/yakus/mixedtriplechi.cc index ad0fba5b..3983de7b 100644 --- a/src/scoring/yakus/mixedtriplechi.cc +++ b/src/scoring/yakus/mixedtriplechi.cc @@ -6,11 +6,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isMixedTripleChi(const GameState& state, int player, +bool isMixedTripleChi(const GameState& /* state */, const Hand& hand, const std::vector& branch) { const int k_piecesinasuit = 9; std::array bamboo_chi = {}; @@ -29,7 +30,7 @@ bool isMixedTripleChi(const GameState& state, int player, } } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type == SetType::kChi) { if (meld.start.getSuit() == Piece::Type::kBambooSuit) { bamboo_chi.at(meld.start.getPieceNum()) = true; diff --git a/src/scoring/yakus/mixedtriplechi.h b/src/scoring/yakus/mixedtriplechi.h index 0957b7dc..e8c6abc3 100644 --- a/src/scoring/yakus/mixedtriplechi.h +++ b/src/scoring/yakus/mixedtriplechi.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isMixedTripleChi(const GameState& state, int player, +bool isMixedTripleChi(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/ninegates.cc b/src/scoring/yakus/ninegates.cc index 73fee0cc..3ea55c9e 100644 --- a/src/scoring/yakus/ninegates.cc +++ b/src/scoring/yakus/ninegates.cc @@ -7,18 +7,19 @@ #include "scoring/yakus.h" #include "scoring/yakus/fullflush.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isNineGates(const GameState& state, int player, +bool isNineGates(const GameState& state, const Hand& hand, const std::vector& branch) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } - if (!isFullFlush(state, player, branch)) { + if (!isFullFlush(state, hand, branch)) { return false; } - if (state.hands.at(player).open) { + if (hand.open) { return false; } std::map pieces; @@ -26,7 +27,7 @@ bool isNineGates(const GameState& state, int player, pieces.at(i) = 0; } bool duplicate = false; - for (const auto& piece : state.hands.at(player).live) { + for (const auto& piece : hand.live_range()) { if (pieces.contains(piece.getPieceNum())) { if ((pieces[piece.getPieceNum()] != 0) && !piece.isTerminal()) { if (duplicate) { diff --git a/src/scoring/yakus/ninegates.h b/src/scoring/yakus/ninegates.h index 385bc219..d0e2c096 100644 --- a/src/scoring/yakus/ninegates.h +++ b/src/scoring/yakus/ninegates.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isNineGates(const GameState& state, int player, +bool isNineGates(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/outsidehand.cc b/src/scoring/yakus/outsidehand.cc index 4057202b..b60089a9 100644 --- a/src/scoring/yakus/outsidehand.cc +++ b/src/scoring/yakus/outsidehand.cc @@ -7,11 +7,12 @@ #include "scoring/yakus/allterminalsandhonors.h" #include "scoring/yakus/terminalsinallsets.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isOutsideHand(const GameState& state, int player, +bool isOutsideHand(const GameState& state, const Hand& hand, const std::vector& branch) { bool chi = false; for (const auto* node : branch) { @@ -27,7 +28,7 @@ bool isOutsideHand(const GameState& state, int player, } } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type == SetType::kChi) { if (meld.start.isTerminal() || (meld.start + 2).isTerminal()) { chi = true; @@ -42,8 +43,8 @@ bool isOutsideHand(const GameState& state, int player, } // Terminals in all Sets and All Terminals and Honors are more valuable and // score instead of Outside Hand. - return chi && !isTerminalsInAllSets(state, player, branch) && - !isAllTerminalsAndHonors(state, player); + return chi && !isTerminalsInAllSets(state, hand, branch) && + !isAllTerminalsAndHonors(state, hand); } REGISTER_YAKU({ diff --git a/src/scoring/yakus/outsidehand.h b/src/scoring/yakus/outsidehand.h index 07bc7531..f8c7f9b2 100644 --- a/src/scoring/yakus/outsidehand.h +++ b/src/scoring/yakus/outsidehand.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isOutsideHand(const GameState& state, int player, +bool isOutsideHand(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/pinfu.cc b/src/scoring/yakus/pinfu.cc index 4ad90b19..bf242afd 100644 --- a/src/scoring/yakus/pinfu.cc +++ b/src/scoring/yakus/pinfu.cc @@ -7,14 +7,15 @@ #include "scoring/yakus.h" #include "statefunctions/stateutilities.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isPinfu(const GameState& state, int player, +bool isPinfu(const GameState& state, const Hand& hand, const std::vector& branch) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } for (const auto* node : branch) { @@ -37,13 +38,14 @@ bool isPinfu(const GameState& state, int player, if (node->start() == kEastWind && state.roundNum < 4) { return false; } - if (node->start() == Piece::fromWind(GetSeat(state.roundNum, player))) { + if (node->start() == + Piece::fromWind(GetSeat(state.roundNum, hand.id))) { return false; } } } } - return getWaits(state.hands[player], state.pendingPiece).size() >= 2; + return getWaits(hand, state.pendingPiece).size() >= 2; } REGISTER_YAKU({ diff --git a/src/scoring/yakus/pinfu.h b/src/scoring/yakus/pinfu.h index 6d5f2cd6..d311a2bf 100644 --- a/src/scoring/yakus/pinfu.h +++ b/src/scoring/yakus/pinfu.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isPinfu(const GameState& state, int player, +bool isPinfu(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/puredoublechi.cc b/src/scoring/yakus/puredoublechi.cc index ed529af7..b36ec578 100644 --- a/src/scoring/yakus/puredoublechi.cc +++ b/src/scoring/yakus/puredoublechi.cc @@ -7,13 +7,14 @@ #include "scoring/yakus.h" #include "scoring/yakus/twicepuredoublechi.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isPureDoubleChi(const GameState& state, int player, +bool isPureDoubleChi(const GameState& state, const Hand& hand, const std::vector& branch) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } for (size_t i = 0; i < branch.size(); i++) { @@ -26,7 +27,7 @@ bool isPureDoubleChi(const GameState& state, int player, } if (branch.at(i)->type() == branch[j]->type() && branch.at(i)->start() == branch[j]->start()) { - return !isTwicePureDoubleChi(state, player, branch); + return !isTwicePureDoubleChi(state, hand, branch); } } } diff --git a/src/scoring/yakus/puredoublechi.h b/src/scoring/yakus/puredoublechi.h index 912af0af..970b4a62 100644 --- a/src/scoring/yakus/puredoublechi.h +++ b/src/scoring/yakus/puredoublechi.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isPureDoubleChi(const GameState& state, int player, +bool isPureDoubleChi(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/purestraight.cc b/src/scoring/yakus/purestraight.cc index 8bfad632..7700c95e 100644 --- a/src/scoring/yakus/purestraight.cc +++ b/src/scoring/yakus/purestraight.cc @@ -6,11 +6,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isPureStraight(const GameState& state, int player, +bool isPureStraight(const GameState& /* state */, const Hand& hand, const std::vector& branch) { const int k_firstchistart = 1; const int k_secondchistart = 4; @@ -41,7 +42,7 @@ bool isPureStraight(const GameState& state, int player, } } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type == SetType::kChi) { int ind = 0; if (meld.start.getPieceNum() == k_firstchistart) { diff --git a/src/scoring/yakus/purestraight.h b/src/scoring/yakus/purestraight.h index 64e879aa..7d2d4134 100644 --- a/src/scoring/yakus/purestraight.h +++ b/src/scoring/yakus/purestraight.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isPureStraight(const GameState& state, int player, +bool isPureStraight(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/riichi.cc b/src/scoring/yakus/riichi.cc index 69847226..e469ddf7 100644 --- a/src/scoring/yakus/riichi.cc +++ b/src/scoring/yakus/riichi.cc @@ -5,13 +5,13 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isRiichi(const GameState& state, int player, +bool isRiichi(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - return state.hands.at(player).riichi && !state.hands.at(player).open && - (state.turnNum > 4 || state.lastCall < 0); + return hand.riichi && !hand.open && (state.turnNum > 4 || state.lastCall < 0); } REGISTER_YAKU({ diff --git a/src/scoring/yakus/riichi.h b/src/scoring/yakus/riichi.h index 1b983db6..5b273737 100644 --- a/src/scoring/yakus/riichi.h +++ b/src/scoring/yakus/riichi.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isRiichi(const GameState& state, int player, +bool isRiichi(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/robbingakan.cc b/src/scoring/yakus/robbingakan.cc index 042f4dc1..7952f863 100644 --- a/src/scoring/yakus/robbingakan.cc +++ b/src/scoring/yakus/robbingakan.cc @@ -5,13 +5,14 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/statefunction.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isRobbingAKan(const GameState& state, int player, +bool isRobbingAKan(const GameState& state, const Hand& hand, const std::vector& /*branch*/) { - if (!state.hasRonned.at(player)) { + if (!hand.hasRonned) { return false; } if (state.nextState == StateFunctionType::kKanDiscard) { diff --git a/src/scoring/yakus/robbingakan.h b/src/scoring/yakus/robbingakan.h index 3d5f9949..d11bcdf6 100644 --- a/src/scoring/yakus/robbingakan.h +++ b/src/scoring/yakus/robbingakan.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isRobbingAKan(const GameState& state, int player, +bool isRobbingAKan(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/sevenpairs.cc b/src/scoring/yakus/sevenpairs.cc index 00c3968b..47b867b6 100644 --- a/src/scoring/yakus/sevenpairs.cc +++ b/src/scoring/yakus/sevenpairs.cc @@ -6,14 +6,15 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isSevenPairs(const GameState& state, int player, +bool isSevenPairs(const GameState& /* state */, const Hand& hand, const std::vector& branch) { - if (state.hands[player].open) { + if (hand.open) { return false; } std::set pairs; diff --git a/src/scoring/yakus/sevenpairs.h b/src/scoring/yakus/sevenpairs.h index 2f571e6f..9e2f3d3a 100644 --- a/src/scoring/yakus/sevenpairs.h +++ b/src/scoring/yakus/sevenpairs.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isSevenPairs(const GameState& state, int player, +bool isSevenPairs(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/terminalsinallsets.cc b/src/scoring/yakus/terminalsinallsets.cc index 19aff3cb..b5cfd7b8 100644 --- a/src/scoring/yakus/terminalsinallsets.cc +++ b/src/scoring/yakus/terminalsinallsets.cc @@ -5,11 +5,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isTerminalsInAllSets(const GameState& state, int player, +bool isTerminalsInAllSets(const GameState& /*state*/, const Hand& hand, const std::vector& branch) { for (const auto* node : branch) { switch (node->type()) { @@ -30,7 +31,7 @@ bool isTerminalsInAllSets(const GameState& state, int player, break; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { switch (meld.type) { case SetType::kSingle: return false; diff --git a/src/scoring/yakus/terminalsinallsets.h b/src/scoring/yakus/terminalsinallsets.h index 0368d2be..7316ba44 100644 --- a/src/scoring/yakus/terminalsinallsets.h +++ b/src/scoring/yakus/terminalsinallsets.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isTerminalsInAllSets(const GameState& state, int player, +bool isTerminalsInAllSets(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/thirteenorphans.cc b/src/scoring/yakus/thirteenorphans.cc index aa8c0a39..54a64b6b 100644 --- a/src/scoring/yakus/thirteenorphans.cc +++ b/src/scoring/yakus/thirteenorphans.cc @@ -6,14 +6,15 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" #include "types/piecetype.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isThirteenOrphans(const GameState& state, int player, +bool isThirteenOrphans(const GameState& /*state*/, const Hand& hand, const std::vector& /*branch*/) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } std::map pieces = { @@ -23,7 +24,8 @@ bool isThirteenOrphans(const GameState& state, int player, {kNorthWind, false}, {kRedDragon, false}, {kWhiteDragon, false}, {kGreenDragon, false}}; bool duplicate = false; - for (const auto& piece : state.hands.at(player).live) { + + for (const auto& piece : hand.live_range()) { if (pieces.contains(piece)) { if (pieces[piece]) { duplicate = true; diff --git a/src/scoring/yakus/thirteenorphans.h b/src/scoring/yakus/thirteenorphans.h index cad273ce..97bae860 100644 --- a/src/scoring/yakus/thirteenorphans.h +++ b/src/scoring/yakus/thirteenorphans.h @@ -3,10 +3,11 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { bool isThirteenOrphans( - const GameState& state, int player, + const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/threeconcealedpons.cc b/src/scoring/yakus/threeconcealedpons.cc index 9be856c3..fc0599e9 100644 --- a/src/scoring/yakus/threeconcealedpons.cc +++ b/src/scoring/yakus/threeconcealedpons.cc @@ -5,11 +5,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isThreeConcealedPons(const GameState& state, int player, +bool isThreeConcealedPons(const GameState& /* state */, const Hand& hand, const std::vector& branch) { int concealed_pons = 0; for (const auto* node : branch) { @@ -17,7 +18,7 @@ bool isThreeConcealedPons(const GameState& state, int player, concealed_pons++; } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type == SetType::kConcealedKan) { concealed_pons++; } diff --git a/src/scoring/yakus/threeconcealedpons.h b/src/scoring/yakus/threeconcealedpons.h index 0f65d725..b9f26d16 100644 --- a/src/scoring/yakus/threeconcealedpons.h +++ b/src/scoring/yakus/threeconcealedpons.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isThreeConcealedPons(const GameState& state, int player, +bool isThreeConcealedPons(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/threekans.cc b/src/scoring/yakus/threekans.cc index b22f233c..6d5f95fa 100644 --- a/src/scoring/yakus/threekans.cc +++ b/src/scoring/yakus/threekans.cc @@ -5,14 +5,15 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isThreeKans(const GameState& state, int player, +bool isThreeKans(const GameState& /* state */, const Hand& hand, const std::vector& /*branch*/) { int kans = 0; - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type >= SetType::kKan) { kans++; } diff --git a/src/scoring/yakus/threekans.h b/src/scoring/yakus/threekans.h index 044f6ef6..fc69c69e 100644 --- a/src/scoring/yakus/threekans.h +++ b/src/scoring/yakus/threekans.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isThreeKans(const GameState& state, int player, +bool isThreeKans(const GameState& state, const Hand& hand, const std::vector& /*unused*/ = {}); } diff --git a/src/scoring/yakus/triplepon.cc b/src/scoring/yakus/triplepon.cc index 462e8844..5492b2ed 100644 --- a/src/scoring/yakus/triplepon.cc +++ b/src/scoring/yakus/triplepon.cc @@ -6,11 +6,12 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isTriplePon(const GameState& state, int player, +bool isTriplePon(const GameState& /* state */, const Hand& hand, const std::vector& branch) { std::array bamboo_pon = {}; std::array char_pon = {}; @@ -28,7 +29,7 @@ bool isTriplePon(const GameState& state, int player, } } } - for (const auto& meld : state.hands.at(player).melds) { + for (const auto& meld : hand.melds_range()) { if (meld.type >= SetType::kPon) { if (meld.start.getSuit() == Piece::Type::kBambooSuit) { bamboo_pon.at(meld.start.getPieceNum() - 1) = true; diff --git a/src/scoring/yakus/triplepon.h b/src/scoring/yakus/triplepon.h index 1f664b37..d276f9c9 100644 --- a/src/scoring/yakus/triplepon.h +++ b/src/scoring/yakus/triplepon.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isTriplePon(const GameState& state, int player, +bool isTriplePon(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/scoring/yakus/twicepuredoublechi.cc b/src/scoring/yakus/twicepuredoublechi.cc index 11a8c998..3886dfed 100644 --- a/src/scoring/yakus/twicepuredoublechi.cc +++ b/src/scoring/yakus/twicepuredoublechi.cc @@ -6,13 +6,14 @@ #include "analysis/handnode.h" #include "scoring/yakus.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/sets.h" #include "types/yaku.h" namespace mahjong::yaku { -bool isTwicePureDoubleChi(const GameState& state, int player, +bool isTwicePureDoubleChi(const GameState& /*state*/, const Hand& hand, const std::vector& branch) { - if (state.hands.at(player).open) { + if (hand.open) { return false; } int pairs = 0; diff --git a/src/scoring/yakus/twicepuredoublechi.h b/src/scoring/yakus/twicepuredoublechi.h index 96bc4869..974af058 100644 --- a/src/scoring/yakus/twicepuredoublechi.h +++ b/src/scoring/yakus/twicepuredoublechi.h @@ -3,9 +3,10 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong::yaku { -bool isTwicePureDoubleChi(const GameState& state, int player, +bool isTwicePureDoubleChi(const GameState& state, const Hand& hand, const std::vector& branch); } diff --git a/src/statefunctions/decisionfunction.cc b/src/statefunctions/decisionfunction.cc index 506c4a7f..40655ad7 100644 --- a/src/statefunctions/decisionfunction.cc +++ b/src/statefunctions/decisionfunction.cc @@ -2,13 +2,13 @@ #include #include -#include #include "analysis/util.h" #include "scoring/scoring.h" #include "scoring/yakus/thirteenorphans.h" #include "statefunctions/stateutilities.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/sets.h" #include "types/walls.h" @@ -16,120 +16,102 @@ namespace mahjong { // TODO(#18): "I really hate this" - alice -bool CanRon(const GameState& state, int player) { +bool CanRon(const GameState& state, const Hand& hand) { // If the pending piece is your discard, you can't Ron - for (const auto& piece : state.hands.at(player).discards) { + for (const auto& piece : hand.discards) { if (state.pendingPiece == piece) { return false; } } // Build the theoretical hand - auto& tmp_state = const_cast(state); - tmp_state.hands.at(player).live.push_back(state.pendingPiece); - tmp_state.hands.at(player).sort(); + Hand tmp_hand = hand; + tmp_hand.live[tmp_hand.live_count++] = state.pendingPiece; // If this Ron is occurring due to a concealed kan discard, if (state.concealedKan) { // If it happens to be a ron for a thirteen orphans, - // it's allowed and you can ron - if (yaku::isThirteenOrphans(state, player)) { - tmp_state.hands.at(player).live.erase( - std::find(state.hands.at(player).live.begin(), - state.hands.at(player).live.end(), state.pendingPiece)); - return true; - } - - // otherwise, you can't - tmp_state.hands.at(player).live.erase( - std::find(state.hands.at(player).live.begin(), - state.hands.at(player).live.end(), state.pendingPiece)); - return false; + // it's allowed and you can ron otherwise, you can't. + return yaku::isThirteenOrphans(state, hand); } - // if not a concealed kan, check if it's complete - const bool can_ron = isComplete(state, player); - tmp_state.hands.at(player).live.erase( - std::find(state.hands.at(player).live.begin(), - state.hands.at(player).live.end(), state.pendingPiece)); - return can_ron; + return isComplete(state, hand); } -bool CanKan(const GameState& state, int player) { - if (state.walls.GetRemainingPieces() == 0) { +bool CanKan(const GameState& state, const Hand& hand) { + if (Walls::GetRemainingPieces(state) == 0) { return false; } - if (state.hands.at(player).riichi) { + if (hand.riichi) { return false; } - return CountPieces(state, player, state.pendingPiece) == 3; + return CountPieces(hand, state.pendingPiece) == 3; } -bool CanPon(const GameState& state, int player) { - if (state.hands.at(player).riichi) { +bool CanPon(const GameState& state, const Hand& hand) { + if (hand.riichi) { return false; } - return CountPieces(state, player, state.pendingPiece) == 2; + return CountPieces(hand, state.pendingPiece) == 2; } -bool CanChi(const GameState& state, int player) { - if (state.hands.at(player).riichi) { +bool CanChi(const GameState& state, const Hand& hand) { + if (hand.riichi) { return false; } if (state.pendingPiece.isHonor()) { return false; } - if (((state.currentPlayer + 1) % 4) != player) { + if (((state.currentPlayer + 1) % 4) != hand.id) { return false; } - if (CountPieces(state, player, state.pendingPiece - 2) > 0 && - CountPieces(state, player, state.pendingPiece - 1) > 0) { + if (CountPieces(hand, state.pendingPiece - 2) > 0 && + CountPieces(hand, state.pendingPiece - 1) > 0) { return true; } - if (CountPieces(state, player, state.pendingPiece - 1) > 0 && - CountPieces(state, player, state.pendingPiece + 1) > 0) { + if (CountPieces(hand, state.pendingPiece - 1) > 0 && + CountPieces(hand, state.pendingPiece + 1) > 0) { return true; } - if (CountPieces(state, player, state.pendingPiece + 1) > 0 && - CountPieces(state, player, state.pendingPiece + 2) > 0) { + if (CountPieces(hand, state.pendingPiece + 1) > 0 && + CountPieces(hand, state.pendingPiece + 2) > 0) { return true; } return false; } -bool CanTsumo(const GameState& state, int player) { - return isComplete(state, player); +bool CanTsumo(const GameState& state, const Hand& hand) { + return isComplete(state, hand); } -bool CanConvertedKan(const GameState& state, int player) { - if (state.walls.GetRemainingPieces() == 0) { +bool CanConvertedKan(const GameState& state, const Hand& hand) { + if (Walls::GetRemainingPieces(state) == 0) { return false; } - return std::any_of(state.hands.at(player).melds.begin(), - state.hands.at(player).melds.end(), [&](auto meld) { - return meld.type == SetType::kPon && - CountPieces(state, player, meld.start) == 1; - }); + return std::any_of( + hand.melds.begin(), hand.melds.begin() + hand.meld_count, [&](auto meld) { + return meld.type == SetType::kPon && CountPieces(hand, meld.start) == 1; + }); } -bool CanConcealedKan(const GameState& state, int player) { - if (state.walls.GetRemainingPieces() == 0) { +bool CanConcealedKan(const GameState& state, const Hand& hand) { + if (Walls::GetRemainingPieces(state) == 0) { return false; } // TODO(#19): Allow riichi concealed kan under the right conditions - if (state.hands.at(player).riichi) { + if (hand.riichi) { return false; } - return CountPieces(state, player, state.pendingPiece) == 4; + return CountPieces(hand, state.pendingPiece) == 4; } -bool CanRiichi(const GameState& state, int player) { - if (state.hands.at(player).riichi) { +bool CanRiichi(const GameState& /*unused*/, const Hand& hand) { + if (hand.riichi) { return false; } - if (state.hands.at(player).open) { + if (hand.open) { return false; } - return !getPossibleWaits(state.hands[player]).empty(); + return !getPossibleWaits(hand).empty(); } } // namespace mahjong diff --git a/src/statefunctions/decisionfunction.h b/src/statefunctions/decisionfunction.h index 4ebaa376..f7df2d78 100644 --- a/src/statefunctions/decisionfunction.h +++ b/src/statefunctions/decisionfunction.h @@ -4,23 +4,24 @@ #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong { using DecisionFunction = - std::function; + std::function; struct PossibleDecision { Event::Type type; DecisionFunction func; }; -bool CanKan(const GameState& state, int player); -bool CanPon(const GameState& state, int player); -bool CanChi(const GameState& state, int player); -bool CanRon(const GameState& state, int player); +bool CanKan(const GameState& state, const Hand& hand); +bool CanPon(const GameState& state, const Hand& hand); +bool CanChi(const GameState& state, const Hand& hand); +bool CanRon(const GameState& state, const Hand& hand); -bool CanTsumo(const GameState& state, int player); -bool CanConcealedKan(const GameState& state, int player); -bool CanConvertedKan(const GameState& state, int player); -bool CanRiichi(const GameState& state, int player); +bool CanTsumo(const GameState& state, const Hand& hand); +bool CanConcealedKan(const GameState& state, const Hand& hand); +bool CanConvertedKan(const GameState& state, const Hand& hand); +bool CanRiichi(const GameState& state, const Hand& hand); } // namespace mahjong diff --git a/src/statefunctions/gamestates/chi.cc b/src/statefunctions/gamestates/chi.cc index d8b01d1f..e72edcb1 100644 --- a/src/statefunctions/gamestates/chi.cc +++ b/src/statefunctions/gamestates/chi.cc @@ -2,12 +2,13 @@ #include #include #include -#include #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" +#include "types/meld.h" #include "types/pieces.h" #include "types/piecetype.h" #include "types/sets.h" @@ -15,40 +16,57 @@ namespace mahjong { namespace { -Piece GetChiStart(const GameState& state, int player) { - if (CountPieces(state, player, state.pendingPiece - 2) > 0 && - CountPieces(state, player, state.pendingPiece - 1) > 0) { - return state.pendingPiece - 2; +Piece GetChiStart(const Hand& hand, Piece piece) { + if (CountPieces(hand, piece - 2) > 0 && CountPieces(hand, piece - 1) > 0) { + return piece - 2; } - if (CountPieces(state, player, state.pendingPiece - 1) > 0 && - CountPieces(state, player, state.pendingPiece + 1) > 0) { - return state.pendingPiece - 1; + if (CountPieces(hand, piece - 1) > 0 && CountPieces(hand, piece + 1) > 0) { + return piece - 1; } - if (CountPieces(state, player, state.pendingPiece + 1) > 0 && - CountPieces(state, player, state.pendingPiece + 2) > 0) { - return state.pendingPiece; + if (CountPieces(hand, piece + 1) > 0 && CountPieces(hand, piece + 2) > 0) { + return piece; } return kError; } std::unique_ptr Chi(std::unique_ptr state) { + if (Hand& hand = state->hands[state->currentPlayer]; + hand.riichi && hand.discards_count == hand.riichiPieceDiscard) { + hand.riichiPieceDiscard++; + } + + state->currentPlayer = state->lastCaller; + state->lastCall = state->turnNum; + state->concealedKan = false; + state->turnNum++; + + Hand& hand = state->hands[state->currentPlayer]; + hand.open = true; // only gives a single one of the chis // ui oof - const Piece chi_start = GetChiStart(*state, state->lastCaller); + const Piece chi_start = GetChiStart(hand, state->pendingPiece); if (chi_start == kError) { std::cerr << "Failed to get start of Chi" << '\n'; state->nextState = StateFunctionType::kError; return state; } - if (state->hands.at(state->currentPlayer).riichi && - state->hands.at(state->currentPlayer).discards.size() == - state->hands.at(state->currentPlayer).riichiPieceDiscard) { - state->hands.at(state->currentPlayer).riichiPieceDiscard++; + for (int i = 0; i < 3; ++i) { + if (chi_start + i == state->pendingPiece) { + continue; + } + if (RemovePieces(hand, chi_start + i, + /*count=*/1) != 1) { + std::cerr << "Not Enough Pieces to remove in Chi" << '\n'; + state->nextState = StateFunctionType::kError; + return state; + } } - state->hands.at(state->lastCaller).open = true; - state->currentPlayer = state->lastCaller; + hand.melds[hand.meld_count++] = Meld{ + .type = SetType::kChi, + .start = chi_start, + }; AlertPlayers( *state, @@ -59,24 +77,6 @@ std::unique_ptr Chi(std::unique_ptr state) { .decision = false, // decision }); - state->hands.at(state->lastCaller).live.push_back(state->pendingPiece); - state->hands.at(state->lastCaller).sort(); - state->lastCall = state->turnNum; - state->concealedKan = false; - state->turnNum++; - - if (RemovePieces(*state, state->lastCaller, chi_start, /*count=*/1) != 1 || - RemovePieces(*state, state->lastCaller, chi_start + 1, /*count=*/1) != - 1 || - RemovePieces(*state, state->lastCaller, chi_start + 2, /*count=*/1) != - 1) { - std::cerr << "Not Enough Pieces to remove in Chi" << '\n'; - state->nextState = StateFunctionType::kError; - return state; - } - state->hands.at(state->lastCaller) - .melds.push_back({SetType::kChi, chi_start}); - state->pendingPiece = AskForDiscard(*state); state->nextState = StateFunctionType::kDiscard; diff --git a/src/statefunctions/gamestates/concealedkan.cc b/src/statefunctions/gamestates/concealedkan.cc index 819d2528..d0d972c3 100644 --- a/src/statefunctions/gamestates/concealedkan.cc +++ b/src/statefunctions/gamestates/concealedkan.cc @@ -2,12 +2,13 @@ #include #include #include -#include #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" +#include "types/meld.h" #include "types/piecetype.h" #include "types/sets.h" #include "types/statefunction.h" @@ -23,14 +24,17 @@ std::unique_ptr ConcealedKan(std::unique_ptr state) { state->pendingPiece.toUint8_t()), // piece .decision = false, // decision }); - if (RemovePieces(*state, state->currentPlayer, state->pendingPiece, + Hand& hand = state->hands[state->currentPlayer]; + if (RemovePieces(hand, state->pendingPiece, /*count=*/4) != 4) { std::cerr << "Not Enough pieces to remove in ConcealedKan" << '\n'; state->nextState = StateFunctionType::kError; return state; } - state->hands.at(state->currentPlayer) - .melds.push_back({SetType::kConcealedKan, state->pendingPiece}); + hand.melds[hand.meld_count++] = Meld{ + .type = SetType::kConcealedKan, + .start = state->pendingPiece, + }; state->concealedKan = true; state->nextState = StateFunctionType::kKanDiscard; return state; diff --git a/src/statefunctions/gamestates/convertedkan.cc b/src/statefunctions/gamestates/convertedkan.cc index 92a46442..facf987c 100644 --- a/src/statefunctions/gamestates/convertedkan.cc +++ b/src/statefunctions/gamestates/convertedkan.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,6 +8,7 @@ #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/meld.h" #include "types/piecetype.h" #include "types/sets.h" @@ -22,22 +24,22 @@ std::unique_ptr ConvertedKan(std::unique_ptr state) { state->pendingPiece.toUint8_t()), // piece .decision = false, // decision }); - if (RemovePieces(*state, state->currentPlayer, state->pendingPiece, + Hand& hand = state->hands[state->currentPlayer]; + if (RemovePieces(hand, state->pendingPiece, /*count=*/1) != 1) { std::cerr << "Not Enough pieces to remove in ConvertedKan" << '\n'; state->nextState = StateFunctionType::kError; return state; } - state->concealedKan = false; - for (auto& meld : state->hands.at(state->currentPlayer).melds) { - if (meld.type == SetType::kPon && meld.start == state->pendingPiece) { - meld.type = SetType::kKan; - state->nextState = StateFunctionType::kKanDiscard; - return state; - } + Meld* meld = std::ranges::find( + hand.melds, Meld{.type = SetType::kPon, .start = state->pendingPiece}); + if (meld == hand.melds.end()) { + std::cerr << "Could Not find matching pon" << '\n'; + state->nextState = StateFunctionType::kError; + return state; } - std::cerr << "Could Not find matching pon" << '\n'; - state->nextState = StateFunctionType::kError; + meld->type = SetType::kKan; + state->nextState = StateFunctionType::kKanDiscard; return state; } } // namespace diff --git a/src/statefunctions/gamestates/discard.cc b/src/statefunctions/gamestates/discard.cc index 67ae2a25..88e5aa45 100644 --- a/src/statefunctions/gamestates/discard.cc +++ b/src/statefunctions/gamestates/discard.cc @@ -4,12 +4,12 @@ #include #include -#include "controllers/playercontroller.h" #include "statefunctions/decisionfunction.h" #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" #include "types/walls.h" @@ -25,7 +25,7 @@ std::unique_ptr Discard(std::unique_ptr state) { state->pendingPiece.toUint8_t()), // piece .decision = false, // decision }); - DiscardPiece(*state, state->currentPlayer, state->pendingPiece); + DiscardPiece(state->hands[state->currentPlayer], state->pendingPiece); const std::vector decisions = { {.type = Event::kChi, .func = CanChi}, @@ -35,14 +35,14 @@ std::unique_ptr Discard(std::unique_ptr state) { }; std::array need_decision = {false, false, false, false}; - for (int player = 0; player < 4; player++) { - if (player == state->currentPlayer) { + for (const Hand& hand : state->hands) { + if (hand.id == state->currentPlayer) { continue; } for (const auto& [decision, decisionIsPossible] : decisions) { - if (decisionIsPossible(*state, player)) { - need_decision.at(player) = true; - state->players.at(player).controller->ReceiveEvent(Event{ + if (decisionIsPossible(*state, hand)) { + need_decision.at(hand.id) = true; + state->controllers.at(hand.id)->ReceiveEvent(Event{ .type = decision, // type .player = state->currentPlayer, // player .piece = @@ -54,24 +54,24 @@ std::unique_ptr Discard(std::unique_ptr state) { } Event decision = kDeclineEvent; - for (int i = 0; i < 4; i++) { - if (need_decision.at(i)) { + for (Hand& hand : state->hands) { + if (need_decision.at(hand.id)) { Event temp_decision = - GetValidDecisionOrThrow(*state, i, /*inHand=*/false); + GetValidDecisionOrThrow(*state, hand, /*inHand=*/false); if (temp_decision.type < decision.type) { // lower is higher priority - temp_decision.player = i; + temp_decision.player = hand.id; temp_decision.piece = static_cast(state->pendingPiece.toUint8_t()); decision = temp_decision; } if (temp_decision.type == Event::kRon) { - state->hasRonned.at(i) = true; + hand.hasRonned = true; } } } if (decision.type == Event::kDecline && - state->walls.GetRemainingPieces() == 0) { + Walls::GetRemainingPieces(*state) == 0) { state->nextState = StateFunctionType::kExhaust; return state; } diff --git a/src/statefunctions/gamestates/draw.cc b/src/statefunctions/gamestates/draw.cc index f5c55ad6..bbd30297 100644 --- a/src/statefunctions/gamestates/draw.cc +++ b/src/statefunctions/gamestates/draw.cc @@ -1,10 +1,9 @@ #include #include -#include #include "statefunctions/router.h" #include "types/gamestate.h" -#include "types/piecetype.h" +#include "types/hand.h" #include "types/statefunction.h" #include "types/walls.h" @@ -13,9 +12,9 @@ namespace { std::unique_ptr Draw(std::unique_ptr state) { state->currentPlayer = (state->currentPlayer + 1) % 4; state->turnNum++; - state->pendingPiece = state->walls.TakePiece(); - state->hands.at(state->currentPlayer).live.push_back(state->pendingPiece); - state->hands.at(state->currentPlayer).sort(); + state->pendingPiece = Walls::TakePiece(*state); + Hand& hand = state->hands[state->currentPlayer]; + hand.live[hand.live_count++] = state->pendingPiece; state->nextState = StateFunctionType::kPlayerHand; return state; } diff --git a/src/statefunctions/gamestates/exhaust.cc b/src/statefunctions/gamestates/exhaust.cc index 39d1b7b5..37d8ad01 100644 --- a/src/statefunctions/gamestates/exhaust.cc +++ b/src/statefunctions/gamestates/exhaust.cc @@ -14,8 +14,8 @@ std::unique_ptr Exhaust(std::unique_ptr state) { int total_winners = 0; for (int i = 0; i < 4; i++) { // TODO(#21): Implement no tenpai if you have all pieces of your wait - if (state->hands.at(i).riichi || !getWaits(state->hands[i]).empty()) { - winning_players.at(i) = 1; + if (state->hands[i].riichi || !getWaits(state->hands[i]).empty()) { + winning_players[i] = 1; total_winners++; } } @@ -25,25 +25,25 @@ std::unique_ptr Exhaust(std::unique_ptr state) { } if (total_winners < 4 && total_winners > 0) { for (int i = 0; i < 4; i++) { - if (winning_players.at(i) != 0) { - state->scores.at(i) = 3000 / total_winners; + if (winning_players[i] != 0) { + state->hands[i].score = 3000 / total_winners; } else { switch (total_winners) { case 1: - state->scores.at(i) = -1000; + state->hands[i].score = -1000; break; case 2: - state->scores.at(i) = -1500; + state->hands[i].score = -1500; break; case 3: - state->scores.at(i) = -3000; + state->hands[i].score = -3000; break; default: break; } } - if (state->hands.at(i).riichi) { - state->scores.at(i) -= 1000; + if (state->hands[i].riichi) { + state->hands[i].score -= 1000; } } } diff --git a/src/statefunctions/gamestates/gameend.cc b/src/statefunctions/gamestates/gameend.cc index d7b178e8..81f45d72 100644 --- a/src/statefunctions/gamestates/gameend.cc +++ b/src/statefunctions/gamestates/gameend.cc @@ -4,16 +4,15 @@ #include "statefunctions/router.h" #include "types/event.h" #include "types/gamestate.h" -#include "types/player.h" #include "types/statefunction.h" namespace mahjong { namespace { std::unique_ptr GameEnd(std::unique_ptr state) { - for (auto& player : state->players) { - player.controller->ReceiveEvent(kEndEvent); - player.controller.reset(); + for (auto& controller : state->controllers) { + controller->ReceiveEvent(kEndEvent); + controller.reset(); } return state; } diff --git a/src/statefunctions/gamestates/gamestart.cc b/src/statefunctions/gamestates/gamestart.cc index a978d141..067e454b 100644 --- a/src/statefunctions/gamestates/gamestart.cc +++ b/src/statefunctions/gamestates/gamestart.cc @@ -1,9 +1,8 @@ -#include #include -#include "controllers/playercontroller.h" #include "statefunctions/router.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/settings.h" #include "types/statefunction.h" @@ -11,9 +10,11 @@ namespace mahjong { namespace { std::unique_ptr GameStart(std::unique_ptr state) { - for (int i = 0; i < 4; i++) { - state->players.at(i).points = kStartingPoints; - state->players.at(i).controller->GameStart(i); + int i = 0; + for (Hand& hand : state->hands) { + hand.id = i++; + hand.points = kStartingPoints; + state->controllers[hand.id]->GameStart(hand.id); } state->g.seed(state->seed); state->nextState = StateFunctionType::kRoundStart; diff --git a/src/statefunctions/gamestates/kan.cc b/src/statefunctions/gamestates/kan.cc index 3b75e01d..9d9582c5 100644 --- a/src/statefunctions/gamestates/kan.cc +++ b/src/statefunctions/gamestates/kan.cc @@ -2,12 +2,13 @@ #include #include #include -#include #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" +#include "types/meld.h" #include "types/piecetype.h" #include "types/sets.h" #include "types/statefunction.h" @@ -24,28 +25,28 @@ std::unique_ptr Kan(std::unique_ptr state) { .decision = false, // decision }); - if (state->hands.at(state->currentPlayer).riichi && - state->hands.at(state->currentPlayer).discards.size() == - state->hands.at(state->currentPlayer).riichiPieceDiscard) { - state->hands.at(state->currentPlayer).riichiPieceDiscard++; + if (Hand& hand = state->hands[state->currentPlayer]; + hand.riichi && hand.discards_count == hand.riichiPieceDiscard) { + hand.riichiPieceDiscard++; } - state->hands.at(state->lastCaller).open = true; - state->currentPlayer = state->lastCaller; - state->hands.at(state->lastCaller).live.push_back(state->pendingPiece); - state->hands.at(state->lastCaller).sort(); state->lastCall = state->turnNum; state->concealedKan = false; state->turnNum++; + state->currentPlayer = state->lastCaller; - if (RemovePieces(*state, state->lastCaller, state->pendingPiece, - /*count=*/4) != 4) { + Hand& hand = state->hands[state->currentPlayer]; + hand.open = true; + if (RemovePieces(hand, state->pendingPiece, + /*count=*/3) != 3) { std::cerr << "Not Enough Pieces to remove in kan" << '\n'; state->nextState = StateFunctionType::kError; return state; } - state->hands.at(state->lastCaller) - .melds.push_back({SetType::kKan, state->pendingPiece}); + hand.melds[hand.meld_count++] = Meld{ + .type = SetType::kKan, + .start = state->pendingPiece, + }; state->nextState = StateFunctionType::kKanDiscard; return state; diff --git a/src/statefunctions/gamestates/kandiscard.cc b/src/statefunctions/gamestates/kandiscard.cc index 11619c85..2699fd30 100644 --- a/src/statefunctions/gamestates/kandiscard.cc +++ b/src/statefunctions/gamestates/kandiscard.cc @@ -2,12 +2,12 @@ #include #include -#include "controllers/playercontroller.h" #include "statefunctions/decisionfunction.h" #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" @@ -16,14 +16,14 @@ namespace mahjong { namespace { std::unique_ptr KanDiscard(std::unique_ptr state) { std::array need_decision = {false, false, false, false}; - for (int player = 0; player < 4; player++) { - if (player == state->currentPlayer) { + for (const Hand& hand : state->hands) { + if (hand.id == state->currentPlayer) { continue; } - if (CanRon(*state, player)) { - need_decision.at(player) = true; - state->players.at(state->currentPlayer) - .controller->ReceiveEvent(Event{ + if (CanRon(*state, hand)) { + need_decision.at(hand.id) = true; + state->controllers.at(state->currentPlayer) + ->ReceiveEvent(Event{ .type = Event::kRon, // type .player = state->currentPlayer, // player .piece = static_cast( @@ -34,12 +34,12 @@ std::unique_ptr KanDiscard(std::unique_ptr state) { } bool have_ronned = false; - for (int i = 0; i < 4; i++) { - if (need_decision.at(i)) { + for (Hand& hand : state->hands) { + if (need_decision.at(hand.id)) { const Event temp_decision = - GetValidDecisionOrThrow(*state, i, /*inHand=*/false); + GetValidDecisionOrThrow(*state, hand, /*inHand=*/false); if (temp_decision.type == Event::kRon) { - state->hasRonned.at(i) = true; + hand.hasRonned = true; have_ronned = true; } } diff --git a/src/statefunctions/gamestates/playerhand.cc b/src/statefunctions/gamestates/playerhand.cc index 4bd2a513..ded98dfb 100644 --- a/src/statefunctions/gamestates/playerhand.cc +++ b/src/statefunctions/gamestates/playerhand.cc @@ -4,14 +4,15 @@ #include #include -#include "controllers/playercontroller.h" #include "statefunctions/decisionfunction.h" #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" +#include "types/typeprinter.h" namespace mahjong { @@ -23,16 +24,17 @@ std::unique_ptr PlayerHand(std::unique_ptr state) { PossibleDecision{.type = Event::kConvertedKan, .func = CanConvertedKan}, PossibleDecision{.type = Event::kRiichi, .func = CanRiichi}, PossibleDecision{.type = Event::kDiscard, - .func = [](const GameState& state, int) { - return !state.hands.at(state.currentPlayer).riichi; + .func = [](const GameState&, const Hand& hand) { + return !hand.riichi; }}}; + const Hand& hand = state->hands[state->currentPlayer]; bool decision_asked = false; for (const auto& [decision, decisionIsPossible] : decisions) { - if (decisionIsPossible(*state, state->currentPlayer)) { + if (decisionIsPossible(*state, hand)) { decision_asked = true; - state->players.at(state->currentPlayer) - .controller->ReceiveEvent(Event{ + state->controllers.at(state->currentPlayer) + ->ReceiveEvent(Event{ .type = decision, // type .player = state->currentPlayer, // player .piece = static_cast( @@ -50,8 +52,8 @@ std::unique_ptr PlayerHand(std::unique_ptr state) { decision.decision = true; state->nextState = StateFunctionType::kDiscard; } else { - decision = - GetValidDecisionOrThrow(*state, state->currentPlayer, /*inHand=*/true); + decision = GetValidDecisionOrThrow(*state, hand, + /*inHand=*/true); } // note riichi handling is a lil borked on the player agency side diff --git a/src/statefunctions/gamestates/pon.cc b/src/statefunctions/gamestates/pon.cc index 6628e23c..f282b8f7 100644 --- a/src/statefunctions/gamestates/pon.cc +++ b/src/statefunctions/gamestates/pon.cc @@ -2,12 +2,13 @@ #include #include #include -#include #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" +#include "types/meld.h" #include "types/piecetype.h" #include "types/sets.h" #include "types/statefunction.h" @@ -16,37 +17,36 @@ namespace mahjong { namespace { std::unique_ptr Pon(std::unique_ptr state) { - state->hands.at(state->lastCaller).open = true; - - AlertPlayers(*state, Event{ - .type = Event::kPon, // type - .player = state->lastCaller, // player - .piece = static_cast( - state->pendingPiece.toUint8_t()), // piece - .decision = false, // decision - }); - - if (state->hands.at(state->currentPlayer).riichi && - state->hands.at(state->currentPlayer).discards.size() == - state->hands.at(state->currentPlayer).riichiPieceDiscard) { - state->hands.at(state->currentPlayer).riichiPieceDiscard++; + if (Hand& hand = state->hands[state->currentPlayer]; + hand.riichi && hand.discards_count == hand.riichiPieceDiscard) { + hand.riichiPieceDiscard++; } state->currentPlayer = state->lastCaller; - state->hands.at(state->lastCaller).live.push_back(state->pendingPiece); - state->hands.at(state->lastCaller).sort(); state->lastCall = state->turnNum; state->concealedKan = false; state->turnNum++; - if (RemovePieces(*state, state->lastCaller, state->pendingPiece, - /*count=*/3) != 3) { + Hand& hand = state->hands.at(state->currentPlayer); + hand.open = true; + if (RemovePieces(hand, state->pendingPiece, + /*count=*/2) != 2) { std::cerr << "Not enough pieces to remove in Pon" << '\n'; state->nextState = StateFunctionType::kError; return state; } - state->hands.at(state->lastCaller) - .melds.push_back({SetType::kPon, state->pendingPiece}); + hand.melds[hand.meld_count++] = Meld{ + .type = SetType::kPon, + .start = state->pendingPiece, + }; + + AlertPlayers(*state, Event{ + .type = Event::kPon, // type + .player = state->lastCaller, // player + .piece = static_cast( + state->pendingPiece.toUint8_t()), // piece + .decision = false, // decision + }); state->pendingPiece = AskForDiscard(*state); diff --git a/src/statefunctions/gamestates/replacement.cc b/src/statefunctions/gamestates/replacement.cc index d9c04297..b3bd1533 100644 --- a/src/statefunctions/gamestates/replacement.cc +++ b/src/statefunctions/gamestates/replacement.cc @@ -7,6 +7,7 @@ #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" #include "types/walls.h" @@ -14,9 +15,9 @@ namespace mahjong { namespace { std::unique_ptr Replacement(std::unique_ptr state) { - const Piece draw = state->walls.TakeReplacementTile(); - state->hands.at(state->currentPlayer).live.push_back(draw); - state->hands.at(state->currentPlayer).sort(); + const Piece draw = Walls::TakeReplacementTile(*state); + Hand& hand = state->hands[state->currentPlayer]; + hand.live[hand.live_count++] = draw; state->pendingPiece = draw; AlertPlayers(*state, @@ -24,7 +25,7 @@ std::unique_ptr Replacement(std::unique_ptr state) { .type = Event::kDora, // type .player = -1, // player .piece = static_cast( - state->walls.GetDoras().back().toUint8_t()), // piece + Walls::GetDoras(*state).back().toUint8_t()), // piece .decision = false, // decision }); diff --git a/src/statefunctions/gamestates/riichi.cc b/src/statefunctions/gamestates/riichi.cc index 66959a58..71238a4a 100644 --- a/src/statefunctions/gamestates/riichi.cc +++ b/src/statefunctions/gamestates/riichi.cc @@ -8,6 +8,7 @@ #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" @@ -15,10 +16,14 @@ namespace mahjong { namespace { std::unique_ptr Riichi(std::unique_ptr state) { + Hand& hand = state->hands[state->currentPlayer]; + hand.riichiRound = state->turnNum; + hand.riichiPieceDiscard = hand.discards_count; + hand.riichi = true; + // TODO(#22): Ask the players if they want to riichi - state->pendingPiece = getPossibleWaits(state->hands[state->currentPlayer]) - .begin() - ->second.front(); + state->pendingPiece = getPossibleWaits(hand).begin()->second.front(); + state->riichiSticks++; AlertPlayers(*state, Event{ @@ -29,12 +34,6 @@ std::unique_ptr Riichi(std::unique_ptr state) { .decision = false, // decision }); - state->hands.at(state->currentPlayer).riichiRound = state->turnNum; - state->hands.at(state->currentPlayer).riichiPieceDiscard = - state->hands.at(state->currentPlayer).discards.size(); - state->hands.at(state->currentPlayer).riichi = true; - state->riichiSticks++; - state->nextState = StateFunctionType::kDiscard; return state; } diff --git a/src/statefunctions/gamestates/ron.cc b/src/statefunctions/gamestates/ron.cc index ccc24046..a29cf546 100644 --- a/src/statefunctions/gamestates/ron.cc +++ b/src/statefunctions/gamestates/ron.cc @@ -1,13 +1,13 @@ #include #include #include -#include #include "scoring/scoring.h" #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" @@ -15,60 +15,59 @@ namespace mahjong { namespace { std::unique_ptr Ron(std::unique_ptr state) { - state->hands.at(state->lastCaller).live.push_back(state->pendingPiece); - state->hands.at(state->lastCaller).sort(); - - std::array basic_points = {}; - if (state->hands.at(state->currentPlayer).riichi && - state->hands.at(state->currentPlayer).discards.size() == - state->hands.at(state->currentPlayer).riichiPieceDiscard) { + if (Hand& hand = state->hands[state->currentPlayer]; + hand.riichi && hand.discards_count == hand.riichiPieceDiscard) { state->riichiSticks--; - state->hands.at(state->currentPlayer).riichi = false; + hand.riichi = false; } - for (int player = 0; player < 4; player++) { - if (state->hasRonned.at(player)) { + + state->hands[state->lastCaller] + .live[state->hands[state->lastCaller].live_count++] = state->pendingPiece; + + std::array basic_points = {}; + for (Hand& hand : state->hands) { + if (hand.hasRonned) { AlertPlayers(*state, Event{ .type = Event::kRon, // type - .player = player, // player + .player = hand.id, // player .piece = static_cast( state->pendingPiece.toUint8_t()), // piece .decision = false, // decision }); - basic_points.at(player) = getBasicPoints(scoreHand(*state, player)); + basic_points.at(hand.id) = getBasicPoints(scoreHand(*state, hand)); } - if (state->hands.at(player).riichi) { - state->scores.at(player) -= 1000; + if (hand.riichi) { + hand.score -= 1000; } } int payment = 0; - for (int i = 0; i < 4; i++) { - const int player = (state->roundNum + i) % 4; - if (state->hasRonned.at(player)) { - state->scores.at(player) += 1000 * state->riichiSticks; + for (Hand& hand : state->hands) { + if (hand.hasRonned) { + hand.score += 1000 * state->riichiSticks; state->riichiSticks = 0; - state->scores.at(player) += 300 * state->counters; + hand.score += 300 * state->counters; payment += 300 * state->counters; - if (i == state->roundNum % 4) { - int amount = 6 * basic_points.at(player); + if (hand.id == state->roundNum % 4) { + int amount = 6 * basic_points.at(hand.id); if ((amount % 100) != 0) { amount = amount + (100 - (amount % 100)); } - state->scores.at(player) += amount; + hand.score += amount; payment += amount; } else { - int amount = 4 * basic_points.at(player); + int amount = 4 * basic_points.at(hand.id); if ((amount % 100) != 0) { amount = amount + (100 - (amount % 100)); } - state->scores.at(player) += amount; + hand.score += amount; payment += amount; } } } - state->scores.at(state->currentPlayer) -= payment; + state->hands[state->currentPlayer].score -= payment; - if (state->hasRonned.at(state->roundNum % 4)) { + if (state->hands[state->roundNum % 4].hasRonned) { state->counters++; } else { state->roundNum++; @@ -77,7 +76,7 @@ std::unique_ptr Ron(std::unique_ptr state) { bool allzeros = true; for (int i = 0; i < 4; i++) { - if (state->scores.at(i) != 0) { + if (state->hands[i].score != 0) { allzeros = false; } } diff --git a/src/statefunctions/gamestates/roundend.cc b/src/statefunctions/gamestates/roundend.cc index 7beef2cf..f53b545d 100644 --- a/src/statefunctions/gamestates/roundend.cc +++ b/src/statefunctions/gamestates/roundend.cc @@ -7,6 +7,7 @@ #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" @@ -20,39 +21,40 @@ std::unique_ptr RoundEnd(std::unique_ptr state) { state->concealedKan = false; state->lastCaller = -1; state->pendingPiece = Piece(Piece::kError); - state->hasRonned = {}; + for (auto& hand : state->hands) { + hand.hasRonned = false; + } - state->hands = {}; + state->controllers = {}; const int last_round = 3; if (state->roundNum > last_round && state->riichiSticks > 0) { std::vector winners; int highscore = -100000; - for (int i = 0; i < 4; i++) { - if (state->players.at(i).points + state->scores.at(i) > highscore) { - highscore = state->players.at(i).points + state->scores.at(i); + for (const Hand& hand : state->hands) { + if (hand.points + hand.score > highscore) { + highscore = hand.points + hand.score; winners.clear(); - winners.push_back(i); - } else if (state->players.at(i).points + state->scores.at(i) == - highscore) { - winners.push_back(i); + winners.push_back(hand.id); + } else if (hand.points + hand.score == highscore) { + winners.push_back(hand.id); } } for (const auto& winner : winners) { - state->scores.at(winner) += (state->riichiSticks * 1000) / winners.size(); + state->hands.at(winner).score += + (state->riichiSticks * 1000) / winners.size(); } } // TODO(#17): Scoring - for (int i = 0; i < 4; i++) { - AlertPlayers(*state, - Event{.type = Event::kPointDiff, - .player = i, - .piece = static_cast(state->scores.at(i) / 100), - .decision = false}); - state->players.at(i).points += state->scores.at(i); + for (Hand& hand : state->hands) { + AlertPlayers(*state, Event{.type = Event::kPointDiff, + .player = hand.id, + .piece = static_cast(hand.score / 100), + .decision = false}); + hand.points += hand.score; + hand.score = 0; } - state->scores = {}; // TODO (#14): for now naively increment the round number state->roundNum++; diff --git a/src/statefunctions/gamestates/roundstart.cc b/src/statefunctions/gamestates/roundstart.cc index cf604dca..fdb92f95 100644 --- a/src/statefunctions/gamestates/roundstart.cc +++ b/src/statefunctions/gamestates/roundstart.cc @@ -1,10 +1,9 @@ +#include #include -#include #include #include #include -#include "controllers/playercontroller.h" #include "statefunctions/router.h" #include "statefunctions/stateutilities.h" #include "types/event.h" @@ -18,13 +17,14 @@ namespace mahjong { namespace { std::unique_ptr RoundStart(std::unique_ptr state) { - state->walls = Walls(state->g); - for (size_t i = 0; i < 4; i++) { - auto hand = state->walls.TakeHand(); - state->players.at(i).controller->RoundStart( - hand, static_cast((i + 3 * (state->roundNum % 4)) % 4), + Walls::New(*state); + for (Hand& hand : state->hands) { + auto draw = Walls::TakeHand(*state); + state->controllers.at(hand.id)->RoundStart( + draw, static_cast((hand.id + 3 * (state->roundNum % 4)) % 4), (state->roundNum > 3) ? kSouth : kEast); - state->hands.at(i) = Hand(hand); + hand.live_count = draw.size(); + std::ranges::move(draw, hand.live.begin()); } AlertPlayers(*state, @@ -32,7 +32,7 @@ std::unique_ptr RoundStart(std::unique_ptr state) { .type = Event::kDora, // type .player = -1, // player .piece = static_cast( - state->walls.GetDoras()[0].toUint8_t()), // piece + Walls::GetDoras(*state)[0].toUint8_t()), // piece .decision = false, // decision }); diff --git a/src/statefunctions/gamestates/tsumo.cc b/src/statefunctions/gamestates/tsumo.cc index 88994182..e1f0bb8f 100644 --- a/src/statefunctions/gamestates/tsumo.cc +++ b/src/statefunctions/gamestates/tsumo.cc @@ -7,6 +7,7 @@ #include "statefunctions/stateutilities.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/statefunction.h" @@ -21,22 +22,23 @@ std::unique_ptr Tsumo(std::unique_ptr state) { state->pendingPiece.toUint8_t()), // piece .decision = false, // decision }); - const int basic_points = - getBasicPoints(scoreHand(*state, state->currentPlayer)); - state->scores.at(state->currentPlayer) += state->riichiSticks * 1000; + + Hand& winning_hand = state->hands[state->currentPlayer]; + const int basic_points = getBasicPoints(scoreHand(*state, winning_hand)); + winning_hand.score += state->riichiSticks * 1000; state->riichiSticks = 0; - state->scores.at(state->currentPlayer) += state->counters * 300; + winning_hand.score += state->counters * 300; - for (int i = 0; i < 4; i++) { - if (i == state->currentPlayer) { - if (state->hands.at(state->currentPlayer).riichi) { - state->scores.at(i) -= 1000; + for (Hand& hand : state->hands) { + if (hand.id == winning_hand.id) { + if (hand.riichi) { + hand.score -= 1000; } continue; } int amount = 0; if (state->currentPlayer == state->roundNum % 4 || - i == state->roundNum % 4) { + hand.id == state->roundNum % 4) { amount = 2 * basic_points; } else { amount = basic_points; @@ -44,12 +46,12 @@ std::unique_ptr Tsumo(std::unique_ptr state) { if ((amount % 100) != 0) { amount = amount + (100 - (amount % 100)); } - state->scores.at(i) -= amount; - state->scores.at(i) -= state->counters * 100; - if (state->hands.at(i).riichi) { - state->scores.at(i) -= 1000; + hand.score -= amount; + hand.score -= state->counters * 100; + if (hand.riichi) { + hand.score -= 1000; } - state->scores.at(state->currentPlayer) += amount; + winning_hand.score += amount; } if (state->currentPlayer == state->roundNum % 4) { diff --git a/src/statefunctions/statecontroller.cc b/src/statefunctions/statecontroller.cc index 6f9a65c6..b4f3d559 100644 --- a/src/statefunctions/statecontroller.cc +++ b/src/statefunctions/statecontroller.cc @@ -42,9 +42,8 @@ void ExitGame(int game) { std::unique_ptr InitGameState(const GameSettings& settings) { auto state = std::make_unique(); for (int i = 0; i < 4; i++) { - state->players.at(i).controller = - ControllerManager::Instance().NewController( - settings.seatControllers.at(i)); + state->controllers[i] = ControllerManager::Instance().NewController( + settings.seatControllers[i]); } if (settings.seed != 0U) { state->seed = settings.seed; diff --git a/src/statefunctions/stateutilities.cc b/src/statefunctions/stateutilities.cc index 7e7a101c..fa99b390 100644 --- a/src/statefunctions/stateutilities.cc +++ b/src/statefunctions/stateutilities.cc @@ -5,14 +5,14 @@ #include #include #include -#include +#include #include "controllers/playercontroller.h" #include "statefunctions/decisionfunction.h" #include "types/event.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" -#include "types/player.h" #include "types/winds.h" namespace mahjong { @@ -24,56 +24,53 @@ Wind GetSeat(int round, int player) { // Push Event to Player Queue void AlertPlayers(const GameState& state, Event e) { e.decision = false; - for (const auto& player : state.players) { - player.controller->ReceiveEvent(e); + for (const auto& controller : state.controllers) { + controller->ReceiveEvent(e); } } // Count number of piece p that are in given players hands -uint8_t CountPieces(const GameState& state, int player, Piece p) { - return std::count(state.hands.at(player).live.begin(), - state.hands.at(player).live.end(), p); +uint8_t CountPieces(const Hand& hand, Piece p) { + return std::ranges::count(hand.live_range(), p); } // Remove an instance of piece p from given players hand -uint8_t RemovePieces(GameState& state, int player, Piece p, uint8_t count) { - count = std::min(CountPieces(state, player, p), count); +uint8_t RemovePieces(Hand& hand, Piece p, uint8_t count) { uint8_t removed = 0; - state.hands.at(player).live.erase( - std::remove_if(state.hands.at(player).live.begin(), - state.hands.at(player).live.end(), - [&](Piece _p) { - if (count > removed && p == _p) { - removed++; - return true; - } - return false; - }), - state.hands.at(player).live.end()); + count = std::min(CountPieces(hand, p), count); + const auto match_piece = [&](Piece _p) { + if (count > removed && p == _p) { + removed++; + return true; + } + return false; + }; + std::ranges::remove_if(hand.live, match_piece); + hand.live_count -= removed; return removed; } // Discard an instance of piece p from given players hand -void DiscardPiece(GameState& state, int player, Piece p) { - RemovePieces(state, player, p, /*count=*/1); - state.hands.at(player).discards.push_back(p); +void DiscardPiece(Hand& hand, Piece p) { + RemovePieces(hand, p, /*count=*/1); + hand.discards[hand.discards_count++] = p; } Piece AskForDiscard(const GameState& state) { - state.players.at(state.currentPlayer) - .controller->ReceiveEvent(Event{ - .type = Event::kDiscard, // type - .player = state.currentPlayer, // player - .piece = 0, // piece - .decision = true, // decision - }); + state.controllers[state.currentPlayer]->ReceiveEvent(Event{ + .type = Event::kDiscard, // type + .player = state.currentPlayer, // player + .piece = 0, // piece + .decision = true, // decision + }); - return Piece( - GetValidDecisionOrThrow(state, state.currentPlayer, /*inHand=*/true) - .piece); + return Piece(GetValidDecisionOrThrow(state, state.hands[state.currentPlayer], + /*inHand=*/true) + .piece); } -Event GetValidDecisionOrThrow(const GameState& state, int player, bool inHand) { +Event GetValidDecisionOrThrow(const GameState& state, const Hand& hand, + bool inHand) { Event decision; bool valid = false; int i = 0; @@ -82,29 +79,28 @@ Event GetValidDecisionOrThrow(const GameState& state, int player, bool inHand) { Event replacement_decision = decision; replacement_decision.type = inHand ? Event::kDiscard : Event::kDecline; if (inHand) { - replacement_decision.piece = static_cast( - state.hands.at(player).live.back().toUint8_t()); + replacement_decision.piece = hand.live_range().back().toUint8_t(); } - if (ValidateDecision(state, player, replacement_decision, inHand)) { + if (ValidateDecision(state, hand, replacement_decision, inHand)) { return replacement_decision; } std::cerr << "WARNING: Player Controller sent invalid event too many times." << '\n'; std::cerr << "Decision.type: " << decision.type << " Decision.piece " - << decision.piece << " player: " << player + << decision.piece << " player: " << hand.id << " inHand: " << (inHand ? "true" : "false") << '\n'; std::cerr << "ERROR: was not able to recover from invalid event." << '\n'; throw 0xBAD22222; } i++; - decision = state.players.at(player).controller->RetrieveDecision(); - valid = ValidateDecision(state, player, decision, inHand); + decision = state.controllers[hand.id]->RetrieveDecision(); + valid = ValidateDecision(state, hand, decision, inHand); } return decision; } -bool ValidateDecision(const GameState& state, int player, Event decision, +bool ValidateDecision(const GameState& state, const Hand& hand, Event decision, bool inHand) { if (decision.type > Event::kDiscard) { return false; @@ -117,23 +113,23 @@ bool ValidateDecision(const GameState& state, int player, Event decision, } switch (decision.type) { case Event::kRon: - return CanRon(state, player); + return CanRon(state, hand); case Event::kKan: - return CanKan(state, player); + return CanKan(state, hand); case Event::kPon: - return CanPon(state, player); + return CanPon(state, hand); case Event::kChi: - return CanChi(state, player); + return CanChi(state, hand); case Event::kTsumo: - return CanTsumo(state, player); + return CanTsumo(state, hand); case Event::kConcealedKan: - return CanConcealedKan(state, player); + return CanConcealedKan(state, hand); case Event::kConvertedKan: - return CanConvertedKan(state, player); + return CanConvertedKan(state, hand); case Event::kRiichi: - return CanRiichi(state, player); + return CanRiichi(state, hand); case Event::kDiscard: - return CountPieces(state, player, Piece(decision.piece)) > 0; + return CountPieces(hand, Piece(decision.piece)) > 0; case Event::kDecline: return true; default: diff --git a/src/statefunctions/stateutilities.h b/src/statefunctions/stateutilities.h index 13fcd2f7..e98e7e32 100644 --- a/src/statefunctions/stateutilities.h +++ b/src/statefunctions/stateutilities.h @@ -2,6 +2,8 @@ #include #include "types/event.h" +#include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/winds.h" @@ -10,14 +12,15 @@ struct GameState; Wind GetSeat(int round, int player); -uint8_t RemovePieces(GameState& state, int player, Piece p, uint8_t count); -void DiscardPiece(GameState& state, int player, Piece p); +uint8_t RemovePieces(Hand& hand, Piece p, uint8_t count); +void DiscardPiece(Hand& hand, Piece p); void AlertPlayers(const GameState& state, Event e); Piece AskForDiscard(const GameState& state); -uint8_t CountPieces(const GameState& state, int player, Piece p); -bool ValidateDecision(const GameState& state, int player, Event decision, +uint8_t CountPieces(const Hand& hand, Piece p); +bool ValidateDecision(const GameState& state, const Hand& hand, Event decision, bool inHand); -Event GetValidDecisionOrThrow(const GameState& state, int player, bool inHand); +Event GetValidDecisionOrThrow(const GameState& state, const Hand& hand, + bool inHand); } // namespace mahjong diff --git a/src/types/CMakeLists.txt b/src/types/CMakeLists.txt index 5fb3d06e..8f337895 100644 --- a/src/types/CMakeLists.txt +++ b/src/types/CMakeLists.txt @@ -12,7 +12,6 @@ target_sources( "meld.h" "pieces.h" "piecetype.h" - "player.h" "score.h" "settings.h" "statefunction.h" diff --git a/src/types/event.h b/src/types/event.h index c1896766..59ce79d6 100644 --- a/src/types/event.h +++ b/src/types/event.h @@ -1,10 +1,7 @@ #pragma once #include -#include #include -#include "types/piecetype.h" - namespace mahjong { struct Event { diff --git a/src/types/gamestate.h b/src/types/gamestate.h index 2dd24668..2cdfff00 100644 --- a/src/types/gamestate.h +++ b/src/types/gamestate.h @@ -2,13 +2,13 @@ #include #include #include +#include +#include "controllers/playercontroller.h" #include "types/hand.h" #include "types/piecetype.h" -#include "types/player.h" #include "types/settings.h" #include "types/statefunction.h" -#include "types/walls.h" namespace mahjong { @@ -20,18 +20,23 @@ struct GameState { int counters = 0; int lastCall = -1; int lastCaller = -1; + int livingWallIndex = 0; + int deadWallIndex = 0; + int doraCount = 1; bool concealedKan = false; + Piece pendingPiece = Piece(Piece::Type::kError); + + std::array hands; + std::array livingWall; + std::array deadWall; + uint64_t seed = 0; std::mt19937_64 g; - Piece pendingPiece = Piece(Piece::Type::kError); + std::array, kNumPlayers> controllers = {}; + StateFunctionType prevState; StateFunctionType currState; StateFunctionType nextState; - Walls walls; - std::array scores = {}; - std::array hasRonned = {}; - std::array hands = {}; - std::array players = {}; }; } // namespace mahjong diff --git a/src/types/hand.h b/src/types/hand.h index 69476c81..8cf70f6a 100644 --- a/src/types/hand.h +++ b/src/types/hand.h @@ -1,26 +1,32 @@ #pragma once -#include -#include -#include -#include + +#include #include "types/meld.h" #include "types/piecetype.h" - +#include "types/settings.h" namespace mahjong { -class Hand { - public: - Hand() = default; - explicit Hand(std::vector live) : live(std::move(std::move(live))) {} - void sort() { std::ranges::sort(live); } - std::vector live; - std::vector melds; - std::vector discards; - bool open = false; +struct Hand { + int id; + std::array discards; + int discards_count = 0; bool riichi = false; - size_t riichiPieceDiscard = -1; - int riichiRound = -1; + int riichiPieceDiscard; + int riichiRound; + int score; + bool hasRonned; + int points; + bool open; + std::array live; + int live_count = 13; + std::array melds; + int meld_count = 0; + [[nodiscard]] std::ranges::subrange live_range() const { + return std::ranges::subrange(live.begin(), live.begin() + live_count); + } + [[nodiscard]] std::ranges::subrange melds_range() const { + return std::ranges::subrange(melds.begin(), melds.begin() + meld_count); + } }; - } // namespace mahjong diff --git a/src/types/player.h b/src/types/player.h deleted file mode 100644 index 65a5f3ce..00000000 --- a/src/types/player.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include - -#include "controllers/playercontroller.h" - -namespace mahjong { - -struct Player { - int points; - std::unique_ptr controller; -}; - -}; // namespace mahjong diff --git a/src/types/settings.h b/src/types/settings.h index 1378d14a..612c41e9 100644 --- a/src/types/settings.h +++ b/src/types/settings.h @@ -1,9 +1,8 @@ #pragma once +#include #include #include -#include "types/piecetype.h" - namespace mahjong { // TODO(#24): Support 3 player mahjong @@ -11,6 +10,13 @@ constexpr int kNumPlayers = 4; // TODO(#25): Support configuring parts of scoring constexpr int kStartingPoints = 25000; +constexpr int kTileCount = 136; +constexpr int kReplacementCount = kNumPlayers == 4 ? 4 : 8; +constexpr int kDoraCount = 5; +constexpr int kDeadWallCount = (2 * kDoraCount) + kReplacementCount; +constexpr int kLivingWallCount = kTileCount - kDeadWallCount; +constexpr int kMaxDiscardCount = 27; + struct GameSettings { std::vector seatControllers; uint64_t seed = 0; diff --git a/src/types/typeprinter.cc b/src/types/typeprinter.cc index 5f42a530..b920f4ca 100644 --- a/src/types/typeprinter.cc +++ b/src/types/typeprinter.cc @@ -14,8 +14,6 @@ #include "types/hand.h" #include "types/meld.h" #include "types/piecetype.h" -#include "types/player.h" -#include "types/walls.h" std::ostream& operator<<(std::ostream& os, const mahjong::Event& e) { os << "{type: " << e.typeToStr(); @@ -36,19 +34,32 @@ std::ostream& operator<<(std::ostream& os, const mahjong::GameState& state) { os << "lastCaller: " << state.lastCaller << '\n'; os << "seed: " << state.seed << '\n'; os << "pendingPiece: " << state.pendingPiece.toStr() << '\n'; - os << "hasRonned: " << '\n'; - for (size_t i = 0; i < state.hasRonned.size(); i++) { - os << "hasRonned[" << i << "]: {" << state.hasRonned.at(i) << "}" << '\n'; - } os << "hands: " << '\n'; for (size_t i = 0; i < state.hands.size(); i++) { os << "hand[" << i << "]: {" << state.hands.at(i) << "}" << '\n'; } - os << "players: " << '\n'; - for (size_t i = 0; i < state.players.size(); i++) { - os << "player[" << i << "]: " << state.players.at(i) << '\n'; + os << "controllers: " << '\n'; + for (size_t i = 0; i < state.controllers.size(); i++) { + os << "player[" << i << "]: "; + os << "{ controller: " + << ((state.controllers[i] != nullptr) ? state.controllers[i]->Name() + : "NULLPTR"); + os << '\n'; + } + os << "{ doraCount: " << state.doraCount; + os << " livingWall: [" << '\n'; + for (const auto& piece : state.livingWall) { + os << piece.toStr() << ", "; + } + os << "]" << '\n'; + os << " livingWallIndex: " << state.livingWallIndex; + os << " deadWall: [" << '\n'; + for (const auto& piece : state.deadWall) { + os << piece.toStr() << ", "; } - os << state.walls << '\n'; + os << "]" << '\n'; + os << " DeadWallIndex: " << state.deadWallIndex; + os << "}" << '\n'; return os; } @@ -58,14 +69,18 @@ std::ostream& operator<<(std::ostream& os, const mahjong::Hand& hand) { os << " riichiPieceDiscard: " << hand.riichiPieceDiscard; os << " riichiRound: " << hand.riichiRound; os << " live: [" << '\n'; - ; - for (const auto& piece : hand.live) { + os << "hasRonned: " << '\n'; + os << "hasRonned: {" << hand.hasRonned << "}" << '\n'; + os << " points: " << hand.points; + for (const auto& piece : hand.live_range()) { os << piece.toStr() << ", "; } os << "]" << '\n'; os << "melds: [" << '\n'; - for (const auto& meld : hand.melds) { - os << meld << ", "; + for (const auto& meld : hand.melds_range()) { + os << "{ type: " << meld.typeToStr(); + os << ", start: " << meld.start.toStr() << "}"; + os << ", "; } os << "]" << '\n'; os << "discards: [" << '\n'; @@ -92,34 +107,3 @@ std::ostream& operator<<(std::ostream& os, const mahjong::Node& node) { os << '\n'; return os; } - -std::ostream& operator<<(std::ostream& os, const mahjong::Meld& meld) { - os << "{ type: " << meld.typeToStr(); - os << ", start: " << meld.start.toStr() << "}"; - return os; -} - -std::ostream& operator<<(std::ostream& os, const mahjong::Walls& walls) { - os << "{ doraCount: " << walls.doraCount; - os << " replacements: " << walls.replacements; - os << " livingWalls: [" << '\n'; - for (const auto& piece : walls.livingWalls) { - os << piece.toStr() << ", "; - } - os << "]" << '\n'; - os << " deadWall: [" << '\n'; - for (const auto& piece : walls.deadWall) { - os << piece.toStr() << ", "; - } - os << "]" << '\n'; - os << "}"; - return os; -} - -std::ostream& operator<<(std::ostream& os, const mahjong::Player& player) { - os << "{ controller: " - << ((player.controller != nullptr) ? player.controller->Name() - : "NULLPTR"); - os << " points: " << player.points; - return os; -} diff --git a/src/types/typeprinter.h b/src/types/typeprinter.h index 30ecc47b..820e8f80 100644 --- a/src/types/typeprinter.h +++ b/src/types/typeprinter.h @@ -5,11 +5,10 @@ namespace mahjong { struct Event; struct GameState; -class Hand; +struct Hand; class Node; struct Meld; class Walls; -struct Player; } // namespace mahjong std::ostream& operator<<(std::ostream& os, const mahjong::Event& e); @@ -23,5 +22,3 @@ std::ostream& operator<<(std::ostream& os, const mahjong::Node& node); std::ostream& operator<<(std::ostream& os, const mahjong::Meld& meld); std::ostream& operator<<(std::ostream& os, const mahjong::Walls& walls); - -std::ostream& operator<<(std::ostream& os, const mahjong::Player& player); diff --git a/src/types/walls.cc b/src/types/walls.cc index a1584711..90bb4718 100644 --- a/src/types/walls.cc +++ b/src/types/walls.cc @@ -3,11 +3,12 @@ #include #include #include -#include #include +#include "types/gamestate.h" #include "types/pieces.h" #include "types/piecetype.h" +#include "types/settings.h" namespace mahjong { @@ -22,99 +23,68 @@ const std::vector kPieceSet = { kGreenDragon, kRedDragon, kEastWind, kSouthWind, kNorthWind, kWestWind}; -Walls::Walls() { - std::random_device rd; - std::mt19937_64 g(rd()); - - for (int i = 0; i < 4; i++) { - livingWalls.insert(livingWalls.end(), kPieceSet.begin(), kPieceSet.end()); - } - std::shuffle(livingWalls.begin(), livingWalls.end(), g); - - std::move(livingWalls.begin(), livingWalls.begin() + 14, - std::back_inserter(deadWall)); - for (size_t i = 0; i < 14; i++) { - livingWalls.erase(livingWalls.begin()); - } -} - -Walls::Walls(std::mt19937_64& g) { - const std::vector wall; +void Walls::New(GameState& state) { + std::vector living_walls; + living_walls.reserve(kPieceSet.size() * 4); for (int i = 0; i < 4; i++) { - livingWalls.insert(livingWalls.end(), kPieceSet.begin(), kPieceSet.end()); + living_walls.insert(living_walls.end(), kPieceSet.begin(), kPieceSet.end()); } - std::shuffle(livingWalls.begin(), livingWalls.end(), g); + std::shuffle(living_walls.begin(), living_walls.end(), state.g); - std::move(livingWalls.begin(), livingWalls.begin() + 14, - std::back_inserter(deadWall)); - for (size_t i = 0; i < 14; i++) { - livingWalls.erase(livingWalls.begin()); + for (int i = 0; i < kLivingWallCount; ++i) { + state.livingWall[i] = living_walls[i]; } -} - -Walls::Walls(std::vector wall) { - std::swap(livingWalls, wall); - std::move(livingWalls.rbegin(), livingWalls.rbegin() + 14, - std::back_inserter(deadWall)); - for (size_t i = 0; i < 14; i++) { - livingWalls.pop_back(); + for (int i = 0; i < kDeadWallCount; ++i) { + state.deadWall[i] = living_walls[kLivingWallCount + i]; } } -Piece Walls::TakePiece() { - if (!livingWalls.empty()) { - const Piece p = livingWalls.front(); - livingWalls.erase(livingWalls.begin()); - return p; +Piece Walls::TakePiece(GameState& state) { + if (state.livingWallIndex == kLivingWallCount) { + return kError; } - return kError; + const Piece p = state.livingWall[state.livingWallIndex++]; + return p; } -std::vector Walls::TakeHand() { - if (livingWalls.size() < 13) { +std::vector Walls::TakeHand(GameState& state) { + if (state.livingWallIndex + 13 == kLivingWallCount) { return {}; } std::vector hand; - std::move(livingWalls.begin(), livingWalls.begin() + 13, - std::back_inserter(hand)); + hand.resize(13); for (size_t i = 0; i < 13; i++) { - livingWalls.erase(livingWalls.begin()); + hand[i] = state.livingWall[state.livingWallIndex++]; } return hand; } -Piece Walls::TakeReplacementTile() { - if (livingWalls.empty()) { +Piece Walls::TakeReplacementTile(GameState& state) { + if (state.deadWallIndex == kReplacementCount) { return kError; } - if (replacements < 1) { - return kError; - } - replacements--; - const Piece p = deadWall.front(); - deadWall.erase(deadWall.begin()); - deadWall.push_back(livingWalls.back()); - doraCount++; - livingWalls.pop_back(); + const Piece p = state.deadWall[state.deadWallIndex++]; + state.livingWallIndex++; + state.doraCount++; return p; } -std::vector Walls::GetDoras() const { +std::vector Walls::GetDoras(const GameState& state) { std::vector doras; - std::copy_n(deadWall.begin() + replacements, doraCount, + std::copy_n(state.deadWall.begin() + kReplacementCount, state.doraCount, std::back_inserter(doras)); return doras; } -std::vector Walls::GetUraDoras() const { +std::vector Walls::GetUraDoras(const GameState& state) { std::vector doras; - std::copy_n(deadWall.begin() + replacements + doraCount, doraCount, - std::back_inserter(doras)); + std::copy_n(state.deadWall.begin() + kReplacementCount + kDoraCount, + state.doraCount, std::back_inserter(doras)); return doras; } -int Walls::GetRemainingPieces() const { - return livingWalls.size(); +int Walls::GetRemainingPieces(const GameState& state) { + return kLivingWallCount - state.livingWallIndex; } } // namespace mahjong diff --git a/src/types/walls.h b/src/types/walls.h index f6a4c338..64666305 100644 --- a/src/types/walls.h +++ b/src/types/walls.h @@ -1,24 +1,20 @@ #pragma once -#include #include +#include "piecetype.h" +#include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" namespace mahjong { class Walls { public: - explicit Walls(); - explicit Walls(std::mt19937_64& g); - explicit Walls(std::vector wall); - std::vector livingWalls; - std::vector deadWall; - int doraCount = 1; - int replacements = 4; - Piece TakePiece(); - std::vector TakeHand(); - Piece TakeReplacementTile(); - [[nodiscard]] std::vector GetDoras() const; - [[nodiscard]] std::vector GetUraDoras() const; - [[nodiscard]] int GetRemainingPieces() const; + static void New(GameState& state); + static Piece TakePiece(GameState& state); + static std::vector TakeHand(GameState& state); + static Piece TakeReplacementTile(GameState& state); + [[nodiscard]] static std::vector GetDoras(const GameState& state); + [[nodiscard]] static std::vector GetUraDoras(const GameState& state); + [[nodiscard]] static int GetRemainingPieces(const GameState& state); }; } // namespace mahjong diff --git a/src/types/yaku.h b/src/types/yaku.h index b4f4e6ac..c5e64cd9 100644 --- a/src/types/yaku.h +++ b/src/types/yaku.h @@ -4,11 +4,12 @@ #include "analysis/handnode.h" #include "types/gamestate.h" +#include "types/hand.h" namespace mahjong { -using yakuFunc = std::function&)>; +using yakuFunc = std::function&)>; using Han = int; diff --git a/tests/api/game_control.test.cc b/tests/api/game_control.test.cc index 06d15f1a..28aa622f 100644 --- a/tests/api/game_control.test.cc +++ b/tests/api/game_control.test.cc @@ -3,11 +3,9 @@ #include #include #include -#include #include "api/gamestate.h" #include "api/types.h" -#include "controllers/playercontroller.h" #include "types/gamestate.h" #include "types/hand.h" #include "types/piecetype.h" @@ -35,10 +33,9 @@ TEST(Api, SettingsConversion) { mahjong::GameState* state = api::InitGameState(&settings); EXPECT_NE(state, nullptr); EXPECT_EQ(state->seed, settings.seed); - EXPECT_EQ(state->players.size(), settings.num_controllers); - for (uint64_t i = 0; i < state->players.size(); ++i) { - EXPECT_EQ(state->players[i].controller->Name(), - settings.seat_controllers[i]); + EXPECT_EQ(state->controllers.size(), settings.num_controllers); + for (uint64_t i = 0; i < state->controllers.size(); ++i) { + EXPECT_EQ(state->controllers[i]->Name(), settings.seat_controllers[i]); } api::FreeGameState(state); } @@ -85,9 +82,9 @@ TEST(Api, ObserveGameState) { // Check arrays are properly sized and initialized for (int i = 0; i < 4; i++) { - EXPECT_EQ(observed.scores[i], state->scores[i]); - EXPECT_EQ(observed.points[i], state->players[i].points); - EXPECT_EQ(observed.hasRonned[i], state->hasRonned[i]); + EXPECT_EQ(observed.scores[i], state->hands[i].score); + EXPECT_EQ(observed.points[i], state->hands[i].points); + EXPECT_EQ(observed.hasRonned[i], state->hands[i].hasRonned); } api::FreeGameState(state); @@ -127,7 +124,7 @@ TEST(Api, ObserveGameStateAfterAdvancement) { // Check points are copied for (int i = 0; i < 4; i++) { - EXPECT_EQ(observed.points[i], state->players[i].points); + EXPECT_EQ(observed.points[i], state->hands[i].points); } api::FreeGameState(state); @@ -151,7 +148,7 @@ TEST(Api, ObserveGameStateHandsAndDiscards) { const mahjong::Hand& cpp_hand = state->hands[player]; // Check live pieces - EXPECT_EQ(c_hand.livePieceCount, static_cast(cpp_hand.live.size())); + EXPECT_EQ(c_hand.livePieceCount, static_cast(cpp_hand.live_count)); for (int piece = 0; piece < api::kMaxLiveHandSize; piece++) { if (piece < c_hand.livePieceCount) { EXPECT_EQ(c_hand.livePieces[piece], @@ -164,7 +161,7 @@ TEST(Api, ObserveGameStateHandsAndDiscards) { } // Check melds - EXPECT_EQ(c_hand.meldCount, static_cast(cpp_hand.melds.size())); + EXPECT_EQ(c_hand.meldCount, cpp_hand.meld_count); for (int meld = 0; meld < c_hand.meldCount; meld++) { EXPECT_EQ(static_cast(c_hand.melds[meld].type), static_cast(cpp_hand.melds[meld].type)); @@ -174,7 +171,7 @@ TEST(Api, ObserveGameStateHandsAndDiscards) { } // Check discards using CHand structure - EXPECT_EQ(c_hand.discardCount, static_cast(cpp_hand.discards.size())); + EXPECT_EQ(c_hand.discardCount, static_cast(cpp_hand.discards_count)); for (int discard = 0; discard < c_hand.discardCount; discard++) { EXPECT_EQ( c_hand.discards[discard], diff --git a/tests/can_chi.test.cc b/tests/can_chi.test.cc index fdcb319a..7e3b003e 100644 --- a/tests/can_chi.test.cc +++ b/tests/can_chi.test.cc @@ -4,6 +4,7 @@ #include "statefunctions/decisionfunction.h" #include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" #include "types/piecetype.h" @@ -13,31 +14,37 @@ TEST(CanChi, EastShouldChiOnNorth) { GameState state; state.pendingPiece = kOneCharacter; state.currentPlayer = 3; - state.hands[0].live = {kOneCharacter, kTwoCharacter, kThreeCharacter}; - EXPECT_TRUE(CanChi(state, 0)); + Hand& hand = state.hands[0]; + hand.live = {kOneCharacter, kTwoCharacter, kThreeCharacter}; + hand.live_count = 3; + EXPECT_TRUE(CanChi(state, hand)); state.pendingPiece = kTwoCharacter; - EXPECT_TRUE(CanChi(state, 0)); + EXPECT_TRUE(CanChi(state, hand)); state.pendingPiece = kThreeCharacter; - EXPECT_TRUE(CanChi(state, 0)); + EXPECT_TRUE(CanChi(state, hand)); state.pendingPiece = kFourCharacter; - EXPECT_TRUE(CanChi(state, 0)); + EXPECT_TRUE(CanChi(state, hand)); } TEST(CanChi, EastShouldNotChiOnSouth) { GameState state; state.pendingPiece = kOneCharacter; state.currentPlayer = 1; - state.hands[0].live = {kOneCharacter, kTwoCharacter, kThreeCharacter}; - EXPECT_FALSE(CanChi(state, 0)); + Hand& hand = state.hands[0]; + hand.live = {kOneCharacter, kTwoCharacter, kThreeCharacter}; + hand.live_count = 3; + EXPECT_FALSE(CanChi(state, hand)); } TEST(CanChi, NoChiOnHonor) { GameState state; state.pendingPiece = kOneCharacter; state.currentPlayer = 3; - state.hands[0].live = {kOneCharacter, kTwoCharacter, kThreeCharacter}; + Hand& hand = state.hands[0]; + hand.live = {kOneCharacter, kTwoCharacter, kThreeCharacter}; + hand.live_count = 3; state.pendingPiece = kWhiteDragon; - EXPECT_FALSE(CanChi(state, 0)); + EXPECT_FALSE(CanChi(state, hand)); } } // namespace mahjong diff --git a/tests/count_pieces.test.cc b/tests/count_pieces.test.cc index ebe5364f..15e5848a 100644 --- a/tests/count_pieces.test.cc +++ b/tests/count_pieces.test.cc @@ -3,20 +3,20 @@ #include #include "statefunctions/stateutilities.h" -#include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" -#include "types/piecetype.h" namespace mahjong { TEST(CountPieces, CountPieces) { - GameState state; - state.hands[0].live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + Hand hand; + hand.live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + hand.live_count = 4; - EXPECT_EQ(CountPieces(state, 0, kOneCharacter), 1); - EXPECT_EQ(CountPieces(state, 0, kFourPin), 1); - EXPECT_EQ(CountPieces(state, 0, kWhiteDragon), 2); - EXPECT_EQ(CountPieces(state, 0, kGreenDragon), 0); + EXPECT_EQ(CountPieces(hand, kOneCharacter), 1); + EXPECT_EQ(CountPieces(hand, kFourPin), 1); + EXPECT_EQ(CountPieces(hand, kWhiteDragon), 2); + EXPECT_EQ(CountPieces(hand, kGreenDragon), 0); } } // namespace mahjong diff --git a/tests/discard_piece.test.cc b/tests/discard_piece.test.cc index 8e376a24..0f119ee3 100644 --- a/tests/discard_piece.test.cc +++ b/tests/discard_piece.test.cc @@ -1,28 +1,27 @@ #include -#include - #include "statefunctions/stateutilities.h" -#include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" -#include "types/piecetype.h" namespace mahjong { TEST(DiscardPiece, DiscardsCorrectPiece) { - GameState state; - state.hands[0].live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + Hand hand; + hand.live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + hand.live_count = 4; - DiscardPiece(state, 0, kOneCharacter); - EXPECT_EQ(state.hands[0].discards[0], kOneCharacter); + DiscardPiece(hand, kOneCharacter); + EXPECT_EQ(hand.discards[0], kOneCharacter); } TEST(DiscardPiece, DoesntDiscardInCorrectPiece) { - GameState state; - state.hands[0].live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + Hand hand; + hand.live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + hand.live_count = 4; - DiscardPiece(state, 0, kGreenDragon); - EXPECT_EQ(state.hands[0].live.size(), 4); + DiscardPiece(hand, kGreenDragon); + EXPECT_EQ(hand.live_count, 4); } } // namespace mahjong diff --git a/tests/game_play.test.cc b/tests/game_play.test.cc index 96e45639..c89d33cb 100644 --- a/tests/game_play.test.cc +++ b/tests/game_play.test.cc @@ -14,13 +14,14 @@ namespace mahjong { TEST(GamePlay, Furiten) { auto state = GameState(); - state.hands[0] = Hand(HandFromNotation("123m456p234678s44m")); - state.hands[0].open = false; - EXPECT_TRUE(isComplete(state, 0)); + Hand& hand = state.hands[0]; + HandFromNotation("123m456p234678s44m", &hand); + hand.open = false; + EXPECT_TRUE(isComplete(state, hand)); state.pendingPiece = Piece(kFourPin); // Place Four Pin in Discard Pile - state.hands[0].discards = {kFourPin}; - EXPECT_FALSE(CanRon(state, 0)); + hand.discards = {kFourPin}; + EXPECT_FALSE(CanRon(state, hand)); } } // namespace mahjong diff --git a/tests/remove_pieces.test.cc b/tests/remove_pieces.test.cc index c9aeb5fb..d2c97763 100644 --- a/tests/remove_pieces.test.cc +++ b/tests/remove_pieces.test.cc @@ -1,45 +1,47 @@ #include #include -#include #include "statefunctions/stateutilities.h" -#include "types/gamestate.h" +#include "types/hand.h" #include "types/pieces.h" -#include "types/piecetype.h" namespace mahjong { TEST(RemovePieces, RemoveCorrectAmount) { - GameState state; - state.hands[0].live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + Hand hand; + hand.live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + hand.live_count = 4; - EXPECT_EQ(RemovePieces(state, 0, kOneCharacter, 1), 1); - EXPECT_EQ(state.hands[0].live.size(), 3); + EXPECT_EQ(RemovePieces(hand, kOneCharacter, 1), 1); + EXPECT_EQ(hand.live_count, 3); } TEST(RemovePieces, RemoveOnlyTheOnesAvailable) { - GameState state; - state.hands[0].live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + Hand hand; + hand.live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + hand.live_count = 4; - EXPECT_EQ(RemovePieces(state, 0, kFourPin, 2), 1); - EXPECT_EQ(state.hands[0].live.size(), 3); + EXPECT_EQ(RemovePieces(hand, kFourPin, 2), 1); + EXPECT_EQ(hand.live_count, 3); } TEST(RemovePieces, OnlyRemovesOne) { - GameState state; - state.hands[0].live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + Hand hand; + hand.live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + hand.live_count = 4; - EXPECT_EQ(RemovePieces(state, 0, kWhiteDragon, 1), 1); - EXPECT_EQ(state.hands[0].live.back(), kWhiteDragon); + EXPECT_EQ(RemovePieces(hand, kWhiteDragon, 1), 1); + EXPECT_EQ(hand.live[2], kWhiteDragon); } TEST(RemovePieces, RemovesZeroWhenPieceDoesntExist) { - GameState state; - state.hands[0].live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + Hand hand; + hand.live = {kOneCharacter, kFourPin, kWhiteDragon, kWhiteDragon}; + hand.live_count = 4; - EXPECT_EQ(RemovePieces(state, 0, kGreenDragon, 8), 0); - EXPECT_EQ(state.hands[0].live.size(), 4); + EXPECT_EQ(RemovePieces(hand, kGreenDragon, 8), 0); + EXPECT_EQ(hand.live_count, 4); } } // namespace mahjong diff --git a/tests/rules/basic_rules.test.cc b/tests/rules/basic_rules.test.cc index b8ae57bc..d20cfc36 100644 --- a/tests/rules/basic_rules.test.cc +++ b/tests/rules/basic_rules.test.cc @@ -8,6 +8,7 @@ #include "types/gamestate.h" #include "types/settings.h" #include "types/statefunction.h" +#include "types/walls.h" #include "utils/gamestate_utils.h" namespace mahjong { @@ -18,7 +19,7 @@ TEST(GameSetup, playerCount) { // We just ran game start EXPECT_EQ(state->currState, StateFunctionType::kGameStart); // Number of players is as expected - EXPECT_EQ(state->players.size(), kNumPlayers); + EXPECT_EQ(state->hands.size(), kNumPlayers); } TEST(GameSetup, playerScore) { @@ -28,7 +29,7 @@ TEST(GameSetup, playerScore) { EXPECT_EQ(state->currState, StateFunctionType::kGameStart); // All players have the expected starting points for (int i = 0; i < kNumPlayers; i++) { - EXPECT_EQ(state->players[i].points, kStartingPoints); + EXPECT_EQ(state->hands[i].points, kStartingPoints); } } @@ -39,7 +40,7 @@ TEST(RoundSetup, playerHands) { // Each player should have 13 tiles for (int i = 0; i < kNumPlayers; i++) { - EXPECT_EQ(state->hands[i].live.size(), 13) + EXPECT_EQ(state->hands[i].live_count, 13) << "Player " << i << " should have 13 tiles"; } } @@ -53,11 +54,11 @@ TEST(RoundSetup, wall) { EXPECT_EQ(state->seed, 12345); // Check that walls were initialized correctly - EXPECT_EQ(state->walls.livingWalls.size(), 70); - EXPECT_EQ(state->walls.deadWall.size(), 14); + EXPECT_EQ(Walls::GetRemainingPieces(*state), 70); + EXPECT_EQ(state->deadWallIndex, 0); // Check that dora indicator is set - EXPECT_EQ(state->walls.GetDoras().size(), 1); + EXPECT_EQ(Walls::GetDoras(*state).size(), 1); } TEST(TurnOrder, initialPlayer) { @@ -100,25 +101,25 @@ TEST(DrawMechanics, drawIncreasesTileCount) { // All players start with 13 tiles for (int i = 0; i < kNumPlayers; i++) { - EXPECT_EQ(state->hands[i].live.size(), 13); + EXPECT_EQ(state->hands[i].live_count, 13); } - const int walls_before = state->walls.GetRemainingPieces(); + const int walls_before = Walls::GetRemainingPieces(*state); // First player draws state = AdvanceGameState(std::move(state)); EXPECT_EQ(state->currState, StateFunctionType::kDraw); // Player 0 should now have 14 tiles - EXPECT_EQ(state->hands[0].live.size(), 14); + EXPECT_EQ(state->hands[0].live_count, 14); // Other players still have 13 for (int i = 1; i < kNumPlayers; i++) { - EXPECT_EQ(state->hands[i].live.size(), 13); + EXPECT_EQ(state->hands[i].live_count, 13); } // Wall should have one less tile - EXPECT_EQ(state->walls.GetRemainingPieces(), walls_before - 1); + EXPECT_EQ(Walls::GetRemainingPieces(*state), walls_before - 1); } TEST(StateMachine, earlyGameTransitions) { @@ -141,22 +142,22 @@ TEST(DiscardMechanics, discardDecreasesTileCount) { EXPECT_EQ(state->currState, StateFunctionType::kDraw); // Player 0 has 14 tiles after draw - EXPECT_EQ(state->hands[0].live.size(), 14); + EXPECT_EQ(state->hands[0].live_count, 14); // Move through player hand to discard state = AdvanceGameState(std::move(state)); EXPECT_EQ(state->currState, StateFunctionType::kPlayerHand); EXPECT_EQ(state->nextState, StateFunctionType::kDiscard); - const int discards_before = state->hands[0].discards.size(); + const int discards_before = state->hands[0].discards_count; state = AdvanceGameState(std::move(state)); EXPECT_EQ(state->currState, StateFunctionType::kDiscard); // Player should be back to 13 tiles - EXPECT_EQ(state->hands[0].live.size(), 13); + EXPECT_EQ(state->hands[0].live_count, 13); // Discard pile should have increased - EXPECT_EQ(state->hands[0].discards.size(), discards_before + 1); + EXPECT_EQ(state->hands[0].discards_count, discards_before + 1); } } // namespace mahjong diff --git a/tests/rules/calls.test.cc b/tests/rules/calls.test.cc index be8519fe..7b1ba2e5 100644 --- a/tests/rules/calls.test.cc +++ b/tests/rules/calls.test.cc @@ -1,13 +1,15 @@ #include +#include #include #include #include -#include #include "controllers/playercontroller.h" #include "statefunctions/statecontroller.h" #include "types/event.h" +#include "types/gamestate.h" +#include "types/hand.h" #include "types/piecetype.h" #include "types/sets.h" #include "types/statefunction.h" @@ -21,10 +23,10 @@ TEST(Calls, AcceptPon) { // Player 0: Has a 9 bamboo, and discards it // Player 1: Has a pair of 9 bamboo, will pon when player 0 discards - std::vector> controllers; + std::array, 4> controllers; // Player 0 - controllers.push_back( + controllers[0] = std::make_unique([](const Event& e) -> Event { // Discard 9 Bamboo if (e.type == Event::kDiscard) { @@ -34,10 +36,10 @@ TEST(Calls, AcceptPon) { } return PlayerControllerFake::DefaultDecisionCallback(e); - })); + }); // Player 1 - controllers.push_back( + controllers[1] = std::make_unique([](const Event& e) -> Event { // Accept all Pons if (e.type == Event::kPon) { @@ -45,15 +47,18 @@ TEST(Calls, AcceptPon) { } return PlayerControllerFake::DefaultDecisionCallback(e); - })); + }); // Player 2 and 3 are set to default decline all auto state = InitializeTestRound(12345, std::move(controllers)); // Override with hopeless hands - state->hands[0].live = HandFromNotation("1m2p3s4m5p6s7m8p9s1m2p3s4m"); - // Has the pair of nine bamboo - state->hands[1].live = HandFromNotation("1m3m5m7m9m99s2p4p6p1z3z5z7z"); + Hand& hand0 = state->hands[0]; + // 14 Pieces because has drawn in InitializeTestRound. + HandFromNotation("11447m2258p33699s", &hand0); + // Has the pair of nine bamboo 13 Pieces, yet to draw. + Hand& hand1 = state->hands[1]; + HandFromNotation("13579m99s246p135z", &hand1); // Advance to pon event // Should take 4 iterations: draw, player hand, discard, and then the pon @@ -69,12 +74,12 @@ TEST(Calls, AcceptPon) { // - State should be kDiscard (Player 1 needs to discard) EXPECT_EQ(state->currentPlayer, 1); EXPECT_EQ(state->currState, StateFunctionType::kDiscard); - EXPECT_EQ(state->hands[1].melds.size(), 1); - EXPECT_EQ(state->hands[1].melds[0].type, SetType::kPon); - EXPECT_EQ(state->hands[1].melds[0].start, Piece(Piece::kNineBamboo)); + EXPECT_EQ(hand1.meld_count, 1); + EXPECT_EQ(hand1.melds[0].type, SetType::kPon); + EXPECT_EQ(hand1.melds[0].start, Piece(Piece::kNineBamboo)); - // Player 1's hand should be reduced by 2 tiles (used for pon) - EXPECT_EQ(state->hands[1].live.size(), 11); + // Player 1's live hand should be reduced by 3 tiles (pon + discard) + EXPECT_EQ(hand1.live_count, 10); } } // namespace mahjong diff --git a/tests/setup.test.cc b/tests/setup.test.cc index 4b8bb06a..c0a66565 100644 --- a/tests/setup.test.cc +++ b/tests/setup.test.cc @@ -31,22 +31,22 @@ TEST(Setup, PrevalentWind) { TEST(Setup, DoraIndicator) { std::unique_ptr state = std::make_unique(); for (int i = 0; i < 4; i++) { - state->players[i].controller = std::make_unique(); + state->controllers[i] = std::make_unique(); } ASSERT_NO_THROW(state = Router::Instance().Route( StateFunctionType::kRoundStart)(std::move(state))); - EXPECT_EQ(state->walls.GetDoras().size(), 1); + EXPECT_EQ(Walls::GetDoras(*state).size(), 1); } TEST(Setup, Dealing) { std::unique_ptr state = std::make_unique(); for (int i = 0; i < 4; i++) { - state->players[i].controller = std::make_unique(); + state->controllers[i] = std::make_unique(); } ASSERT_NO_THROW(state = Router::Instance().Route( StateFunctionType::kRoundStart)(std::move(state))); for (int i = 0; i < 4; i++) { - EXPECT_EQ(state->hands[i].live.size(), 13); + EXPECT_EQ(state->hands[i].live_count, 13); } state = Router::Instance().Route(StateFunctionType::kDraw)(std::move(state)); EXPECT_EQ(state->currentPlayer, 0); diff --git a/tests/utils/gamestate_utils.cc b/tests/utils/gamestate_utils.cc index 4dec921d..f8fc27c7 100644 --- a/tests/utils/gamestate_utils.cc +++ b/tests/utils/gamestate_utils.cc @@ -1,13 +1,12 @@ #include "gamestate_utils.h" -#include +#include #include #include #include #include #include #include -#include #include "controllers/playercontroller.h" #include "statefunctions/router.h" @@ -53,37 +52,24 @@ std::unique_ptr CreateTestGameState(uint64_t seed) { auto state = std::make_unique(); state->seed = seed; for (int i = 0; i < kNumPlayers; i++) { - state->players[i].controller = std::make_unique(); + state->controllers[i] = std::make_unique(); } state->nextState = StateFunctionType::kGameStart; return state; } std::unique_ptr InitializeTestRound( - uint64_t seed, - std::vector> playerControllers) { + uint64_t seed, std::array, kNumPlayers> + playerControllers) { auto state = std::make_unique(); state->seed = seed; // Use provided controllers or default to PlayerControllerFake - if (!playerControllers.empty()) { - const size_t num_players = - std::min(playerControllers.size(), static_cast(kNumPlayers)); - for (size_t i = 0; i < num_players; i++) { - if (playerControllers[i]) { - state->players[i].controller = std::move(playerControllers[i]); - } else { - state->players[i].controller = std::make_unique(); - } - } - // Fill remaining players with default controllers - for (size_t i = num_players; i < kNumPlayers; i++) { - state->players[i].controller = std::make_unique(); - } - } else { - // All players get default fake controllers - for (int i = 0; i < kNumPlayers; i++) { - state->players[i].controller = std::make_unique(); + for (size_t i = 0; i < kNumPlayers; i++) { + if (playerControllers[i]) { + state->controllers[i] = std::move(playerControllers[i]); + } else { + state->controllers[i] = std::make_unique(); } } diff --git a/tests/utils/gamestate_utils.h b/tests/utils/gamestate_utils.h index e4a212d3..7875aade 100644 --- a/tests/utils/gamestate_utils.h +++ b/tests/utils/gamestate_utils.h @@ -1,9 +1,9 @@ #pragma once #include -#include #include "controllers/playercontroller.h" #include "types/gamestate.h" +#include "types/settings.h" #include "types/statefunction.h" namespace mahjong { @@ -25,6 +25,7 @@ std::unique_ptr CreateTestGameState(uint64_t seed = 12345); // Advances through standard round initialization std::unique_ptr InitializeTestRound( uint64_t seed = 12345, - std::vector> playerControllers = {}); + std::array, kNumPlayers> + playerControllers = {}); } // namespace mahjong diff --git a/tests/utils/gamestate_utils.test.cc b/tests/utils/gamestate_utils.test.cc index 329603a1..e3f5dc53 100644 --- a/tests/utils/gamestate_utils.test.cc +++ b/tests/utils/gamestate_utils.test.cc @@ -44,14 +44,14 @@ TEST(GameStateUtils, InitializeTestRoundDefault) { // All players are filled with a controller for (int i = 0; i < kNumPlayers; i++) { - EXPECT_NE(state->players[i].controller, nullptr); + EXPECT_NE(state->controllers[i], nullptr); } // Draw occurred - EXPECT_EQ(state->hands[0].live.size(), 14); + EXPECT_EQ(state->hands[0].live_count, 14); for (int i = 1; i < kNumPlayers; i++) { - EXPECT_EQ(state->hands[i].live.size(), 13); + EXPECT_EQ(state->hands[i].live_count, 13); } } diff --git a/tests/utils/handformer.cc b/tests/utils/handformer.cc index ce26e932..77b9389d 100644 --- a/tests/utils/handformer.cc +++ b/tests/utils/handformer.cc @@ -10,6 +10,7 @@ #include #include +#include "types/hand.h" #include "types/pieces.h" #include "types/piecetype.h" @@ -92,6 +93,12 @@ std::vector HandFromNotation(const std::string& notation) { return result; } +void HandFromNotation(const std::string& notation, Hand* hand) { + std::vector hand_vec = HandFromNotation(notation); + hand->live_count = hand_vec.size(); + std::ranges::move(hand_vec, hand->live.begin()); +} + bool IsValidNotation(const std::string& notation) { std::vector current_set; for (const auto& c : notation) { diff --git a/tests/utils/handformer.h b/tests/utils/handformer.h index e865e126..6595f5bc 100644 --- a/tests/utils/handformer.h +++ b/tests/utils/handformer.h @@ -3,11 +3,13 @@ #include #include +#include "types/hand.h" #include "types/piecetype.h" namespace mahjong { std::vector HandFromNotation(const std::string& notation); +void HandFromNotation(const std::string& notation, Hand* hand); bool IsValidNotation(const std::string& notation); std::string HandToNotation(const std::vector& hand); diff --git a/tests/yakus/afterkan.test.cc b/tests/yakus/afterkan.test.cc index 1fe6e965..f045edba 100644 --- a/tests/yakus/afterkan.test.cc +++ b/tests/yakus/afterkan.test.cc @@ -16,15 +16,16 @@ namespace mahjong::yaku { TEST(isAfterAKan, 1Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m1111z999s55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m1111z999s55z", &hand); game_state.currentPlayer = 0; game_state.prevState = StateFunctionType::kReplacement; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAfterAKan(game_state, 0, branch)) { + if (isAfterAKan(game_state, hand, branch)) { SUCCEED(); return; } @@ -34,15 +35,16 @@ TEST(isAfterAKan, 1Han) { TEST(isAfterAKan, DoesntApply) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m1111z999s55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m1111z999s55z", &hand); game_state.currentPlayer = 0; game_state.prevState = StateFunctionType::kPon; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAfterAKan(game_state, 0, branch)) { + if (isAfterAKan(game_state, hand, branch)) { FAIL(); return; } @@ -52,15 +54,16 @@ TEST(isAfterAKan, DoesntApply) { TEST(isAfterAKan, WrongPlayer) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m1111z999s55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m1111z999s55z", &hand); game_state.currentPlayer = 2; game_state.prevState = StateFunctionType::kReplacement; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAfterAKan(game_state, 0, branch)) { + if (isAfterAKan(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/allpon.test.cc b/tests/yakus/allpon.test.cc index 40cd0392..257daa32 100644 --- a/tests/yakus/allpon.test.cc +++ b/tests/yakus/allpon.test.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -20,12 +19,13 @@ namespace mahjong::yaku { TEST(isAllPons, 2Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222p888s666z44m")); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222p888s666z44m", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllPons(game_state, 0, branch)) { + if (isAllPons(game_state, hand, branch)) { SUCCEED(); return; } @@ -34,20 +34,19 @@ TEST(isAllPons, 2Han) { } TEST(isAllPons, WithKans) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222p666z44m", &hand); + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kKan, .start = Piece(kFivePin), }; + hand.open = true; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222p666z44m")); - game_state.hands[0].melds = {meld}; - game_state.hands[0].open = true; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllPons(game_state, 0, branch)) { + if (isAllPons(game_state, hand, branch)) { SUCCEED(); return; } @@ -56,20 +55,19 @@ TEST(isAllPons, WithKans) { } TEST(isAllPons, ConcealedKan) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222p666z44m", &hand); + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kConcealedKan, .start = Piece(kFivePin), }; + hand.open = false; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222p666z44m")); - game_state.hands[0].melds = {meld}; - game_state.hands[0].open = false; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllPons(game_state, 0, branch)) { + if (isAllPons(game_state, hand, branch)) { SUCCEED(); return; } @@ -79,13 +77,14 @@ TEST(isAllPons, ConcealedKan) { TEST(isAllPons, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222p666z789m44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222p666z789m44m", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllPons(game_state, 0, branch)) { + if (isAllPons(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/allsimples.test.cc b/tests/yakus/allsimples.test.cc index 41d9d483..55ce3f1b 100644 --- a/tests/yakus/allsimples.test.cc +++ b/tests/yakus/allsimples.test.cc @@ -16,12 +16,13 @@ namespace mahjong::yaku { TEST(isAllSimples, 1Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("222m333p444s555p88m")); + Hand& hand = game_state.hands[0]; + HandFromNotation("222m333p444s555p88m", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllSimples(game_state, 0, branch)) { + if (isAllSimples(game_state, hand, branch)) { SUCCEED(); return; } @@ -31,12 +32,13 @@ TEST(isAllSimples, 1Han) { TEST(isAllSimples, BadHandHonors) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("222m333p444s111z88m")); + Hand& hand = game_state.hands[0]; + HandFromNotation("222m333p444s111z88m", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllSimples(game_state, 0, branch)) { + if (isAllSimples(game_state, hand, branch)) { FAIL(); return; } @@ -46,12 +48,13 @@ TEST(isAllSimples, BadHandHonors) { TEST(isAllSimples, BadHandTerminals) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("222m333p444s111m88m")); + Hand& hand = game_state.hands[0]; + HandFromNotation("222m333p444s111m88m", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllSimples(game_state, 0, branch)) { + if (isAllSimples(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/allterminalsandhonors.test.cc b/tests/yakus/allterminalsandhonors.test.cc index 30c4da92..01de9992 100644 --- a/tests/yakus/allterminalsandhonors.test.cc +++ b/tests/yakus/allterminalsandhonors.test.cc @@ -5,7 +5,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -21,13 +20,14 @@ namespace mahjong::yaku { TEST(isAllTerminalsAndHonors, 2Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("222z111p111m999s66z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("222z111p111m999s66z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllTerminalsAndHonors(game_state, 0, branch)) { + if (isAllTerminalsAndHonors(game_state, hand, branch)) { SUCCEED(); return; } @@ -37,13 +37,14 @@ TEST(isAllTerminalsAndHonors, 2Han) { TEST(isAllTerminalsAndHonors, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("222z111p111m888s66z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("222z111p111m888s66z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllTerminalsAndHonors(game_state, 0, branch)) { + if (isAllTerminalsAndHonors(game_state, hand, branch)) { FAIL(); return; } @@ -52,20 +53,19 @@ TEST(isAllTerminalsAndHonors, BadHand) { } TEST(isAllTerminalsAndHonors, CanBeOpen) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("222z111m999s66z", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kPon, .start = Piece(kNinePin), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("222z111m999s66z")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isAllTerminalsAndHonors(game_state, 0, branch)) { + if (isAllTerminalsAndHonors(game_state, hand, branch)) { SUCCEED(); return; } diff --git a/tests/yakus/blessingofman.test.cc b/tests/yakus/blessingofman.test.cc index 571eada4..d2348fe6 100644 --- a/tests/yakus/blessingofman.test.cc +++ b/tests/yakus/blessingofman.test.cc @@ -15,20 +15,22 @@ namespace mahjong::yaku { TEST(isBlessingOfMan, 5Han) { auto game_state = GameState(); - game_state.hands[3] = Hand(HandFromNotation("123m123p444m111z55m")); - game_state.hands[3].open = false; + Hand& hand = game_state.hands[3]; + HandFromNotation("123m123p444m111z55m", &hand); + hand.open = false; + hand.id = 3; game_state.turnNum = 1; // No calls have occured game_state.lastCall = -1; - game_state.hasRonned[3] = true; + hand.hasRonned = true; - auto root = breakdownHand(game_state.hands.at(3).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isBlessingOfMan(game_state, 3, branch)) { + if (isBlessingOfMan(game_state, hand, branch)) { SUCCEED(); return; } @@ -38,20 +40,21 @@ TEST(isBlessingOfMan, 5Han) { TEST(isBlessingOfMan, MustBeARon) { auto game_state = GameState(); - game_state.hands[3] = Hand(HandFromNotation("123m123p444m111z55m")); - game_state.hands[3].open = false; + Hand& hand = game_state.hands[3]; + HandFromNotation("123m123p444m111z55m", &hand); + hand.open = false; game_state.turnNum = 3; // No calls have occured game_state.lastCall = -1; - game_state.hasRonned[3] = false; + hand.hasRonned = false; - auto root = breakdownHand(game_state.hands.at(3).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isBlessingOfMan(game_state, 3, branch)) { + if (isBlessingOfMan(game_state, hand, branch)) { FAIL(); return; } @@ -61,20 +64,21 @@ TEST(isBlessingOfMan, MustBeARon) { TEST(isBlessingOfMan, MustBeBeforePlayerFirstTurn) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m123p444m111z55m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m123p444m111z55m", &hand); + hand.open = false; game_state.turnNum = 2; // No calls have occured game_state.lastCall = -1; - game_state.hasRonned[0] = true; + hand.hasRonned = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isBlessingOfMan(game_state, 0, branch)) { + if (isBlessingOfMan(game_state, hand, branch)) { FAIL(); return; } @@ -84,19 +88,20 @@ TEST(isBlessingOfMan, MustBeBeforePlayerFirstTurn) { TEST(isBlessingOfMan, NoCalledMustHaveOccured) { auto game_state = GameState(); - game_state.hands[3] = Hand(HandFromNotation("123m123p444m111z55m")); - game_state.hands[3].open = false; + Hand& hand = game_state.hands[3]; + HandFromNotation("123m123p444m111z55m", &hand); + hand.open = false; game_state.turnNum = 1; game_state.lastCall = 0; - game_state.hasRonned[3] = true; + hand.hasRonned = true; - auto root = breakdownHand(game_state.hands.at(3).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isBlessingOfMan(game_state, 3, branch)) { + if (isBlessingOfMan(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/concealedhand.test.cc b/tests/yakus/concealedhand.test.cc index f885fdfc..aefa022e 100644 --- a/tests/yakus/concealedhand.test.cc +++ b/tests/yakus/concealedhand.test.cc @@ -2,51 +2,54 @@ #include #include -#include #include "scoring/yakus/fullyconcealedhand.h" #include "types/gamestate.h" #include "types/hand.h" -#include "types/walls.h" +#include "types/settings.h" #include "utils/handformer.h" namespace mahjong::yaku { TEST(isFullyConcealedHand, 1Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.open = false; game_state.currentPlayer = 0; - EXPECT_TRUE(isFullyConcealedHand(game_state, 0)); + EXPECT_TRUE(isFullyConcealedHand(game_state, hand)); } TEST(isFullyConcealedHand, MustTsumo) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.open = false; game_state.currentPlayer = 1; - EXPECT_FALSE(isFullyConcealedHand(game_state, 0)); + EXPECT_FALSE(isFullyConcealedHand(game_state, hand)); } -TEST(isFullyConcealedHand, MustBeClosedHand) { +TEST(isFullyConcealedHand, MustBeClosedPlayer) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.open = true; game_state.currentPlayer = 0; - EXPECT_FALSE(isFullyConcealedHand(game_state, 0)); + EXPECT_FALSE(isFullyConcealedHand(game_state, hand)); } TEST(isFullyConcealedHand, MustHavePiecesRemainingInWall) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.open = false; game_state.currentPlayer = 0; // Empty the Wall - game_state.walls.livingWalls.clear(); + game_state.livingWallIndex = kLivingWallCount; - EXPECT_FALSE(isFullyConcealedHand(game_state, 0)); + EXPECT_FALSE(isFullyConcealedHand(game_state, hand)); } } // namespace mahjong::yaku diff --git a/tests/yakus/concealedpon.test.cc b/tests/yakus/concealedpon.test.cc index 4f13e3ee..9b5de2b1 100644 --- a/tests/yakus/concealedpon.test.cc +++ b/tests/yakus/concealedpon.test.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -12,20 +11,20 @@ #include "types/hand.h" #include "types/meld.h" #include "types/pieces.h" -#include "types/piecetype.h" #include "types/sets.h" #include "utils/handformer.h" namespace mahjong::yaku { TEST(isThreeConcealedPons, 2Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p111s666z44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p111s666z44m", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeConcealedPons(game_state, 0, branch)) { + if (isThreeConcealedPons(game_state, hand, branch)) { SUCCEED(); return; } @@ -34,19 +33,19 @@ TEST(isThreeConcealedPons, 2Han) { } TEST(isThreeConcealedPons, PonsMustBeConcealed) { - auto meld = Meld(); - meld.type = SetType::kChi; - meld.start = Piece(kTwoBamboo); - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p111s44m")); - game_state.hands[0].melds.push_back(meld); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p111s44m", &hand); + hand.melds[hand.meld_count++] = Meld{ + .type = SetType::kChi, + .start = kTwoBamboo, + }; + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeConcealedPons(game_state, 0, branch)) { + if (isThreeConcealedPons(game_state, hand, branch)) { SUCCEED(); return; } @@ -55,19 +54,19 @@ TEST(isThreeConcealedPons, PonsMustBeConcealed) { } TEST(isThreeConcealedPons, CanHaveAdditionalOpenPon) { - auto meld = Meld(); - meld.type = SetType::kPon; - meld.start = Piece(kTwoBamboo); - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p111s44m")); - game_state.hands[0].melds.push_back(meld); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p111s44m", &hand); + hand.melds[hand.meld_count++] = Meld{ + .type = SetType::kPon, + .start = kTwoBamboo, + }; + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeConcealedPons(game_state, 0, branch)) { + if (isThreeConcealedPons(game_state, hand, branch)) { SUCCEED(); return; } @@ -76,19 +75,19 @@ TEST(isThreeConcealedPons, CanHaveAdditionalOpenPon) { } TEST(isThreeConcealedPons, PonsMustBeConcealedNegative) { - auto meld = Meld(); - meld.type = SetType::kPon; - meld.start = Piece(kOneBamboo); - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p234s44m")); - game_state.hands[0].melds.push_back(meld); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p234s44m", &hand); + hand.melds[hand.meld_count++] = Meld{ + .type = SetType::kPon, + .start = kOneBamboo, + }; + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeConcealedPons(game_state, 0, branch)) { + if (isThreeConcealedPons(game_state, hand, branch)) { FAIL(); return; } @@ -98,13 +97,14 @@ TEST(isThreeConcealedPons, PonsMustBeConcealedNegative) { TEST(isThreeConcealedPons, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p234567s44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p234567s44m", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeConcealedPons(game_state, 0, branch)) { + if (isThreeConcealedPons(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/dragonpon.test.cc b/tests/yakus/dragonpon.test.cc index 9d95487b..d7f3a5d7 100644 --- a/tests/yakus/dragonpon.test.cc +++ b/tests/yakus/dragonpon.test.cc @@ -15,12 +15,13 @@ namespace mahjong::yaku { TEST(isWindOrDragonPon, WhiteDragon) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m555z11z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m555z11z", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isWhiteDragon(game_state, 0, branch)) { + if (isWhiteDragon(game_state, hand, branch)) { SUCCEED(); return; } @@ -30,12 +31,13 @@ TEST(isWindOrDragonPon, WhiteDragon) { TEST(isWindOrDragonPon, GreenDragon) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m666z11z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m666z11z", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isGreenDragon(game_state, 0, branch)) { + if (isGreenDragon(game_state, hand, branch)) { SUCCEED(); return; } @@ -45,12 +47,13 @@ TEST(isWindOrDragonPon, GreenDragon) { TEST(isWindOrDragonPon, RedDragon) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m777z11z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m777z11z", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isRedDragon(game_state, 0, branch)) { + if (isRedDragon(game_state, hand, branch)) { SUCCEED(); return; } @@ -60,12 +63,13 @@ TEST(isWindOrDragonPon, RedDragon) { TEST(isWindOrDragonPon, Kan) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m7777z11z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m7777z11z", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isRedDragon(game_state, 0, branch)) { + if (isRedDragon(game_state, hand, branch)) { SUCCEED(); return; } @@ -75,13 +79,14 @@ TEST(isWindOrDragonPon, Kan) { TEST(isWindOrDragonPon, CanWhenOpen) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m777z11z")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m777z11z", &hand); + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isRedDragon(game_state, 0, branch)) { + if (isRedDragon(game_state, hand, branch)) { SUCCEED(); return; } @@ -91,12 +96,13 @@ TEST(isWindOrDragonPon, CanWhenOpen) { TEST(isWindOrDragonPon, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m222p11p")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m222p11p", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isGreenDragon(game_state, 0, branch)) { + if (isGreenDragon(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/fullflush.test.cc b/tests/yakus/fullflush.test.cc index 2fc2f669..a1c4af47 100644 --- a/tests/yakus/fullflush.test.cc +++ b/tests/yakus/fullflush.test.cc @@ -5,7 +5,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -20,20 +19,19 @@ namespace mahjong::yaku { TEST(isFullFlush, 5Han) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222m345m99m", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kPon, .start = Piece(kSixCharacter), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222m345m99m")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isFullFlush(game_state, 0, branch)) { + if (isFullFlush(game_state, hand, branch)) { SUCCEED(); return; } @@ -43,13 +41,14 @@ TEST(isFullFlush, 5Han) { TEST(isFullFlush, 6Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222m345m666m99m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222m345m666m99m", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isFullFlush(game_state, 0, branch)) { + if (isFullFlush(game_state, hand, branch)) { SUCCEED(); return; } @@ -59,13 +58,14 @@ TEST(isFullFlush, 6Han) { TEST(isFullFlush, BadHandHonors) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222m111z666m99m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222m111z666m99m", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isFullFlush(game_state, 0, branch)) { + if (isFullFlush(game_state, hand, branch)) { FAIL(); return; } @@ -75,13 +75,14 @@ TEST(isFullFlush, BadHandHonors) { TEST(isFullFlush, BadHandSuit) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m222m111p666m99m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("111m222m111p666m99m", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isFullFlush(game_state, 0, branch)) { + if (isFullFlush(game_state, hand, branch)) { FAIL(); return; } @@ -91,13 +92,14 @@ TEST(isFullFlush, BadHandSuit) { TEST(isFullFlush, BadHandFullFlushHonors) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111z222z333z444z55z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("111z222z333z444z55z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isFullFlush(game_state, 0, branch)) { + if (isFullFlush(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/halfflush.test.cc b/tests/yakus/halfflush.test.cc index ad990f14..c3888240 100644 --- a/tests/yakus/halfflush.test.cc +++ b/tests/yakus/halfflush.test.cc @@ -5,7 +5,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -20,20 +19,19 @@ namespace mahjong::yaku { TEST(isHalfFlush, 2Han) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("11m234m111z222z", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kPon, .start = Piece(kSixCharacter), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("11m234m111z222z")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isHalfFlush(game_state, 0, branch)) { + if (isHalfFlush(game_state, hand, branch)) { SUCCEED(); return; } @@ -43,13 +41,14 @@ TEST(isHalfFlush, 2Han) { TEST(isHalfFlush, 3Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("11m234m777m111z222z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("11m234m777m111z222z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isHalfFlush(game_state, 0, branch)) { + if (isHalfFlush(game_state, hand, branch)) { SUCCEED(); return; } @@ -58,20 +57,19 @@ TEST(isHalfFlush, 3Han) { } TEST(isHalfFlush, BadHand) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("11m234m111p222z", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kPon, .start = Piece(kSixCharacter), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("11m234m111p222z")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isHalfFlush(game_state, 0, branch)) { + if (isHalfFlush(game_state, hand, branch)) { FAIL(); return; } @@ -80,20 +78,19 @@ TEST(isHalfFlush, BadHand) { } TEST(isHalfFlush, FullFlushIncompatible) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("11m234m888m777m", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kPon, .start = Piece(kSixCharacter), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("11m234m888m777m")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isHalfFlush(game_state, 0, branch)) { + if (isHalfFlush(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/littlethreedragons.test.cc b/tests/yakus/littlethreedragons.test.cc index f5814caa..38f9c661 100644 --- a/tests/yakus/littlethreedragons.test.cc +++ b/tests/yakus/littlethreedragons.test.cc @@ -5,7 +5,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -21,13 +20,14 @@ namespace mahjong::yaku { TEST(isLittleThreeDragons, 2Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555z666z111m222p77z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("555z666z111m222p77z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isLittleThreeDragons(game_state, 0, branch)) { + if (isLittleThreeDragons(game_state, hand, branch)) { SUCCEED(); return; } @@ -36,20 +36,19 @@ TEST(isLittleThreeDragons, 2Han) { } TEST(isLittleThreeDragons, WhenOpen) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("666z111m222p77z", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kPon, .start = Piece(kWhiteDragon), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("666z111m222p77z")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isLittleThreeDragons(game_state, 0, branch)) { + if (isLittleThreeDragons(game_state, hand, branch)) { SUCCEED(); return; } @@ -58,20 +57,19 @@ TEST(isLittleThreeDragons, WhenOpen) { } TEST(isLittleThreeDragons, BadHand) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("666z111m222p77s", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kPon, .start = Piece(kWhiteDragon), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("666z111m222p77s")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isLittleThreeDragons(game_state, 0, branch)) { + if (isLittleThreeDragons(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/mixedtriplechi.test.cc b/tests/yakus/mixedtriplechi.test.cc index 6211099d..86505c75 100644 --- a/tests/yakus/mixedtriplechi.test.cc +++ b/tests/yakus/mixedtriplechi.test.cc @@ -16,13 +16,14 @@ namespace mahjong::yaku { TEST(isMixedTripleChi, Open) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123p123m123s555p11z")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123p123m123s555p11z", &hand); + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isMixedTripleChi(game_state, 0, branch)) { + if (isMixedTripleChi(game_state, hand, branch)) { SUCCEED(); return; } @@ -32,13 +33,14 @@ TEST(isMixedTripleChi, Open) { TEST(isMixedTripleChi, Closed) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123p123m123s555p11z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123p123m123s555p11z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isMixedTripleChi(game_state, 0, branch)) { + if (isMixedTripleChi(game_state, hand, branch)) { SUCCEED(); return; } @@ -48,13 +50,14 @@ TEST(isMixedTripleChi, Closed) { TEST(isMixedTripleChi, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123p123p123s555p11z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123p123p123s555p11z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isMixedTripleChi(game_state, 0, branch)) { + if (isMixedTripleChi(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/outsidehand.test.cc b/tests/yakus/outsidehand.test.cc index ffc78bb1..11f1ccbc 100644 --- a/tests/yakus/outsidehand.test.cc +++ b/tests/yakus/outsidehand.test.cc @@ -16,13 +16,14 @@ namespace mahjong::yaku { TEST(isOutsideHand, Open) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m111z999s55z")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m111z999s55z", &hand); + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isOutsideHand(game_state, 0, branch)) { + if (isOutsideHand(game_state, hand, branch)) { SUCCEED(); return; } @@ -32,13 +33,14 @@ TEST(isOutsideHand, Open) { TEST(isOutsideHand, Closed) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m111z999s55z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m111z999s55z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isOutsideHand(game_state, 0, branch)) { + if (isOutsideHand(game_state, hand, branch)) { SUCCEED(); return; } @@ -48,12 +50,13 @@ TEST(isOutsideHand, Closed) { TEST(isOutsideHand, NoChi) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p111s999m66z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p111s999m66z", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isOutsideHand(game_state, 0, branch)) { + if (isOutsideHand(game_state, hand, branch)) { FAIL(); return; } @@ -63,12 +66,13 @@ TEST(isOutsideHand, NoChi) { TEST(isOutsideHand, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m234p111s999m66z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m234p111s999m66z", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isOutsideHand(game_state, 0, branch)) { + if (isOutsideHand(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/pinfu.test.cc b/tests/yakus/pinfu.test.cc index 9f8bbfd6..ca911901 100644 --- a/tests/yakus/pinfu.test.cc +++ b/tests/yakus/pinfu.test.cc @@ -18,14 +18,15 @@ namespace mahjong::yaku { TEST(isPinfu, 1Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456p234678s44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456p234678s44m", &hand); + hand.open = false; game_state.pendingPiece = Piece(kFourPin); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPinfu(game_state, 0, branch)) { + if (isPinfu(game_state, hand, branch)) { SUCCEED(); return; } @@ -35,14 +36,15 @@ TEST(isPinfu, 1Han) { TEST(isPinfu, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m555p234678s44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m555p234678s44m", &hand); + hand.open = false; game_state.pendingPiece = Piece(kTwoBamboo); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPinfu(game_state, 0, branch)) { + if (isPinfu(game_state, hand, branch)) { FAIL(); return; } @@ -52,14 +54,15 @@ TEST(isPinfu, BadHand) { TEST(isPinfu, CantBeOpen) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456p234678s44m")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456p234678s44m", &hand); + hand.open = true; game_state.pendingPiece = Piece(kFourPin); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPinfu(game_state, 0, branch)) { + if (isPinfu(game_state, hand, branch)) { FAIL(); return; } @@ -69,14 +72,15 @@ TEST(isPinfu, CantBeOpen) { TEST(isPinfu, NeedTwoWait) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456p234678s44m")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456p234678s44m", &hand); + hand.open = false; game_state.pendingPiece = Piece(kFivePin); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPinfu(game_state, 0, branch)) { + if (isPinfu(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/puredoublechi.test.cc b/tests/yakus/puredoublechi.test.cc index 14e3be84..123ad6f0 100644 --- a/tests/yakus/puredoublechi.test.cc +++ b/tests/yakus/puredoublechi.test.cc @@ -16,13 +16,14 @@ namespace mahjong::yaku { TEST(isPureDoubleChi, 1Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("234m234m555p888s88p")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("234m234m555p888s88p", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPureDoubleChi(game_state, 0, branch)) { + if (isPureDoubleChi(game_state, hand, branch)) { SUCCEED(); return; } @@ -32,13 +33,14 @@ TEST(isPureDoubleChi, 1Han) { TEST(isPureDoubleChi, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("234m123m555p888s88p")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("234m123m555p888s88p", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPureDoubleChi(game_state, 0, branch)) { + if (isPureDoubleChi(game_state, hand, branch)) { FAIL(); return; } @@ -48,13 +50,14 @@ TEST(isPureDoubleChi, BadHand) { TEST(isPureDoubleChi, MustBeConcealed) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("234m234m555p888s88p")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("234m234m555p888s88p", &hand); + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPureDoubleChi(game_state, 0, branch)) { + if (isPureDoubleChi(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/purestraight.test.cc b/tests/yakus/purestraight.test.cc index 231818be..696831ed 100644 --- a/tests/yakus/purestraight.test.cc +++ b/tests/yakus/purestraight.test.cc @@ -16,13 +16,14 @@ namespace mahjong::yaku { TEST(isPureStraight, Open) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m789m555p11z")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m789m555p11z", &hand); + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPureStraight(game_state, 0, branch)) { + if (isPureStraight(game_state, hand, branch)) { SUCCEED(); return; } @@ -32,13 +33,14 @@ TEST(isPureStraight, Open) { TEST(isPureStraight, Closed) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m789m555p11z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m789m555p11z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPureStraight(game_state, 0, branch)) { + if (isPureStraight(game_state, hand, branch)) { SUCCEED(); return; } @@ -48,13 +50,14 @@ TEST(isPureStraight, Closed) { TEST(isPureStraight, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m999m555p11z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m999m555p11z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPureStraight(game_state, 0, branch)) { + if (isPureStraight(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/riichi.test.cc b/tests/yakus/riichi.test.cc index 8297afc3..c33340e9 100644 --- a/tests/yakus/riichi.test.cc +++ b/tests/yakus/riichi.test.cc @@ -15,48 +15,52 @@ namespace mahjong::yaku { TEST(isRiichi, Riichi) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].riichi = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.riichi = true; - game_state.hands[0].riichiRound = 7; + hand.riichiRound = 7; game_state.turnNum = 15; game_state.lastCall = 2; - EXPECT_TRUE(isRiichi(game_state, 0)); + EXPECT_TRUE(isRiichi(game_state, hand)); } TEST(isRiichi, Ippatsu) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].riichi = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.riichi = true; - game_state.hands[0].riichiRound = 14; + hand.riichiRound = 14; game_state.turnNum = 15; game_state.lastCall = 2; - EXPECT_TRUE(isIppatsu(game_state, 0)); + EXPECT_TRUE(isIppatsu(game_state, hand)); } TEST(isRiichi, DoubleRiichi) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].riichi = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.riichi = true; - game_state.hands[0].riichiRound = 1; + hand.riichiRound = 1; game_state.turnNum = 2; game_state.lastCall = -1; - EXPECT_TRUE(isDoubleRiichi(game_state, 0)); + EXPECT_TRUE(isDoubleRiichi(game_state, hand)); } TEST(isRiichi, NoRiichi) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("555m555p555s111z44m")); - game_state.hands[0].riichi = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("555m555p555s111z44m", &hand); + hand.riichi = false; game_state.turnNum = 2; game_state.lastCall = -1; - EXPECT_FALSE(isRiichi(game_state, 0)); + EXPECT_FALSE(isRiichi(game_state, hand)); } } // namespace mahjong::yaku diff --git a/tests/yakus/robbingkan.test.cc b/tests/yakus/robbingkan.test.cc index 3e0949c7..f802baeb 100644 --- a/tests/yakus/robbingkan.test.cc +++ b/tests/yakus/robbingkan.test.cc @@ -16,14 +16,15 @@ namespace mahjong::yaku { TEST(isRobbingAKan, 1Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m1111z999s55z")); - game_state.hasRonned[0] = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m1111z999s55z", &hand); + game_state.hands[0].hasRonned = true; game_state.nextState = StateFunctionType::kKanDiscard; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isRobbingAKan(game_state, 0, branch)) { + if (isRobbingAKan(game_state, hand, branch)) { SUCCEED(); return; } @@ -33,14 +34,15 @@ TEST(isRobbingAKan, 1Han) { TEST(isRobbingAKan, DoesntApply) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m1111z999s55z")); - game_state.hasRonned[0] = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m1111z999s55z", &hand); + game_state.hands[0].hasRonned = true; game_state.nextState = StateFunctionType::kPon; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isRobbingAKan(game_state, 0, branch)) { + if (isRobbingAKan(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/sevenpairs.test.cc b/tests/yakus/sevenpairs.test.cc index 5c1c0dbc..f5f41633 100644 --- a/tests/yakus/sevenpairs.test.cc +++ b/tests/yakus/sevenpairs.test.cc @@ -16,13 +16,14 @@ namespace mahjong::yaku { TEST(isSevenPairs, 2Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("11m22p33s44z55m11z66z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("11m22p33s44z55m11z66z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSevenPairs(game_state, 0, branch)) { + if (isSevenPairs(game_state, hand, branch)) { SUCCEED(); return; } @@ -32,13 +33,14 @@ TEST(isSevenPairs, 2Han) { TEST(isSevenPairs, MustBeConcealed) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("11m22p33s44z55m11z66z")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("11m22p33s44z55m11z66z", &hand); + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSevenPairs(game_state, 0, branch)) { + if (isSevenPairs(game_state, hand, branch)) { FAIL(); return; } @@ -48,13 +50,14 @@ TEST(isSevenPairs, MustBeConcealed) { TEST(isSevenPairs, UniquePairsOnly) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("1111m22p33s44z11z66z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("1111m22p33s44z11z66z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSevenPairs(game_state, 0, branch)) { + if (isSevenPairs(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/terminalinsets.test.cc b/tests/yakus/terminalinsets.test.cc index bb354420..886e2454 100644 --- a/tests/yakus/terminalinsets.test.cc +++ b/tests/yakus/terminalinsets.test.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -19,20 +18,19 @@ namespace mahjong::yaku { TEST(isTerminalsInAllSets, 2Han) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m111p11s", &hand); + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kChi, .start = Piece(kSevenPin), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m111p11s")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTerminalsInAllSets(game_state, 0, branch)) { + if (isTerminalsInAllSets(game_state, hand, branch)) { SUCCEED(); return; } @@ -42,13 +40,14 @@ TEST(isTerminalsInAllSets, 2Han) { TEST(isTerminalsInAllSets, 3Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m111p789p11s")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m111p789p11s", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTerminalsInAllSets(game_state, 0, branch)) { + if (isTerminalsInAllSets(game_state, hand, branch)) { SUCCEED(); return; } @@ -57,20 +56,20 @@ TEST(isTerminalsInAllSets, 3Han) { } TEST(isTerminalsInAllSets, BadHand) { - const Meld meld = { + auto game_state = GameState(); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m222p11s", &hand); + hand.open = true; + hand.open = true; + hand.melds[hand.meld_count++] = Meld{ .type = SetType::kChi, .start = Piece(kSevenPin), }; - auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m222p11s")); - game_state.hands[0].open = true; - game_state.hands[0].melds = {meld}; - - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTerminalsInAllSets(game_state, 0, branch)) { + if (isTerminalsInAllSets(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/triplekan.test.cc b/tests/yakus/triplekan.test.cc index 2d7e5c24..d78ebd9b 100644 --- a/tests/yakus/triplekan.test.cc +++ b/tests/yakus/triplekan.test.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" @@ -33,14 +32,16 @@ TEST(isThreeKans, 2Han) { }; auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("666z22m")); - game_state.hands[0].melds = {meld_a, meld_b, meld_c}; - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("666z22m", &hand); + hand.melds = {meld_a, meld_b, meld_c}; + hand.meld_count = 3; + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeKans(game_state, 0, branch)) { + if (isThreeKans(game_state, hand, branch)) { SUCCEED(); return; } @@ -67,14 +68,16 @@ TEST(isThreeKans, WithOtherMelds) { }; auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("22m")); - game_state.hands[0].melds = {meld_a, meld_b, meld_c, meld_d}; - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("22m", &hand); + hand.melds = {meld_a, meld_b, meld_c, meld_d}; + hand.meld_count = 4; + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeKans(game_state, 0, branch)) { + if (isThreeKans(game_state, hand, branch)) { SUCCEED(); return; } @@ -93,14 +96,16 @@ TEST(isThreeKans, BadHand) { }; auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("777p666z22m")); - game_state.hands[0].melds = {meld_a, meld_b}; - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("777p666z22m", &hand); + hand.melds = {meld_a, meld_b}; + hand.meld_count = 2; + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeKans(game_state, 0, branch)) { + if (isThreeKans(game_state, hand, branch)) { FAIL(); return; } @@ -123,14 +128,16 @@ TEST(isThreeKans, AllConcealed) { }; auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("666z22m")); - game_state.hands[0].melds = {meld_a, meld_b, meld_c}; - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("666z22m", &hand); + hand.melds = {meld_a, meld_b, meld_c}; + hand.meld_count = 3; + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isThreeKans(game_state, 0, branch)) { + if (isThreeKans(game_state, hand, branch)) { SUCCEED(); return; } diff --git a/tests/yakus/triplepon.test.cc b/tests/yakus/triplepon.test.cc index 4c1d0805..b81d36f2 100644 --- a/tests/yakus/triplepon.test.cc +++ b/tests/yakus/triplepon.test.cc @@ -16,12 +16,13 @@ namespace mahjong::yaku { TEST(isTriplePon, 2Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p111s666z44m")); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p111s666z44m", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTriplePon(game_state, 0, branch)) { + if (isTriplePon(game_state, hand, branch)) { SUCCEED(); return; } @@ -31,12 +32,13 @@ TEST(isTriplePon, 2Han) { TEST(isTriplePon, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("111m111p222m666z44m")); + Hand& hand = game_state.hands[0]; + HandFromNotation("111m111p222m666z44m", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTriplePon(game_state, 0, branch)) { + if (isTriplePon(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/twicepuredoublechi.test.cc b/tests/yakus/twicepuredoublechi.test.cc index d8ab809a..0afdab77 100644 --- a/tests/yakus/twicepuredoublechi.test.cc +++ b/tests/yakus/twicepuredoublechi.test.cc @@ -16,13 +16,14 @@ namespace mahjong::yaku { TEST(isTwicePureDoubleChi, 3Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("789p789p234m234m11z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("789p789p234m234m11z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTwicePureDoubleChi(game_state, 0, branch)) { + if (isTwicePureDoubleChi(game_state, hand, branch)) { SUCCEED(); return; } @@ -32,13 +33,14 @@ TEST(isTwicePureDoubleChi, 3Han) { TEST(isTwicePureDoubleChi, MustBeConcealed) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("789p789p234m234m11z")); - game_state.hands[0].open = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("789p789p234m234m11z", &hand); + hand.open = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTwicePureDoubleChi(game_state, 0, branch)) { + if (isTwicePureDoubleChi(game_state, hand, branch)) { FAIL(); return; } @@ -48,13 +50,14 @@ TEST(isTwicePureDoubleChi, MustBeConcealed) { TEST(isTwicePureDoubleChi, BadHand) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("789p789p234m567m11z")); - game_state.hands[0].open = false; + Hand& hand = game_state.hands[0]; + HandFromNotation("789p789p234m567m11z", &hand); + hand.open = false; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isTwicePureDoubleChi(game_state, 0, branch)) { + if (isTwicePureDoubleChi(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/underthesea.test.cc b/tests/yakus/underthesea.test.cc index 74d3d979..db7b4c96 100644 --- a/tests/yakus/underthesea.test.cc +++ b/tests/yakus/underthesea.test.cc @@ -2,28 +2,27 @@ #include #include -#include -#include #include "analysis/analysis.h" #include "analysis/handnode.h" #include "scoring/yakus/bottomofthesea.h" #include "types/gamestate.h" #include "types/hand.h" -#include "types/walls.h" +#include "types/settings.h" #include "utils/handformer.h" namespace mahjong::yaku { TEST(isBottomOfTheSea, 1Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m111z999s55z")); - game_state.walls.livingWalls.clear(); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m111z999s55z", &hand); + game_state.livingWallIndex = kLivingWallCount; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isBottomOfTheSea(game_state, 0, branch)) { + if (isBottomOfTheSea(game_state, hand, branch)) { SUCCEED(); return; } @@ -33,14 +32,15 @@ TEST(isBottomOfTheSea, 1Han) { TEST(isBottomOfTheSea, 1HanRonned) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m111z999s55z")); - game_state.walls.livingWalls.clear(); - game_state.hasRonned[0] = true; + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m111z999s55z", &hand); + game_state.livingWallIndex = kLivingWallCount; + game_state.hands[0].hasRonned = true; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isBottomOfTheSea(game_state, 0, branch)) { + if (isBottomOfTheSea(game_state, hand, branch)) { SUCCEED(); return; } @@ -50,12 +50,13 @@ TEST(isBottomOfTheSea, 1HanRonned) { TEST(isBottomOfTheSea, DoesntApply) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m789m111z999s55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m789m111z999s55z", &hand); - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isBottomOfTheSea(game_state, 0, branch)) { + if (isBottomOfTheSea(game_state, hand, branch)) { FAIL(); return; } diff --git a/tests/yakus/windpons.test.cc b/tests/yakus/windpons.test.cc index f8c6946a..bf4000ea 100644 --- a/tests/yakus/windpons.test.cc +++ b/tests/yakus/windpons.test.cc @@ -15,15 +15,16 @@ namespace mahjong::yaku { TEST(isWindOrDragonPon, SeatWind) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m444z55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m444z55z", &hand); game_state.roundNum = 1; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); auto branches = Node::AsBranchVectors(root.get()); for (const auto& branch : branches) { - if (isSeatWind(game_state, 0, branch)) { + if (isSeatWind(game_state, hand, branch)) { SUCCEED(); return; } @@ -34,13 +35,14 @@ TEST(isWindOrDragonPon, SeatWind) { TEST(isWindOrDragonPon, SeatWindKan) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m4444z55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m4444z55z", &hand); game_state.roundNum = 1; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSeatWind(game_state, 0, branch)) { + if (isSeatWind(game_state, hand, branch)) { SUCCEED(); return; } @@ -50,13 +52,14 @@ TEST(isWindOrDragonPon, SeatWindKan) { TEST(isWindOrDragonPon, PrevalentWind) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m111z55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m111z55z", &hand); game_state.roundNum = 1; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPrevalentWind(game_state, 0, branch)) { + if (isPrevalentWind(game_state, hand, branch)) { SUCCEED(); return; } @@ -66,13 +69,14 @@ TEST(isWindOrDragonPon, PrevalentWind) { TEST(isWindOrDragonPon, PrevalentWindKan) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m1111z55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m1111z55z", &hand); game_state.roundNum = 1; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isPrevalentWind(game_state, 0, branch)) { + if (isPrevalentWind(game_state, hand, branch)) { SUCCEED(); return; } @@ -82,14 +86,15 @@ TEST(isWindOrDragonPon, PrevalentWindKan) { TEST(isWindOrDragonPon, Dealer2Han) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m111z55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m111z55z", &hand); game_state.roundNum = 0; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSeatWind(game_state, 0, branch) && - isPrevalentWind(game_state, 0, branch)) { + if (isSeatWind(game_state, hand, branch) && + isPrevalentWind(game_state, hand, branch)) { SUCCEED(); return; } @@ -99,14 +104,15 @@ TEST(isWindOrDragonPon, Dealer2Han) { TEST(isWindOrDragonPon, Dealer2HanKan) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m1111z55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m1111z55z", &hand); game_state.roundNum = 0; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSeatWind(game_state, 0, branch) && - isPrevalentWind(game_state, 0, branch)) { + if (isSeatWind(game_state, hand, branch) && + isPrevalentWind(game_state, hand, branch)) { SUCCEED(); return; } @@ -116,14 +122,15 @@ TEST(isWindOrDragonPon, Dealer2HanKan) { TEST(isWindOrDragonPon, NotSeatOrPrevalent) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m222z55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m222z55z", &hand); game_state.roundNum = 0; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSeatWind(game_state, 0, branch) || - isPrevalentWind(game_state, 0, branch)) { + if (isSeatWind(game_state, hand, branch) || + isPrevalentWind(game_state, hand, branch)) { FAIL(); return; } @@ -133,14 +140,15 @@ TEST(isWindOrDragonPon, NotSeatOrPrevalent) { TEST(isWindOrDragonPon, NoWind) { auto game_state = GameState(); - game_state.hands[0] = Hand(HandFromNotation("123m456m666m55z")); + Hand& hand = game_state.hands[0]; + HandFromNotation("123m456m666m55z", &hand); game_state.roundNum = 0; - auto root = breakdownHand(game_state.hands.at(0).live); + auto root = breakdownHand(hand.live_range()); for (const auto& branch : Node::AsBranchVectors(root.get())) { - if (isSeatWind(game_state, 0, branch) || - isPrevalentWind(game_state, 0, branch)) { + if (isSeatWind(game_state, hand, branch) || + isPrevalentWind(game_state, hand, branch)) { FAIL(); return; }