Merge branch 'main' into cpr-fetch-content
This commit is contained in:
commit
759386f21b
8 changed files with 78 additions and 36 deletions
|
@ -1,7 +1,7 @@
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(endgame-analyzer CXX)
|
project(endgame-analyzer CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Werror")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Werror")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DGAME_STATE_NO_TABLEBASE_LOOKUP")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DGAME_STATE_NO_TABLEBASE_LOOKUP")
|
||||||
|
|
|
@ -21,7 +21,8 @@ namespace Hanabi
|
||||||
Card card;
|
Card card;
|
||||||
unsigned multiplicity;
|
unsigned multiplicity;
|
||||||
|
|
||||||
bool operator==(const CardMultiplicity &) const = default;
|
bool operator==(const CardMultiplicity &) const;
|
||||||
|
bool operator!=(const CardMultiplicity &) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HanabiStateIF
|
class HanabiStateIF
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace Hanabi
|
||||||
|
|
||||||
value_type & operator[](const Card & card);
|
value_type & operator[](const Card & card);
|
||||||
|
|
||||||
auto operator<=>(const CardArray &) const = default;
|
//auto operator<=>(const CardArray &) const = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using inner_array_t = typename InnerCardArray<T>::template array_t<starting_card_rank>;
|
using inner_array_t = typename InnerCardArray<T>::template array_t<starting_card_rank>;
|
||||||
|
@ -132,7 +132,7 @@ namespace Hanabi
|
||||||
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>>
|
std::vector<std::pair<CardMultiplicity, std::optional<probability_t>>>
|
||||||
possible_next_states(hand_index_t index, bool play) final;
|
possible_next_states(hand_index_t index, bool play) final;
|
||||||
|
|
||||||
auto operator<=>(const HanabiState &) const = default;
|
//auto operator<=>(const HanabiState &) const = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void print(std::ostream & os) const final;
|
void print(std::ostream & os) const final;
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Hanabi
|
||||||
, _actions_log(), _relative_representation(), _position_tablebase()
|
, _actions_log(), _relative_representation(), _position_tablebase()
|
||||||
, _enumerated_states(0)
|
, _enumerated_states(0)
|
||||||
{
|
{
|
||||||
std::ranges::fill(_stacks, starting_card_rank);
|
std::fill(_stacks.begin(), _stacks.end(), starting_card_rank);
|
||||||
for (const Card & card: deck)
|
for (const Card & card: deck)
|
||||||
{
|
{
|
||||||
_draw_pile.push_back({card, 1});
|
_draw_pile.push_back({card, 1});
|
||||||
|
@ -347,8 +347,11 @@ namespace Hanabi
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto replaced_card_it = std::ranges::find(_relative_representation.card_positions_draw[discarded.local_index]
|
auto replaced_card_it = std::find(
|
||||||
, _turn);
|
_relative_representation.card_positions_draw[discarded.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[discarded.local_index].end(),
|
||||||
|
_turn
|
||||||
|
);
|
||||||
ASSERT(replaced_card_it != _relative_representation.card_positions_draw[discarded.local_index].end());
|
ASSERT(replaced_card_it != _relative_representation.card_positions_draw[discarded.local_index].end());
|
||||||
if (played)
|
if (played)
|
||||||
{
|
{
|
||||||
|
@ -358,7 +361,10 @@ namespace Hanabi
|
||||||
{
|
{
|
||||||
*replaced_card_it = RelativeRepresentationData::discard_pile;
|
*replaced_card_it = RelativeRepresentationData::discard_pile;
|
||||||
}
|
}
|
||||||
std::ranges::sort(_relative_representation.card_positions_draw[discarded.local_index]);
|
std::sort(
|
||||||
|
_relative_representation.card_positions_draw[discarded.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[discarded.local_index].end()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,11 +398,17 @@ namespace Hanabi
|
||||||
if (!draw.card.initial_trash)
|
if (!draw.card.initial_trash)
|
||||||
{
|
{
|
||||||
ASSERT(draw.card.in_starting_hand == false);
|
ASSERT(draw.card.in_starting_hand == false);
|
||||||
auto new_card_it = std::ranges::find(_relative_representation.card_positions_draw[draw.card.local_index]
|
auto new_card_it = std::find(
|
||||||
, RelativeRepresentationData::draw_pile);
|
_relative_representation.card_positions_draw[draw.card.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[draw.card.local_index].end(),
|
||||||
|
RelativeRepresentationData::draw_pile
|
||||||
|
);
|
||||||
ASSERT(new_card_it != _relative_representation.card_positions_draw[draw.card.local_index].end());
|
ASSERT(new_card_it != _relative_representation.card_positions_draw[draw.card.local_index].end());
|
||||||
*new_card_it = _turn;
|
*new_card_it = _turn;
|
||||||
std::ranges::sort(_relative_representation.card_positions_draw[draw.card.local_index]);
|
std::sort(
|
||||||
|
_relative_representation.card_positions_draw[draw.card.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[draw.card.local_index].end()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,10 +472,17 @@ namespace Hanabi
|
||||||
if (_relative_representation.initialized && !drawn.initial_trash)
|
if (_relative_representation.initialized && !drawn.initial_trash)
|
||||||
{
|
{
|
||||||
ASSERT(drawn.in_starting_hand == false);
|
ASSERT(drawn.in_starting_hand == false);
|
||||||
auto drawn_card_it = std::ranges::find(_relative_representation.card_positions_draw[drawn.local_index], _turn);
|
auto drawn_card_it = std::find(
|
||||||
|
_relative_representation.card_positions_draw[drawn.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[drawn.local_index].end(),
|
||||||
|
_turn
|
||||||
|
);
|
||||||
ASSERT(drawn_card_it != _relative_representation.card_positions_draw[drawn.local_index].end());
|
ASSERT(drawn_card_it != _relative_representation.card_positions_draw[drawn.local_index].end());
|
||||||
*drawn_card_it = RelativeRepresentationData::draw_pile;
|
*drawn_card_it = RelativeRepresentationData::draw_pile;
|
||||||
std::ranges::sort(_relative_representation.card_positions_draw[drawn.local_index]);
|
std::sort(
|
||||||
|
_relative_representation.card_positions_draw[drawn.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[drawn.local_index].end()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_weighted_draw_pile_size++;
|
_weighted_draw_pile_size++;
|
||||||
|
@ -494,11 +513,17 @@ namespace Hanabi
|
||||||
return RelativeRepresentationData::discard_pile;
|
return RelativeRepresentationData::discard_pile;
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
auto hand_card_it = std::ranges::find(_relative_representation.card_positions_draw[discarded_card.local_index]
|
auto hand_card_it = std::find(
|
||||||
, old_position);
|
_relative_representation.card_positions_draw[discarded_card.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[discarded_card.local_index].end(),
|
||||||
|
old_position
|
||||||
|
);
|
||||||
ASSERT(hand_card_it != _relative_representation.card_positions_draw[discarded_card.local_index].end());
|
ASSERT(hand_card_it != _relative_representation.card_positions_draw[discarded_card.local_index].end());
|
||||||
*hand_card_it = _turn;
|
*hand_card_it = _turn;
|
||||||
std::ranges::sort(_relative_representation.card_positions_draw[discarded_card.local_index]);
|
std::sort(
|
||||||
|
_relative_representation.card_positions_draw[discarded_card.local_index].begin(),
|
||||||
|
_relative_representation.card_positions_draw[discarded_card.local_index].end()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,7 +942,7 @@ namespace Hanabi
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const auto id = unique_id();
|
const auto id = unique_id();
|
||||||
if (_position_tablebase.contains(id))
|
if (_position_tablebase.count(id) == 1)
|
||||||
{
|
{
|
||||||
return _position_tablebase.at(id);
|
return _position_tablebase.at(id);
|
||||||
}
|
}
|
||||||
|
@ -969,7 +994,7 @@ namespace Hanabi
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#ifndef GAME_STATE_NO_TABLEBASE_LOOKUP
|
#ifndef GAME_STATE_NO_TABLEBASE_LOOKUP
|
||||||
if (_position_tablebase.contains(id_of_state)) {
|
if (_position_tablebase.count(id_of_state) == 1) {
|
||||||
return _position_tablebase[id_of_state];
|
return _position_tablebase[id_of_state];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1334,7 +1359,7 @@ namespace Hanabi
|
||||||
std::cout << "\n" << std::endl;
|
std::cout << "\n" << std::endl;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (_position_tablebase.contains(id))
|
if (_position_tablebase.count(id) == 1)
|
||||||
{
|
{
|
||||||
ASSERT(_position_tablebase[id] == probability);
|
ASSERT(_position_tablebase[id] == probability);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace Hanabi
|
||||||
constexpr rank_t starting_card_rank = 5;
|
constexpr rank_t starting_card_rank = 5;
|
||||||
constexpr suit_t max_suit_index = 5;
|
constexpr suit_t max_suit_index = 5;
|
||||||
constexpr size_t max_card_duplicity = 3;
|
constexpr size_t max_card_duplicity = 3;
|
||||||
constexpr clue_t max_num_clues = 8;
|
const clue_t max_num_clues = 8;
|
||||||
constexpr hand_index_t invalid_hand_idx = std::numeric_limits<hand_index_t>::max();
|
constexpr hand_index_t invalid_hand_idx = std::numeric_limits<hand_index_t>::max();
|
||||||
|
|
||||||
// We might want to change these at runtime to adapt to other variants.
|
// We might want to change these at runtime to adapt to other variants.
|
||||||
|
@ -75,6 +75,7 @@ namespace Hanabi
|
||||||
* This is inlined as this is a runtime critical function when backtracking.
|
* This is inlined as this is a runtime critical function when backtracking.
|
||||||
*/
|
*/
|
||||||
inline bool operator==(const Card & other) const;
|
inline bool operator==(const Card & other) const;
|
||||||
|
inline bool operator!=(const Card & other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ActionType : std::uint8_t
|
enum class ActionType : std::uint8_t
|
||||||
|
@ -154,6 +155,11 @@ namespace Hanabi
|
||||||
return suit == other.suit and rank == other.rank;
|
return suit == other.suit and rank == other.rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Card::operator!=(const Card & other) const
|
||||||
|
{
|
||||||
|
return not (*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::ostream & print_probability(std::ostream & os, const std::optional<T> & prob)
|
std::ostream & print_probability(std::ostream & os, const std::optional<T> & prob)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,16 @@
|
||||||
namespace Hanabi
|
namespace Hanabi
|
||||||
{
|
{
|
||||||
|
|
||||||
|
bool CardMultiplicity::operator==(Hanabi::CardMultiplicity const & other) const
|
||||||
|
{
|
||||||
|
return card == other.card && multiplicity == other.multiplicity;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CardMultiplicity::operator!=(Hanabi::CardMultiplicity const & other) const
|
||||||
|
{
|
||||||
|
return not (*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & os, HanabiStateIF const & hanabi_state)
|
std::ostream & operator<<(std::ostream & os, HanabiStateIF const & hanabi_state)
|
||||||
{
|
{
|
||||||
hanabi_state.print(os);
|
hanabi_state.print(os);
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Parsing
|
||||||
template<class T>
|
template<class T>
|
||||||
void extract(boost::json::object const & obj, T & t, std::string_view key)
|
void extract(boost::json::object const & obj, T & t, std::string_view key)
|
||||||
{
|
{
|
||||||
t = value_to<T>(obj.at(key));
|
t = boost::json::value_to<T>(obj.at(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace Hanabi
|
||||||
std::string text_str(text);
|
std::string text_str(text);
|
||||||
for (auto & command: cli_commands)
|
for (auto & command: cli_commands)
|
||||||
{
|
{
|
||||||
if (command.starts_with(text_str) && state-- <= 0)
|
if (command.find(text_str) == 0 && state-- <= 0)
|
||||||
{
|
{
|
||||||
return strdup(command.c_str());
|
return strdup(command.c_str());
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ namespace Hanabi
|
||||||
states_to_show.pop_back();
|
states_to_show.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ranges::sort(states_to_show, [](const auto & left, const auto & right) {
|
std::sort(states_to_show.begin(), states_to_show.end(), [](const auto & left, const auto & right) {
|
||||||
return left.second > right.second;
|
return left.second > right.second;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ namespace Hanabi
|
||||||
const std::string prompt = read_line_memory_safe("> ");
|
const std::string prompt = read_line_memory_safe("> ");
|
||||||
add_history(prompt.c_str());
|
add_history(prompt.c_str());
|
||||||
|
|
||||||
if (prompt.starts_with("help"))
|
if (prompt.find("help") == 0)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
@ -224,7 +224,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("dump-id-parts"))
|
if (prompt.find("dump-id-parts") == 0)
|
||||||
{
|
{
|
||||||
for (const auto val: game.state->dump_unique_id_parts().first)
|
for (const auto val: game.state->dump_unique_id_parts().first)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +239,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("quit") or prompt == "q")
|
if (prompt.find("quit") == 0 or prompt == "q")
|
||||||
{
|
{
|
||||||
std::cout << "Quitting." << std::endl;
|
std::cout << "Quitting." << std::endl;
|
||||||
clear_history();
|
clear_history();
|
||||||
|
@ -247,7 +247,7 @@ namespace Hanabi
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("set-initials"))
|
if (prompt.find("set-initials") == 0)
|
||||||
{
|
{
|
||||||
if (prompt.length() < 16)
|
if (prompt.length() < 16)
|
||||||
{
|
{
|
||||||
|
@ -268,7 +268,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("state"))
|
if (prompt.find("state") == 0)
|
||||||
{
|
{
|
||||||
std::cout << *game.state << std::endl;
|
std::cout << *game.state << std::endl;
|
||||||
const std::optional<probability_t> prob = game.state->lookup();
|
const std::optional<probability_t> prob = game.state->lookup();
|
||||||
|
@ -277,7 +277,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("evaluate"))
|
if (prompt.find("evaluate") == 0)
|
||||||
{
|
{
|
||||||
std::cout << "Evaluating current game state, this might take a while." << std::endl;
|
std::cout << "Evaluating current game state, this might take a while." << std::endl;
|
||||||
game.state->evaluate_state();
|
game.state->evaluate_state();
|
||||||
|
@ -285,7 +285,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("revert"))
|
if (prompt.find("revert") == 0)
|
||||||
{
|
{
|
||||||
if (depth == 0)
|
if (depth == 0)
|
||||||
{
|
{
|
||||||
|
@ -319,13 +319,13 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("id"))
|
if (prompt.find("id") == 0)
|
||||||
{
|
{
|
||||||
std::cout << game.state->unique_id() << std::endl;
|
std::cout << game.state->unique_id() << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("play"))
|
if (prompt.find("play") == 0)
|
||||||
{
|
{
|
||||||
const Card card = parse_card(prompt.substr(5, 2));
|
const Card card = parse_card(prompt.substr(5, 2));
|
||||||
if (prompt.length() < 7)
|
if (prompt.length() < 7)
|
||||||
|
@ -353,7 +353,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("discard"))
|
if (prompt.find("discard") == 0)
|
||||||
{
|
{
|
||||||
const auto hand = game.state->cur_hand();
|
const auto hand = game.state->cur_hand();
|
||||||
hand_index_t trash_index = invalid_hand_idx;
|
hand_index_t trash_index = invalid_hand_idx;
|
||||||
|
@ -384,7 +384,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("clue"))
|
if (prompt.find("clue") == 0)
|
||||||
{
|
{
|
||||||
if (game.state->num_clues() == clue_t(0))
|
if (game.state->num_clues() == clue_t(0))
|
||||||
{
|
{
|
||||||
|
@ -396,7 +396,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("actions"))
|
if (prompt.find("actions") == 0)
|
||||||
{
|
{
|
||||||
auto reasonable_actions = game.state->get_reasonable_actions();
|
auto reasonable_actions = game.state->get_reasonable_actions();
|
||||||
int max_rational_digit_len = std::accumulate(
|
int max_rational_digit_len = std::accumulate(
|
||||||
|
@ -422,7 +422,7 @@ namespace Hanabi
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prompt.starts_with("opt"))
|
if (prompt.find("opt") == 0)
|
||||||
{
|
{
|
||||||
const auto reasonable_actions = game.state->get_reasonable_actions();
|
const auto reasonable_actions = game.state->get_reasonable_actions();
|
||||||
if (reasonable_actions.empty())
|
if (reasonable_actions.empty())
|
||||||
|
|
Loading…
Reference in a new issue