game_state: support clue starved variants
This commit is contained in:
parent
c7eac217a8
commit
930ba9b408
5 changed files with 27 additions and 19 deletions
|
@ -72,7 +72,7 @@ namespace Hanabi
|
|||
public:
|
||||
HanabiState() = default;
|
||||
|
||||
explicit HanabiState(const std::vector<Card> & deck, uint8_t score_goal = 5 * num_suits);
|
||||
explicit HanabiState(const std::vector<Card> & deck, uint8_t score_goal = 5 * num_suits, clue_t num_clues_gained_on_discard_or_stack_finished = 1);
|
||||
|
||||
void give_clue() final;
|
||||
|
||||
|
@ -219,6 +219,9 @@ namespace Hanabi
|
|||
static constexpr uint8_t no_endgame = std::numeric_limits<uint8_t>::max();
|
||||
|
||||
// Usual game state
|
||||
clue_t const _clues_gained_on_discard_or_stack_finished { 1 };
|
||||
uint8_t const _score_goal{};
|
||||
|
||||
player_t _turn{};
|
||||
clue_t _num_clues{};
|
||||
std::uint8_t _weighted_draw_pile_size{};
|
||||
|
@ -230,7 +233,6 @@ namespace Hanabi
|
|||
// further values of game state that are technically determined, but we update them anyway
|
||||
int8_t _pace{};
|
||||
uint8_t _score{};
|
||||
uint8_t _score_goal{};
|
||||
|
||||
// For reverting the current game
|
||||
std::stack<BacktrackAction> _actions_log;
|
||||
|
|
|
@ -65,10 +65,11 @@ namespace Hanabi
|
|||
}
|
||||
|
||||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||
HanabiState<num_suits, num_players, hand_size>::HanabiState(const std::vector<Card> & deck, uint8_t score_goal):
|
||||
_turn(0), _num_clues(max_num_clues), _weighted_draw_pile_size(deck.size()), _stacks(), _hands(), _draw_pile()
|
||||
HanabiState<num_suits, num_players, hand_size>::HanabiState(const std::vector<Card> & deck, uint8_t score_goal, clue_t num_clues_gained_on_discard_or_stack_finished):
|
||||
_clues_gained_on_discard_or_stack_finished(num_clues_gained_on_discard_or_stack_finished)
|
||||
, _score_goal(score_goal), _turn(0), _num_clues(max_num_clues), _weighted_draw_pile_size(deck.size()), _stacks(), _hands(), _draw_pile()
|
||||
, _endgame_turns_left(no_endgame), _pace(deck.size() - score_goal - num_players * (hand_size - 1)), _score(0)
|
||||
, _score_goal(score_goal), _actions_log(), _relative_representation(), _position_tablebase()
|
||||
, _actions_log(), _relative_representation(), _position_tablebase()
|
||||
, _enumerated_states(0)
|
||||
{
|
||||
std::ranges::fill(_stacks, starting_card_rank);
|
||||
|
@ -183,7 +184,7 @@ namespace Hanabi
|
|||
if (played_card.rank == 0 and _num_clues < max_num_clues)
|
||||
{
|
||||
// update clues if we played the last played_card of a stack
|
||||
_num_clues++;
|
||||
_num_clues += _clues_gained_on_discard_or_stack_finished;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +210,7 @@ namespace Hanabi
|
|||
ASSERT(_num_clues != max_num_clues);
|
||||
|
||||
const Card discarded_card = _hands[_turn][index];
|
||||
_num_clues++;
|
||||
_num_clues += _clues_gained_on_discard_or_stack_finished;
|
||||
_pace--;
|
||||
|
||||
unsigned long multiplicity = draw(index, cycle, false);
|
||||
|
@ -569,7 +570,7 @@ namespace Hanabi
|
|||
decr_turn();
|
||||
if (last_action.discarded.rank == 0 and not last_action.was_on_8_clues and not last_action.strike)
|
||||
{
|
||||
_num_clues--;
|
||||
_num_clues -= _clues_gained_on_discard_or_stack_finished;
|
||||
}
|
||||
revert_draw(last_action.index, last_action.discarded, cycle, !last_action.strike);
|
||||
if (not last_action.strike)
|
||||
|
@ -592,7 +593,7 @@ namespace Hanabi
|
|||
decr_turn();
|
||||
ASSERT(_num_clues > 0);
|
||||
|
||||
_num_clues--;
|
||||
_num_clues -= _clues_gained_on_discard_or_stack_finished;
|
||||
_pace++;
|
||||
|
||||
revert_draw(last_action.index, last_action.discarded, cycle, false);
|
||||
|
@ -650,8 +651,8 @@ namespace Hanabi
|
|||
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||
void HanabiState<num_suits, num_players, hand_size>::set_clues(Hanabi::clue_t clues)
|
||||
{
|
||||
ASSERT(0 <= clues);
|
||||
ASSERT(clues <= 8);
|
||||
ASSERT(clue_t(0) <= clues);
|
||||
ASSERT(clues <= clue_t(8));
|
||||
_num_clues = clues;
|
||||
}
|
||||
|
||||
|
@ -764,6 +765,7 @@ namespace Hanabi
|
|||
}
|
||||
}
|
||||
|
||||
// Check for discards
|
||||
if (_pace > 0 and _num_clues < max_num_clues)
|
||||
{
|
||||
for (std::uint8_t index = 0; index < hand_size; index++)
|
||||
|
@ -1036,8 +1038,10 @@ namespace Hanabi
|
|||
}
|
||||
|
||||
// encode number of clues
|
||||
id *= max_num_clues + 1;
|
||||
id += _num_clues;
|
||||
clue_t const scaled_clues = clue_t(2) * _num_clues;
|
||||
assert(scaled_clues.denominator() == 1);
|
||||
id *= (max_num_clues * clue_t(2)).numerator() + 1;
|
||||
id += scaled_clues.numerator();
|
||||
|
||||
// we can encode draw pile size and extra turn in one metric, since we only have extra turns if draw pile is empty
|
||||
const std::uint8_t draw_pile_size_and_extra_turns = [this]() -> uint8_t {
|
||||
|
@ -1101,7 +1105,9 @@ namespace Hanabi
|
|||
}
|
||||
|
||||
// encode number of clues
|
||||
ret.push_back(_num_clues);
|
||||
clue_t const scaled_clues = clue_t(2) * _num_clues;
|
||||
assert(scaled_clues.denominator() == 1);
|
||||
ret.push_back((clue_t(2) * _num_clues).numerator());
|
||||
|
||||
// we can encode draw pile size and extra turn in one metric, since we only have extra turns if draw pile is empty
|
||||
const std::uint8_t draw_pile_size_and_extra_turns = [this]() -> uint8_t {
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Hanabi
|
|||
|
||||
using rank_t = std::uint8_t;
|
||||
using suit_t = std::uint8_t;
|
||||
using clue_t = std::int8_t;
|
||||
using clue_t = boost::rational<uint16_t>;
|
||||
using player_t = std::uint8_t;
|
||||
using hand_index_t = std::uint8_t;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Hanabi
|
|||
// We want to do this sanity check here again,
|
||||
// so that the run_cli method itself can ensure that arguments are fully valid
|
||||
// and we cannot run into crashes due to bad specified parameters
|
||||
if (parms.recursive and std::holds_alternative<clue_t>(parms.clue_spec) and std::get<clue_t>(parms.clue_spec) != 0)
|
||||
if (parms.recursive and std::holds_alternative<clue_t>(parms.clue_spec) and std::get<clue_t>(parms.clue_spec) != clue_t(0))
|
||||
{
|
||||
throw std::logic_error("Cannot use nonzero clue modifier together with recursive evaluation mode.");
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ namespace Hanabi
|
|||
// When modifying the game state, we want to reset to the actual number of clues
|
||||
// to ensure that actions taken are legal.
|
||||
clue_t const original_num_clues = game.state->num_clues();
|
||||
for (clue_t num_clues = 0; num_clues <= 8; num_clues++)
|
||||
for (clue_t num_clues = 0; num_clues <= clue_t(8); num_clues++)
|
||||
{
|
||||
game.state->set_clues(num_clues);
|
||||
probability_t const result = game.state->evaluate_state();
|
||||
|
@ -304,7 +304,7 @@ namespace Hanabi
|
|||
parms.recursive = vm.count("recursive") > 0;
|
||||
parms.quiet = vm.count("quiet") > 0;
|
||||
|
||||
if (parms.recursive and std::holds_alternative<clue_t>(parms.clue_spec) and std::get<clue_t>(parms.clue_spec) != 0)
|
||||
if (parms.recursive and std::holds_alternative<clue_t>(parms.clue_spec) and std::get<clue_t>(parms.clue_spec) != clue_t(0))
|
||||
{
|
||||
std::cout << "Conflicting options --recursive and --clue-modifier" << std::endl;
|
||||
std::cout << "Use '--help' to print a help message." << std::endl;
|
||||
|
|
|
@ -386,7 +386,7 @@ namespace Hanabi
|
|||
|
||||
if (prompt.starts_with("clue"))
|
||||
{
|
||||
if (game.state->num_clues() == 0)
|
||||
if (game.state->num_clues() == clue_t(0))
|
||||
{
|
||||
std::cout << "You cannot give a clue at 0 clues." << std::endl;
|
||||
continue;
|
||||
|
|
Loading…
Reference in a new issue