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};
|
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.
|
* If true, prints version information of the program and exits immediately.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace Download
|
||||||
* @return Game state
|
* @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.
|
* @brief Create game object from given source.
|
||||||
|
@ -32,7 +32,7 @@ namespace Download
|
||||||
* @return Game state
|
* @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
|
} // namespace Download
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,21 @@ namespace Hanabi
|
||||||
bool operator!=(const CardMultiplicity &) const;
|
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
|
class HanabiStateIF
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -62,21 +62,6 @@ namespace Hanabi
|
||||||
std::array<inner_array_t, num_suits> _array{};
|
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
|
// 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.
|
// history by making and reverting the stored actions.
|
||||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
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) {
|
if (discard_index == invalid_index) {
|
||||||
for (std::uint8_t index = 0; index < hand_size; index++) {
|
for (std::uint8_t index = 0; index < hand_size; index++) {
|
||||||
Card const card = _hands[_turn][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;
|
return card_in_hand == card;
|
||||||
});
|
});
|
||||||
if (it != _hands[_turn].end()) {
|
if (it != _hands[_turn].end()) {
|
||||||
|
|
|
@ -26,8 +26,7 @@ namespace Hanabi
|
||||||
std::size_t num_suits,
|
std::size_t num_suits,
|
||||||
Hanabi::player_t num_players,
|
Hanabi::player_t num_players,
|
||||||
std::vector<Hanabi::Card> const &deck,
|
std::vector<Hanabi::Card> const &deck,
|
||||||
clue_t num_clues_gained_on_discard_or_stack_finished = clue_t(1),
|
Hanabi::HanabiStateConfig config
|
||||||
std::optional<uint8_t> score_goal = std::nullopt
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,11 +66,11 @@ namespace Hanabi
|
||||||
Game game = [&parms] {
|
Game game = [&parms] {
|
||||||
if (std::holds_alternative<int>(parms.game))
|
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
|
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.")
|
("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 "
|
("all-clues", "Whenever evaluating a game state, evaluate it with all clue counts and output their "
|
||||||
"probabilities.")
|
"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.")
|
("quiet,q", "Deactivate all non-essential prints. Useful if output is parsed by another program.")
|
||||||
("version,v", "Print version information and exit.");
|
("version,v", "Print version information and exit.");
|
||||||
|
|
||||||
|
@ -349,6 +350,7 @@ namespace Hanabi
|
||||||
parms.recursive = vm.count("recursive") > 0;
|
parms.recursive = vm.count("recursive") > 0;
|
||||||
parms.list_actions = vm.count("list-actions") > 0;
|
parms.list_actions = vm.count("list-actions") > 0;
|
||||||
parms.quiet = vm.count("quiet") > 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))
|
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();
|
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);
|
std::optional<boost::json::object> const game_json = download_game_json(game_id);
|
||||||
if (!game_json.has_value() or game_json.value().empty())
|
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());
|
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());
|
std::optional<boost::json::object> const game_json = open_game_json(filename.c_str());
|
||||||
if (!game_json.has_value() or game_json.value().empty())
|
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());
|
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
|
} // namespace Download
|
||||||
|
|
|
@ -5,24 +5,22 @@ namespace Hanabi
|
||||||
{
|
{
|
||||||
std::unique_ptr<Hanabi::HanabiStateIF> make_game_state(
|
std::unique_ptr<Hanabi::HanabiStateIF> make_game_state(
|
||||||
std::size_t num_suits, Hanabi::player_t num_players, std::vector<Hanabi::Card> const & deck,
|
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<
|
Hanabi::HanabiStateConfig config
|
||||||
uint8_t> score_goal
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint8_t actual_score_goal = score_goal.value_or(5 * num_suits);
|
|
||||||
switch (num_players)
|
switch (num_players)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
switch (num_suits)
|
switch (num_suits)
|
||||||
{
|
{
|
||||||
case 3:
|
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:
|
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:
|
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:
|
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:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
|
@ -30,13 +28,13 @@ namespace Hanabi
|
||||||
switch (num_suits)
|
switch (num_suits)
|
||||||
{
|
{
|
||||||
case 3:
|
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:
|
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:
|
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:
|
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:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
|
@ -44,13 +42,13 @@ namespace Hanabi
|
||||||
switch (num_suits)
|
switch (num_suits)
|
||||||
{
|
{
|
||||||
case 3:
|
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:
|
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:
|
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:
|
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:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
|
@ -58,13 +56,13 @@ namespace Hanabi
|
||||||
switch (num_suits)
|
switch (num_suits)
|
||||||
{
|
{
|
||||||
case 3:
|
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:
|
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:
|
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:
|
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:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
|
@ -72,13 +70,13 @@ namespace Hanabi
|
||||||
switch (num_suits)
|
switch (num_suits)
|
||||||
{
|
{
|
||||||
case 3:
|
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:
|
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:
|
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:
|
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:
|
default:
|
||||||
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
throw std::runtime_error("Invalid number of suits: " + std::to_string(num_suits));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue