improve cli interface: easier handling, more input validation
This commit is contained in:
parent
a8818418e9
commit
74fe5513da
4 changed files with 192 additions and 64 deletions
|
@ -29,20 +29,23 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
Card parse_card(std::string card_str) {
|
Card parse_card(std::string card_str) {
|
||||||
|
if (card_str == "trash") {
|
||||||
|
return Cards::trash;
|
||||||
|
}
|
||||||
if(card_str.size() != 2) {
|
if(card_str.size() != 2) {
|
||||||
return unknown_card;
|
return Cards::unknown;
|
||||||
}
|
}
|
||||||
constexpr std::array<char, 6> color_initials = {'r', 'y', 'g', 'b', 'p', 't'};
|
constexpr std::array<char, 6> color_initials = {'r', 'y', 'g', 'b', 'p', 't'};
|
||||||
auto it = std::find(color_initials.begin(), color_initials.end(), card_str[0]);
|
auto it = std::find(color_initials.begin(), color_initials.end(), card_str[0]);
|
||||||
if (it == color_initials.end()) {
|
if (it == color_initials.end()) {
|
||||||
return unknown_card;
|
return Cards::unknown;
|
||||||
}
|
}
|
||||||
const suit_t suit = std::distance(color_initials.begin(), it);
|
const suit_t suit = std::distance(color_initials.begin(), it);
|
||||||
try {
|
try {
|
||||||
const rank_t rank = 5 - std::stoi(card_str.substr(1, 1));
|
const rank_t rank = 5 - std::stoi(card_str.substr(1, 1));
|
||||||
return Card {suit, rank};
|
return Card {suit, rank};
|
||||||
} catch(std::invalid_argument&) {
|
} catch(std::invalid_argument&) {
|
||||||
return unknown_card;
|
return Cards::unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,27 +55,51 @@ namespace Hanabi {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
std::cout << "Choose drawn card: " << std::endl;
|
std::cout << "Choose drawn card: " << std::endl;
|
||||||
|
unsigned num_trash = 0;
|
||||||
for(const auto &[card_multiplicity, probability]: next_states) {
|
for(const auto &[card_multiplicity, probability]: next_states) {
|
||||||
if (game->is_trash(card_multiplicity.card)) {
|
if (game->is_trash(card_multiplicity.card)) {
|
||||||
std::cout << "kt " << std::endl;
|
num_trash += card_multiplicity.multiplicity;
|
||||||
} else {
|
} else {
|
||||||
std::cout << card_multiplicity.card << " ";
|
std::cout << card_multiplicity.card << " (" << card_multiplicity.multiplicity;
|
||||||
|
std::cout << " copie(s) in draw) " << probability << std::endl;
|
||||||
}
|
}
|
||||||
std::cout << "(" << card_multiplicity.multiplicity << " copie(s) in draw) " << probability << std::endl;
|
|
||||||
}
|
}
|
||||||
const std::string card_str = read_line_memory_safe("draw? ");
|
if (num_trash > 0) {
|
||||||
const Card drawn_card = parse_card(card_str);
|
std::cout << Cards::trash << " (" << num_trash << " copie(s) in draw" << std::endl;
|
||||||
if (drawn_card == unknown_card) {
|
}
|
||||||
|
|
||||||
|
std::stringstream prompt;
|
||||||
|
prompt << "draw? [";
|
||||||
|
if (num_trash > 0) {
|
||||||
|
prompt << Cards::trash;
|
||||||
|
} else {
|
||||||
|
prompt << next_states.front().first.card;
|
||||||
|
}
|
||||||
|
prompt << "] ";
|
||||||
|
const std::string card_str = read_line_memory_safe(prompt.str().c_str());
|
||||||
|
const Card drawn_card = [&card_str, &num_trash, &next_states](){
|
||||||
|
if (card_str.empty()) {
|
||||||
|
if (num_trash > 0) {
|
||||||
|
return Cards::trash;
|
||||||
|
}
|
||||||
|
return next_states.front().first.card;
|
||||||
|
}
|
||||||
|
return parse_card(card_str);
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (drawn_card == Cards::unknown) {
|
||||||
std::cout << "Could not parse card " << card_str << std::endl;
|
std::cout << "Could not parse card " << card_str << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::find_if(next_states.begin(), next_states.end(), [&drawn_card](const std::pair<CardMultiplicity, std::optional<probability_t>>& pair) {
|
auto selected_draw_it = std::find_if(next_states.begin(), next_states.end(), [&drawn_card, &game](const std::pair<CardMultiplicity, std::optional<probability_t>>& pair) {
|
||||||
return pair.first.card.suit == drawn_card.suit and pair.first.card.rank == drawn_card.rank;
|
return (game->is_trash(pair.first.card) and drawn_card == Cards::trash) or pair.first.card == drawn_card;
|
||||||
}) == next_states.end()) {
|
});
|
||||||
std::cout << "That card is not in the draw pile" << std::endl;
|
if (selected_draw_it == next_states.end()){
|
||||||
|
std::cout << "That card is not in the draw pile, aborting." << std::endl;
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
game->rotate_next_draw(drawn_card);
|
game->rotate_next_draw(selected_draw_it->first.card);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,12 +108,14 @@ namespace Hanabi {
|
||||||
const std::string prompt = read_line_memory_safe("> ");
|
const std::string prompt = read_line_memory_safe("> ");
|
||||||
|
|
||||||
if (prompt.starts_with("help")) {
|
if (prompt.starts_with("help")) {
|
||||||
std::cout << "state: print information on current game state" << std::endl;
|
std::cout << "state: print information on current game state." << std::endl;
|
||||||
std::cout << "clue: give a clue" << std::endl;
|
std::cout << "clue: give a clue." << std::endl;
|
||||||
std::cout << "play <card>: play specified card" << std::endl;
|
std::cout << "play <card>: play specified card." << std::endl;
|
||||||
std::cout << "discard <card>: discard specified card" << std::endl;
|
std::cout << "discard: discard trash from hand." << std::endl;
|
||||||
std::cout << "revert: revert last turn of game" << std::endl;
|
std::cout << "revert: revert last turn of game." << std::endl;
|
||||||
std::cout << "actions: display list of reasonable actions to take and their winning chances" << std::endl;
|
std::cout << "actions: display list of reasonable actions to take and their winning chances." << std::endl;
|
||||||
|
std::cout << "id: display id of state. Has no inherent meaning, useful for debugging." << std::endl;
|
||||||
|
std::cout << "evaluate: evaluate current game state recursively. Potentially runtime-expensive." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,15 +126,29 @@ namespace Hanabi {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prompt.starts_with("evaluate")) {
|
||||||
|
std::cout << "Evaluating current game state, this might take a while." << std::endl;
|
||||||
|
game->evaluate_state();
|
||||||
|
std::cout << "Evaluated state." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("revert")) {
|
if (prompt.starts_with("revert")) {
|
||||||
std::cout << "Reverting one turn" << std::endl;
|
std::cout << "Reverting one turn" << std::endl;
|
||||||
game->revert();
|
game->revert();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prompt.starts_with("id")) {
|
||||||
|
std::cout << game->unique_id() << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("play")) {
|
if (prompt.starts_with("play")) {
|
||||||
const Card card = parse_card(prompt.substr(5,2));
|
const Card card = parse_card(prompt.substr(5,2));
|
||||||
if (card == unknown_card) {
|
if (prompt.length() < 7) {
|
||||||
|
std::cout << "No card specified." << std::endl;
|
||||||
|
}
|
||||||
|
if (card == Cards::unknown) {
|
||||||
std::cout << "Could not parse card " << prompt.substr(5,2) << std::endl;
|
std::cout << "Could not parse card " << prompt.substr(5,2) << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -121,24 +164,31 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("discard")) {
|
if (prompt.starts_with("discard")) {
|
||||||
const Card card = parse_card(prompt.substr(8,2));
|
const auto hand = game->cur_hand();
|
||||||
if (card == unknown_card) {
|
hand_index_t trash_index = invalid_hand_idx;
|
||||||
std::cout << "Could not parse card " << prompt.substr(5,2) << std::endl;
|
for(hand_index_t index = 0; index < hand.size(); index++) {
|
||||||
|
if (game->is_trash(hand[index])) {
|
||||||
|
trash_index = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trash_index == invalid_hand_idx) {
|
||||||
|
std::cout << "No trash in hand found, discarding not supported." << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const hand_index_t index = game->find_card_in_hand(card);
|
if (game->num_clues() == max_num_clues) {
|
||||||
if (index == hand_index_t(-1)) {
|
std::cout << "You cannot discard at " << max_num_clues << " clues." << std::endl;
|
||||||
std::cout << "This card is not in the current players hand, aborting." << std::endl;
|
|
||||||
}
|
|
||||||
if (!ask_for_card_and_rotate_draw(game, index, false)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
game->discard(index);
|
if (!ask_for_card_and_rotate_draw(game, trash_index, false)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
game->discard(trash_index);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("clue")) {
|
if (prompt.starts_with("clue")) {
|
||||||
game->clue();
|
game->give_clue();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,7 @@ namespace Download {
|
||||||
switch(actions[i].type) {
|
switch(actions[i].type) {
|
||||||
case Hanabi::ActionType::color_clue:
|
case Hanabi::ActionType::color_clue:
|
||||||
case Hanabi::ActionType::rank_clue:
|
case Hanabi::ActionType::rank_clue:
|
||||||
game->clue();
|
game->give_clue();
|
||||||
break;
|
break;
|
||||||
case Hanabi::ActionType::discard:
|
case Hanabi::ActionType::discard:
|
||||||
index = game->find_card_in_hand(deck[actions[i].target]);
|
index = game->find_card_in_hand(deck[actions[i].target]);
|
||||||
|
|
91
game_state.h
91
game_state.h
|
@ -39,8 +39,11 @@ namespace Hanabi {
|
||||||
constexpr size_t max_card_duplicity = 3;
|
constexpr size_t max_card_duplicity = 3;
|
||||||
constexpr clue_t max_num_clues = 8;
|
constexpr clue_t max_num_clues = 8;
|
||||||
constexpr uint8_t not_in_starting_hand = std::numeric_limits<uint8_t>::max();
|
constexpr uint8_t not_in_starting_hand = std::numeric_limits<uint8_t>::max();
|
||||||
|
constexpr hand_index_t invalid_hand_idx = std::numeric_limits<hand_index_t>::max();
|
||||||
|
|
||||||
constexpr std::array<std::string, 6> suit_initials{"r", "y", "g", "b", "p", "t"};
|
// We might want to change these at runtime to adapt to other variants.
|
||||||
|
// However, a global variable is used so that we can have an output operator for cards reading from here
|
||||||
|
static std::array<char, 6> suit_initials = {'r', 'y', 'g', 'b', 'p', 't'};
|
||||||
|
|
||||||
struct Card {
|
struct Card {
|
||||||
suit_t suit;
|
suit_t suit;
|
||||||
|
@ -53,7 +56,48 @@ namespace Hanabi {
|
||||||
|
|
||||||
inline const Card operator++(int);
|
inline const Card operator++(int);
|
||||||
|
|
||||||
auto operator<=>(const Card &) const = default;
|
inline bool operator==(const Card &other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Cards {
|
||||||
|
static constexpr Card r0 = {0, 5};
|
||||||
|
static constexpr Card r1 = {0, 4};
|
||||||
|
static constexpr Card r2 = {0, 3};
|
||||||
|
static constexpr Card r3 = {0, 2};
|
||||||
|
static constexpr Card r4 = {0, 1};
|
||||||
|
static constexpr Card r5 = {0, 0};
|
||||||
|
static constexpr Card y0 = {1, 5};
|
||||||
|
static constexpr Card y1 = {1, 4};
|
||||||
|
static constexpr Card y2 = {1, 3};
|
||||||
|
static constexpr Card y3 = {1, 2};
|
||||||
|
static constexpr Card y4 = {1, 1};
|
||||||
|
static constexpr Card y5 = {1, 0};
|
||||||
|
static constexpr Card g0 = {2, 5};
|
||||||
|
static constexpr Card g1 = {2, 4};
|
||||||
|
static constexpr Card g2 = {2, 3};
|
||||||
|
static constexpr Card g3 = {2, 2};
|
||||||
|
static constexpr Card g4 = {2, 1};
|
||||||
|
static constexpr Card g5 = {2, 0};
|
||||||
|
static constexpr Card b0 = {3, 5};
|
||||||
|
static constexpr Card b1 = {3, 4};
|
||||||
|
static constexpr Card b2 = {3, 3};
|
||||||
|
static constexpr Card b3 = {3, 2};
|
||||||
|
static constexpr Card b4 = {3, 1};
|
||||||
|
static constexpr Card b5 = {3, 0};
|
||||||
|
static constexpr Card p0 = {4, 5};
|
||||||
|
static constexpr Card p1 = {4, 4};
|
||||||
|
static constexpr Card p2 = {4, 3};
|
||||||
|
static constexpr Card p3 = {4, 2};
|
||||||
|
static constexpr Card p4 = {4, 1};
|
||||||
|
static constexpr Card p5 = {4, 0};
|
||||||
|
static constexpr Card t0 = {5, 5};
|
||||||
|
static constexpr Card t1 = {5, 4};
|
||||||
|
static constexpr Card t2 = {5, 3};
|
||||||
|
static constexpr Card t3 = {5, 2};
|
||||||
|
static constexpr Card t4 = {5, 1};
|
||||||
|
static constexpr Card t5 = {5, 0};
|
||||||
|
static constexpr Card unknown = {std::numeric_limits<suit_t>::max(), 0};
|
||||||
|
static constexpr Card trash = {std::numeric_limits<suit_t>::max(), 1};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,19 +114,6 @@ namespace Hanabi {
|
||||||
|
|
||||||
inline std::ostream &operator<<(std::ostream &os, const Card &card);
|
inline std::ostream &operator<<(std::ostream &os, const Card &card);
|
||||||
|
|
||||||
constexpr Card r0 = {0, 5};
|
|
||||||
constexpr Card r1 = {0, 4};
|
|
||||||
constexpr Card r2 = {0, 3};
|
|
||||||
constexpr Card r3 = {0, 2};
|
|
||||||
constexpr Card r4 = {0, 1};
|
|
||||||
constexpr Card r5 = {0, 0};
|
|
||||||
constexpr Card y0 = {1, 5};
|
|
||||||
constexpr Card y1 = {1, 4};
|
|
||||||
constexpr Card y2 = {1, 3};
|
|
||||||
constexpr Card y3 = {1, 2};
|
|
||||||
constexpr Card y4 = {1, 1};
|
|
||||||
constexpr Card y5 = {1, 0};
|
|
||||||
constexpr Card unknown_card = {0, 6};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To store:
|
* To store:
|
||||||
|
@ -162,19 +193,23 @@ inline std::ostream& operator<<(std::ostream& os, const Action& action);
|
||||||
|
|
||||||
class HanabiStateIF {
|
class HanabiStateIF {
|
||||||
public:
|
public:
|
||||||
virtual void clue() = 0;
|
virtual void give_clue() = 0;
|
||||||
virtual void discard(hand_index_t index) = 0;
|
virtual void discard(hand_index_t index) = 0;
|
||||||
virtual void play(hand_index_t index) = 0;
|
virtual void play(hand_index_t index) = 0;
|
||||||
|
|
||||||
virtual void rotate_next_draw(const Card& card) = 0;
|
virtual void rotate_next_draw(const Card& card) = 0;
|
||||||
|
|
||||||
virtual void revert() = 0;
|
virtual void revert() = 0;
|
||||||
|
|
||||||
[[nodiscard]] virtual hand_index_t find_card_in_hand(const Card& card) const = 0;
|
[[nodiscard]] virtual player_t turn() const = 0;
|
||||||
|
[[nodiscard]] virtual clue_t num_clues() const = 0;
|
||||||
|
[[nodiscard]] virtual std::vector<std::vector<Card>> hands() const = 0;
|
||||||
|
[[nodiscard]] virtual std::vector<Card> cur_hand() const = 0;
|
||||||
|
[[nodiscard]] virtual size_t draw_pile_size() const = 0;
|
||||||
|
|
||||||
[[nodiscard]] virtual bool is_trash(const Card& card) const = 0;
|
[[nodiscard]] virtual bool is_trash(const Card& card) const = 0;
|
||||||
[[nodiscard]] virtual bool is_playable(const Card& card) const = 0;
|
[[nodiscard]] virtual bool is_playable(const Card& card) const = 0;
|
||||||
[[nodiscard]] virtual size_t draw_pile_size() const = 0;
|
|
||||||
[[nodiscard]] virtual bool is_relative_state_initialized() const = 0;
|
[[nodiscard]] virtual bool is_relative_state_initialized() const = 0;
|
||||||
|
[[nodiscard]] virtual hand_index_t find_card_in_hand(const Card& card) const = 0;
|
||||||
|
|
||||||
[[nodiscard]] virtual std::uint64_t enumerated_states() const = 0;
|
[[nodiscard]] virtual std::uint64_t enumerated_states() const = 0;
|
||||||
[[nodiscard]] virtual const std::unordered_map<unsigned long, probability_t>& position_tablebase() const = 0;
|
[[nodiscard]] virtual const std::unordered_map<unsigned long, probability_t>& position_tablebase() const = 0;
|
||||||
|
@ -182,8 +217,8 @@ public:
|
||||||
virtual void init_backtracking_information() = 0;
|
virtual void init_backtracking_information() = 0;
|
||||||
virtual probability_t evaluate_state() = 0;
|
virtual probability_t evaluate_state() = 0;
|
||||||
|
|
||||||
virtual std::optional<probability_t> lookup() const = 0;
|
[[nodiscard]] virtual std::optional<probability_t> lookup() const = 0;
|
||||||
|
[[nodiscard]] virtual std::uint64_t unique_id() const = 0;
|
||||||
virtual std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions() = 0;
|
virtual std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions() = 0;
|
||||||
virtual std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> possible_next_states(hand_index_t index, bool play) = 0;
|
virtual std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> possible_next_states(hand_index_t index, bool play) = 0;
|
||||||
|
|
||||||
|
@ -203,7 +238,7 @@ public:
|
||||||
HanabiState() = default;
|
HanabiState() = default;
|
||||||
explicit HanabiState(const std::vector<Card>& deck);
|
explicit HanabiState(const std::vector<Card>& deck);
|
||||||
|
|
||||||
void clue() final;
|
void give_clue() final;
|
||||||
void discard(hand_index_t index) final;
|
void discard(hand_index_t index) final;
|
||||||
void play(hand_index_t index) final;
|
void play(hand_index_t index) final;
|
||||||
|
|
||||||
|
@ -211,10 +246,15 @@ public:
|
||||||
|
|
||||||
void revert() final;
|
void revert() final;
|
||||||
|
|
||||||
|
[[nodiscard]] player_t turn() const final;
|
||||||
|
[[nodiscard]] clue_t num_clues() const final;
|
||||||
|
[[nodiscard]] std::vector<std::vector<Card>> hands() const final;
|
||||||
|
[[nodiscard]] std::vector<Card> cur_hand() const final;
|
||||||
|
[[nodiscard]] size_t draw_pile_size() const final;
|
||||||
|
|
||||||
[[nodiscard]] hand_index_t find_card_in_hand(const Card& card) const final;
|
[[nodiscard]] hand_index_t find_card_in_hand(const Card& card) const final;
|
||||||
[[nodiscard]] bool is_trash(const Card& card) const final;
|
[[nodiscard]] bool is_trash(const Card& card) const final;
|
||||||
[[nodiscard]] bool is_playable(const Card& card) const final;
|
[[nodiscard]] bool is_playable(const Card& card) const final;
|
||||||
[[nodiscard]] size_t draw_pile_size() const final;
|
|
||||||
[[nodiscard]] bool is_relative_state_initialized() const final;
|
[[nodiscard]] bool is_relative_state_initialized() const final;
|
||||||
|
|
||||||
[[nodiscard]] std::uint64_t enumerated_states() const final;
|
[[nodiscard]] std::uint64_t enumerated_states() const final;
|
||||||
|
@ -224,6 +264,7 @@ public:
|
||||||
probability_t evaluate_state() final;
|
probability_t evaluate_state() final;
|
||||||
|
|
||||||
std::optional<probability_t> lookup() const;
|
std::optional<probability_t> lookup() const;
|
||||||
|
std::uint64_t unique_id() const final;
|
||||||
|
|
||||||
std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions() final;
|
std::vector<std::pair<Action, std::optional<probability_t>>> get_reasonable_actions() final;
|
||||||
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> possible_next_states(hand_index_t index, bool play) final;
|
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> possible_next_states(hand_index_t index, bool play) final;
|
||||||
|
@ -237,7 +278,7 @@ private:
|
||||||
struct BacktrackAction {
|
struct BacktrackAction {
|
||||||
explicit BacktrackAction(
|
explicit BacktrackAction(
|
||||||
ActionType action_type,
|
ActionType action_type,
|
||||||
Card discarded_or_played = unknown_card,
|
Card discarded_or_played = Cards::unknown,
|
||||||
hand_index_t index = 0,
|
hand_index_t index = 0,
|
||||||
bool was_on_8_clues = false
|
bool was_on_8_clues = false
|
||||||
);
|
);
|
||||||
|
@ -299,8 +340,6 @@ private:
|
||||||
static constexpr player_t draw_pile = num_players;
|
static constexpr player_t draw_pile = num_players;
|
||||||
static constexpr player_t trash_or_play_stack = num_players + 1;
|
static constexpr player_t trash_or_play_stack = num_players + 1;
|
||||||
|
|
||||||
std::uint64_t unique_id() const;
|
|
||||||
|
|
||||||
// Usual game state
|
// Usual game state
|
||||||
player_t _turn{};
|
player_t _turn{};
|
||||||
clue_t _num_clues{};
|
clue_t _num_clues{};
|
||||||
|
|
|
@ -40,8 +40,16 @@ namespace Hanabi {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Card::operator==(const Card &other) const {
|
||||||
|
return suit == other.suit and rank == other.rank;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Card &card) {
|
std::ostream &operator<<(std::ostream &os, const Card &card) {
|
||||||
os << suit_initials[card.suit] << 5 - card.rank;
|
if (card == Cards::trash) {
|
||||||
|
os << "kt";
|
||||||
|
} else {
|
||||||
|
os << suit_initials[card.suit] << 5 - card.rank;
|
||||||
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +126,11 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
void HanabiState<num_suits, num_players, hand_size>::clue() {
|
void HanabiState<num_suits, num_players, hand_size>::give_clue() {
|
||||||
ASSERT(_num_clues > 0);
|
ASSERT(_num_clues > 0);
|
||||||
--_num_clues;
|
--_num_clues;
|
||||||
|
|
||||||
_actions_log.emplace(ActionType::clue, unknown_card, 0);
|
_actions_log.emplace(ActionType::clue, Cards::unknown, 0);
|
||||||
incr_turn();
|
incr_turn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,6 +477,37 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
|
player_t HanabiState<num_suits, num_players, hand_size>::turn() const {
|
||||||
|
return _turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
|
clue_t HanabiState<num_suits, num_players, hand_size>::num_clues() const {
|
||||||
|
return _num_clues;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
|
std::vector<std::vector<Card>> HanabiState<num_suits, num_players, hand_size>::hands() const {
|
||||||
|
std::vector<std::vector<Card>> hands;
|
||||||
|
for(player_t player = 0; player < num_players; player++) {
|
||||||
|
hands.push_back({});
|
||||||
|
for(const Card& card: _hands[player]) {
|
||||||
|
hands.back().push_back(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hands;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<suit_t num_suits, player_t num_players, hand_index_t hand_size>
|
||||||
|
std::vector<Card> HanabiState<num_suits, num_players, hand_size>::cur_hand() const {
|
||||||
|
std::vector<Card> hand;
|
||||||
|
for(const Card& card: _hands[_turn]) {
|
||||||
|
hand.push_back(card);
|
||||||
|
}
|
||||||
|
return hand;
|
||||||
|
}
|
||||||
|
|
||||||
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>
|
||||||
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> HanabiState<num_suits, num_players, hand_size>::possible_next_states(hand_index_t index, bool play) {
|
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> HanabiState<num_suits, num_players, hand_size>::possible_next_states(hand_index_t index, bool play) {
|
||||||
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> next_states;
|
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>> next_states;
|
||||||
|
@ -551,9 +590,9 @@ namespace Hanabi {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_num_clues > 0) {
|
if(_num_clues > 0) {
|
||||||
clue();
|
give_clue();
|
||||||
const std::optional<probability_t> prob = lookup();
|
const std::optional<probability_t> prob = lookup();
|
||||||
const Action action = {ActionType::clue, unknown_card};
|
const Action action = {ActionType::clue, Cards::unknown};
|
||||||
reasonable_actions.emplace_back(action, prob);
|
reasonable_actions.emplace_back(action, prob);
|
||||||
revert_clue();
|
revert_clue();
|
||||||
}
|
}
|
||||||
|
@ -648,7 +687,7 @@ namespace Hanabi {
|
||||||
|
|
||||||
// Last option is to stall
|
// Last option is to stall
|
||||||
if(_num_clues > 0) {
|
if(_num_clues > 0) {
|
||||||
clue();
|
give_clue();
|
||||||
const probability_t probability_stall = evaluate_state();
|
const probability_t probability_stall = evaluate_state();
|
||||||
revert_clue();
|
revert_clue();
|
||||||
best_probability = std::max(best_probability, probability_stall);
|
best_probability = std::max(best_probability, probability_stall);
|
||||||
|
|
Loading…
Reference in a new issue