intoduce CLI option to reduce memory consumption
This commit is contained in:
parent
bfc731ae36
commit
afb6fee540
9 changed files with 67 additions and 49 deletions
|
@ -73,6 +73,14 @@ namespace Hanabi
|
|||
*/
|
||||
bool list_actions{false};
|
||||
|
||||
/**
|
||||
* If set to true, only roughly half of the game states will be stored in the internal lookup table.
|
||||
* This results in roughly halving memory consumption at the cost of roughly a factor 3-5 in execution speed
|
||||
* (the slowdown is theoretically bounded to visiting at most 9 times the number of states if this option is activated).
|
||||
* You should typically never need this, unless you are very specifically short on memory and.
|
||||
* */
|
||||
bool save_memory{false};
|
||||
|
||||
/**
|
||||
* If true, prints version information of the program and exits immediately.
|
||||
*/
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Download
|
|||
* @return Game state
|
||||
*
|
||||
*/
|
||||
Hanabi::Game get_game(int game_id, std::optional<uint8_t> score_goal);
|
||||
Hanabi::Game get_game(int game_id, std::optional<uint8_t> score_goal, bool save_memory = false);
|
||||
|
||||
/**
|
||||
* @brief Create game object from given source.
|
||||
|
@ -32,7 +32,7 @@ namespace Download
|
|||
* @return Game state
|
||||
*
|
||||
*/
|
||||
Hanabi::Game get_game(std::string const & filename, std::optional<uint8_t> score_goal);
|
||||
Hanabi::Game get_game(std::string const & filename, std::optional<uint8_t> score_goal, bool save_memory = false);
|
||||
|
||||
} // namespace Download
|
||||
|
||||
|
|
|
@ -24,6 +24,21 @@ namespace Hanabi
|
|||
bool operator!=(const CardMultiplicity &) const;
|
||||
};
|
||||
|
||||
struct HanabiStateConfig
|
||||
{
|
||||
/** What score to consider as a win. If left empty, automatically replaced by max score on construction. */
|
||||
std::optional<uint8_t> score_goal;
|
||||
/** The number of clues gained when a stack is finished or a card is discarded. Usually 1, 1/2 in clue starved. */
|
||||
clue_t num_clues_gained_on_discard_or_stack_finished {1};
|
||||
/**
|
||||
* If set to true, only roughly half of the game states will be stored in the internal lookup table.
|
||||
* This results in roughly halving memory consumption at the cost of roughly a factor 3-5 in execution speed
|
||||
* (the slowdown is theoretically bounded to visiting at most 9 times the number of states if this option is activated).
|
||||
* You should typically never need this, unless you are very specifically short on memory and.
|
||||
* */
|
||||
bool save_memory {false};
|
||||
};
|
||||
|
||||
class HanabiStateIF
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -62,21 +62,6 @@ namespace Hanabi
|
|||
std::array<inner_array_t, num_suits> _array{};
|
||||
};
|
||||
|
||||
struct HanabiStateConfig
|
||||
{
|
||||
/** What score to consider as a win. If left empty, automatically replaced by max score on construction. */
|
||||
std::optional<uint8_t> score_goal;
|
||||
/** The number of clues gained when a stack is finished or a card is discarded. Usually 1, 1/2 in clue starved. */
|
||||
clue_t num_clues_gained_on_discard_or_stack_finished {1};
|
||||
/**
|
||||
* If set to true, only roughly half of the game states will be stored in the internal lookup table.
|
||||
* This results in roughly halving memory consumption at the cost of roughly a factor 3-5 in execution speed
|
||||
* (the slowdown is theoretically bounded to visiting at most 9 times the number of states if this option is activated).
|
||||
* You should typically never need this, unless you are very specifically short on memory and.
|
||||
* */
|
||||
bool save_memory {false};
|
||||
};
|
||||
|
||||
// A game mimics a game state together with a list of actions and allows to traverse the game
|
||||
// history by making and reverting the stored actions.
|
||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||
|
|
|
@ -1070,7 +1070,7 @@ namespace Hanabi
|
|||
if (discard_index == invalid_index) {
|
||||
for (std::uint8_t index = 0; index < hand_size; index++) {
|
||||
Card const card = _hands[_turn][index];
|
||||
auto it = std::find_if(_hands[_turn].begin() + index + 1, _hands[_turn].end(), [&card, this](Card const & card_in_hand) {
|
||||
auto it = std::find_if(_hands[_turn].begin() + index + 1, _hands[_turn].end(), [&card](Card const & card_in_hand) {
|
||||
return card_in_hand == card;
|
||||
});
|
||||
if (it != _hands[_turn].end()) {
|
||||
|
|
|
@ -26,8 +26,7 @@ namespace Hanabi
|
|||
std::size_t num_suits,
|
||||
Hanabi::player_t num_players,
|
||||
std::vector<Hanabi::Card> const &deck,
|
||||
clue_t num_clues_gained_on_discard_or_stack_finished = clue_t(1),
|
||||
std::optional<uint8_t> score_goal = std::nullopt
|
||||
Hanabi::HanabiStateConfig config
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,11 +66,11 @@ namespace Hanabi
|
|||
Game game = [&parms] {
|
||||
if (std::holds_alternative<int>(parms.game))
|
||||
{
|
||||
return Download::get_game(std::get<int>(parms.game), convert_optional(parms.score_goal));
|
||||
return Download::get_game(std::get<int>(parms.game), convert_optional(parms.score_goal), parms.save_memory);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Download::get_game(std::get<std::string>(parms.game), convert_optional(parms.score_goal));
|
||||
return Download::get_game(std::get<std::string>(parms.game), convert_optional(parms.score_goal), parms.save_memory);
|
||||
}
|
||||
}();
|
||||
|
||||
|
@ -257,6 +257,7 @@ namespace Hanabi
|
|||
("list-actions,l","List all actions (including suboptimal ones) of all turns after specified state.")
|
||||
("all-clues", "Whenever evaluating a game state, evaluate it with all clue counts and output their "
|
||||
"probabilities.")
|
||||
("save-memory", "Reduce memory consumption by roughly 50%. This results in roughly 5 times as much execution time, so use this only if you are really short on memory.")
|
||||
("quiet,q", "Deactivate all non-essential prints. Useful if output is parsed by another program.")
|
||||
("version,v", "Print version information and exit.");
|
||||
|
||||
|
@ -349,6 +350,7 @@ namespace Hanabi
|
|||
parms.recursive = vm.count("recursive") > 0;
|
||||
parms.list_actions = vm.count("list-actions") > 0;
|
||||
parms.quiet = vm.count("quiet") > 0;
|
||||
parms.save_memory = vm.count("save-memory") > 0;
|
||||
|
||||
if (parms.recursive and std::holds_alternative<clue_t>(parms.clue_spec) and std::get<clue_t>(parms.clue_spec) != clue_t(0))
|
||||
{
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Download
|
|||
return boost::json::parse(game_json).as_object();
|
||||
}
|
||||
|
||||
Hanabi::Game get_game(int game_id, std::optional<uint8_t> score_goal)
|
||||
Hanabi::Game get_game(int game_id, std::optional<uint8_t> score_goal, bool save_memory)
|
||||
{
|
||||
std::optional<boost::json::object> const game_json = download_game_json(game_id);
|
||||
if (!game_json.has_value() or game_json.value().empty())
|
||||
|
@ -42,10 +42,15 @@ namespace Download
|
|||
|
||||
Hanabi::GameInfo game_info = Parsing::parse_game(game_json.value());
|
||||
|
||||
return {make_game_state(game_info.num_suits, game_info.num_players, game_info.deck, game_info.num_clues_gained_per_discard_or_stack_finished, score_goal), game_info};
|
||||
Hanabi::HanabiStateConfig config;
|
||||
config.num_clues_gained_on_discard_or_stack_finished = game_info.num_clues_gained_per_discard_or_stack_finished;
|
||||
config.save_memory = save_memory;
|
||||
config.score_goal = score_goal;
|
||||
|
||||
return {make_game_state(game_info.num_suits, game_info.num_players, game_info.deck, config), game_info};
|
||||
}
|
||||
|
||||
Hanabi::Game get_game(std::string const & filename, std::optional<uint8_t> score_goal)
|
||||
Hanabi::Game get_game(std::string const & filename, std::optional<uint8_t> score_goal, bool save_memory)
|
||||
{
|
||||
std::optional<boost::json::object> const game_json = open_game_json(filename.c_str());
|
||||
if (!game_json.has_value() or game_json.value().empty())
|
||||
|
@ -54,6 +59,12 @@ namespace Download
|
|||
}
|
||||
|
||||
Hanabi::GameInfo game_info = Parsing::parse_game(game_json.value());
|
||||
return {make_game_state(game_info.num_suits, game_info.num_players, game_info.deck, game_info.num_clues_gained_per_discard_or_stack_finished, score_goal), game_info};
|
||||
|
||||
Hanabi::HanabiStateConfig config;
|
||||
config.num_clues_gained_on_discard_or_stack_finished = game_info.num_clues_gained_per_discard_or_stack_finished;
|
||||
config.save_memory = save_memory;
|
||||
config.score_goal = score_goal;
|
||||
|
||||
return {make_game_state(game_info.num_suits, game_info.num_players, game_info.deck, config), game_info};
|
||||
}
|
||||
} // namespace Download
|
||||
|
|
|
@ -5,24 +5,22 @@ namespace Hanabi
|
|||
{
|
||||
std::unique_ptr<Hanabi::HanabiStateIF> make_game_state(
|
||||
std::size_t num_suits, Hanabi::player_t num_players, std::vector<Hanabi::Card> const & deck,
|
||||
clue_t num_clues_gained_on_discard_or_stack_finished, std::optional<
|
||||
uint8_t> score_goal
|
||||
Hanabi::HanabiStateConfig config
|
||||
)
|
||||
{
|
||||
uint8_t actual_score_goal = score_goal.value_or(5 * num_suits);
|
||||
switch (num_players)
|
||||
{
|
||||
case 2:
|
||||
switch (num_suits)
|
||||
{
|
||||
case 3:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 2, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 2, 5>(deck, config));
|
||||
case 4:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 2, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 2, 5>(deck, config));
|
||||
case 5:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 2, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 2, 5>(deck, config));
|
||||
case 6:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 2, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 2, 5>(deck, config));
|
||||
default:
|
||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||
}
|
||||
|
@ -30,13 +28,13 @@ namespace Hanabi
|
|||
switch (num_suits)
|
||||
{
|
||||
case 3:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 3, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 3, 5>(deck, config));
|
||||
case 4:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 3, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 3, 5>(deck, config));
|
||||
case 5:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 3, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 3, 5>(deck, config));
|
||||
case 6:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 3, 5>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 3, 5>(deck, config));
|
||||
default:
|
||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||
}
|
||||
|
@ -44,13 +42,13 @@ namespace Hanabi
|
|||
switch (num_suits)
|
||||
{
|
||||
case 3:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 4, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 4, 4>(deck, config));
|
||||
case 4:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 4, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 4, 4>(deck, config));
|
||||
case 5:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 4, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 4, 4>(deck, config));
|
||||
case 6:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 4, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 4, 4>(deck, config));
|
||||
default:
|
||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||
}
|
||||
|
@ -58,13 +56,13 @@ namespace Hanabi
|
|||
switch (num_suits)
|
||||
{
|
||||
case 3:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 5, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 5, 4>(deck, config));
|
||||
case 4:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 5, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 5, 4>(deck, config));
|
||||
case 5:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 5, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 5, 4>(deck, config));
|
||||
case 6:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 5, 4>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 5, 4>(deck, config));
|
||||
default:
|
||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||
}
|
||||
|
@ -72,13 +70,13 @@ namespace Hanabi
|
|||
switch (num_suits)
|
||||
{
|
||||
case 3:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 6, 3>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<3, 6, 3>(deck, config));
|
||||
case 4:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 6, 3>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<4, 6, 3>(deck, config));
|
||||
case 5:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 6, 3>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<5, 6, 3>(deck, config));
|
||||
case 6:
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 6, 3>(deck, actual_score_goal, num_clues_gained_on_discard_or_stack_finished));
|
||||
return std::unique_ptr<Hanabi::HanabiStateIF>(new Hanabi::HanabiState<6, 6, 3>(deck, config));
|
||||
default:
|
||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue