implement initialising + doing actions of game state
This commit is contained in:
parent
95365a8a68
commit
38e4101402
4 changed files with 146 additions and 101 deletions
121
download.h
121
download.h
|
@ -10,12 +10,10 @@
|
||||||
|
|
||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
|
|
||||||
|
|
||||||
// This helper function deduces the type and assigns the value with the matching key
|
// This helper function deduces the type and assigns the value with the matching key
|
||||||
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 = value_to<T>( obj.at( key ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Hanabi {
|
namespace Hanabi {
|
||||||
|
@ -29,89 +27,132 @@ namespace Hanabi {
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, Hanabi::Card const &card) {
|
||||||
|
jv = {{"suitIndex", card.suit},
|
||||||
|
{"rank", card.rank}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Download {
|
||||||
|
|
||||||
|
struct Action {
|
||||||
|
Hanabi::ActionType type{};
|
||||||
|
uint8_t target;
|
||||||
|
};
|
||||||
|
|
||||||
Action tag_invoke(boost::json::value_to_tag<Action>, boost::json::value const &jv) {
|
Action tag_invoke(boost::json::value_to_tag<Action>, boost::json::value const &jv) {
|
||||||
Hanabi::Action action{};
|
Action action{};
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
boost::json::object const &obj = jv.as_object();
|
boost::json::object const &obj = jv.as_object();
|
||||||
|
|
||||||
extract(obj, action.target, "target");
|
extract(obj, action.target, "target");
|
||||||
extract(obj, type, "type");
|
extract(obj, type, "type");
|
||||||
|
|
||||||
action.type = static_cast<ActionType>(type);
|
action.type = static_cast<Hanabi::ActionType>(type);
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionType::color_clue:
|
case Hanabi::ActionType::color_clue:
|
||||||
case ActionType::rank_clue:
|
case Hanabi::ActionType::rank_clue:
|
||||||
action.type = ActionType::clue;
|
action.type = Hanabi::ActionType::clue;
|
||||||
break;
|
break;
|
||||||
case ActionType::end_game:
|
case Hanabi::ActionType::end_game:
|
||||||
case ActionType::vote_terminate:
|
case Hanabi::ActionType::vote_terminate:
|
||||||
action.type = ActionType::end_game;
|
action.type = Hanabi::ActionType::end_game;
|
||||||
break;
|
break;
|
||||||
case ActionType::play:
|
case Hanabi::ActionType::play:
|
||||||
case ActionType::discard:
|
case Hanabi::ActionType::discard:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Invalid game format, could not parse action type " + std::to_string(type));
|
throw std::runtime_error(
|
||||||
|
"Invalid game format, could not parse action type " + std::to_string(type));
|
||||||
}
|
}
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, Hanabi::Card const &card) {
|
std::vector<Hanabi::Card> parse_deck(const boost::json::value &deck_json) {
|
||||||
jv = {{"suitIndex", card.suit}, {"rank", card.rank}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<Hanabi::Card> parse_deck(const boost::json::value& deck_json) {
|
|
||||||
auto deck = boost::json::value_to<std::vector<Hanabi::Card>>(deck_json);
|
auto deck = boost::json::value_to<std::vector<Hanabi::Card>>(deck_json);
|
||||||
for(auto & card : deck) {
|
for (auto &card: deck) {
|
||||||
assert(card.rank < 5);
|
assert(card.rank < 5);
|
||||||
assert(card.rank >= 0);
|
assert(card.rank >= 0);
|
||||||
assert(card.suit < 6);
|
assert(card.suit < 6);
|
||||||
assert(card.suit >= 0);
|
assert(card.suit >= 0);
|
||||||
}
|
}
|
||||||
return deck;
|
return deck;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Hanabi::Action> parse_actions(const boost::json::value& action_json) {
|
std::vector<Action> parse_actions(const boost::json::value &action_json) {
|
||||||
return boost::json::value_to<std::vector<Hanabi::Action>>(action_json);
|
return boost::json::value_to<std::vector<Action>>(action_json);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::json::object download_game_json(int game_id) {
|
boost::json::object download_game_json(int game_id) {
|
||||||
std::string request_str = "https://hanab.live/export/" + std::to_string(game_id);
|
std::string request_str = "https://hanab.live/export/" + std::to_string(game_id);
|
||||||
cpr::Response r = cpr::Get(cpr::Url(request_str));
|
cpr::Response r = cpr::Get(cpr::Url(request_str));
|
||||||
if (r.header["content-type"] != "application/json; charset=utf-8") {
|
if (r.header["content-type"] != "application/json; charset=utf-8") {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return boost::json::parse(r.text).as_object();
|
return boost::json::parse(r.text).as_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::json::object open_game_json(const char* filename) {
|
boost::json::object open_game_json(const char *filename) {
|
||||||
std::ifstream file(filename);
|
std::ifstream file(filename);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
std::cout << "Failed to open " << filename << "." << std::endl;
|
std::cout << "Failed to open " << filename << "." << std::endl;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
std::string game_json ((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
std::string game_json((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
return boost::json::parse(game_json).as_object();
|
return boost::json::parse(game_json).as_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_game(std::variant<int, const char*> game_spec) {
|
template<std::size_t num_suits, Hanabi::player_t num_players, std::size_t hand_size, uint8_t max_draw_pile_size>
|
||||||
const boost::json::object game = [&game_spec](){
|
Hanabi::HanabiState<num_suits, num_players, hand_size, max_draw_pile_size> produce_state(
|
||||||
|
const std::vector<Hanabi::Card>& deck,
|
||||||
|
const std::vector<Action>& actions,
|
||||||
|
size_t num_turns_to_replicate
|
||||||
|
) {
|
||||||
|
Hanabi::HanabiState<num_suits, num_players, hand_size, max_draw_pile_size> game(deck);
|
||||||
|
std::uint8_t index;
|
||||||
|
for (size_t i = 0; i < num_turns_to_replicate; i++) {
|
||||||
|
switch(actions[i].type) {
|
||||||
|
case Hanabi::ActionType::color_clue:
|
||||||
|
case Hanabi::ActionType::rank_clue:
|
||||||
|
game.clue();
|
||||||
|
break;
|
||||||
|
case Hanabi::ActionType::discard:
|
||||||
|
index = game.find_card_in_hand(deck[actions[i].target]);
|
||||||
|
assert(index != -1);
|
||||||
|
game.discard(index);
|
||||||
|
break;
|
||||||
|
case Hanabi::ActionType::play:
|
||||||
|
index = game.find_card_in_hand(deck[actions[i].target]);
|
||||||
|
assert(index != -1);
|
||||||
|
game.play(index);
|
||||||
|
break;
|
||||||
|
case Hanabi::ActionType::vote_terminate:
|
||||||
|
case Hanabi::ActionType::end_game:
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_game(std::variant<int, const char *> game_spec) {
|
||||||
|
const boost::json::object game_json = [&game_spec]() {
|
||||||
if (game_spec.index() == 0) {
|
if (game_spec.index() == 0) {
|
||||||
return download_game_json(std::get<int>(game_spec));
|
return download_game_json(std::get<int>(game_spec));
|
||||||
} else {
|
} else {
|
||||||
return open_game_json(std::get<const char*>(game_spec));
|
return open_game_json(std::get<const char *>(game_spec));
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
const std::vector<Hanabi::Card> deck = parse_deck(game.at("deck"));
|
const std::vector<Hanabi::Card> deck = parse_deck(game_json.at("deck"));
|
||||||
const std::vector<Hanabi::Action> actions = parse_actions(game.at("actions"));
|
const std::vector<Action> actions = parse_actions(game_json.at("actions"));
|
||||||
const unsigned num_players = game.at("players").as_array().size();
|
const unsigned num_players = game_json.at("players").as_array().size();
|
||||||
std::cout << deck.size() << std::endl;
|
std::cout << deck.size() << std::endl;
|
||||||
std::cout << num_players;
|
std::cout << num_players;
|
||||||
}
|
|
||||||
|
auto game = produce_state<6, 3, 5, 0>(deck, actions, 40);
|
||||||
|
std::cout << game << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespacen Download
|
||||||
|
|
||||||
#endif // DYNAMIC_PROGRAM_DOWNLOAD_H
|
#endif // DYNAMIC_PROGRAM_DOWNLOAD_H
|
||||||
|
|
15
game_state.h
15
game_state.h
|
@ -43,7 +43,6 @@ struct Card {
|
||||||
suit_t suit;
|
suit_t suit;
|
||||||
rank_t rank;
|
rank_t rank;
|
||||||
uint8_t copy;
|
uint8_t copy;
|
||||||
uint8_t index; // index of card in the deck
|
|
||||||
|
|
||||||
Card &operator++();
|
Card &operator++();
|
||||||
Card successor() const;
|
Card successor() const;
|
||||||
|
@ -53,7 +52,7 @@ struct Card {
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Card &card) {
|
std::ostream &operator<<(std::ostream &os, const Card &card) {
|
||||||
os << suit_initials[card.suit] << +card.rank;
|
os << suit_initials[card.suit] << 5 - card.rank;
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,14 +111,6 @@ enum class ActionType {
|
||||||
vote_terminate = 10,
|
vote_terminate = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Action type for replay format of hanab.live
|
|
||||||
*/
|
|
||||||
struct Action {
|
|
||||||
ActionType type{};
|
|
||||||
uint8_t target;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BacktrackAction {
|
struct BacktrackAction {
|
||||||
ActionType type{};
|
ActionType type{};
|
||||||
Card discarded{};
|
Card discarded{};
|
||||||
|
@ -142,6 +133,8 @@ public:
|
||||||
|
|
||||||
BacktrackAction discard(std::uint8_t index);
|
BacktrackAction discard(std::uint8_t index);
|
||||||
|
|
||||||
|
std::uint8_t find_card_in_hand(const Card& card) const;
|
||||||
|
|
||||||
void revert(const BacktrackAction &action);
|
void revert(const BacktrackAction &action);
|
||||||
|
|
||||||
void draw(std::uint8_t index);
|
void draw(std::uint8_t index);
|
||||||
|
@ -156,7 +149,7 @@ public:
|
||||||
clue_t _num_clues{};
|
clue_t _num_clues{};
|
||||||
std::uint8_t _draw_pile_size{};
|
std::uint8_t _draw_pile_size{};
|
||||||
Stacks<num_suits> _stacks{};
|
Stacks<num_suits> _stacks{};
|
||||||
std::array<boost::container::static_vector<Card, hand_size>, num_players> _hands{};
|
std::array<std::array<Card, hand_size>, num_players> _hands{};
|
||||||
CardPositions<num_suits> _card_positions{};
|
CardPositions<num_suits> _card_positions{};
|
||||||
std::list<CardMultiplicity> _draw_pile{};
|
std::list<CardMultiplicity> _draw_pile{};
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,17 @@ namespace Hanabi {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<std::size_t num_suits, player_t num_players, std::size_t hand_size, uint8_t max_draw_pile_size>
|
||||||
|
std::uint8_t HanabiState<num_suits, num_players, hand_size, max_draw_pile_size>::find_card_in_hand(
|
||||||
|
const Hanabi::Card &card) const {
|
||||||
|
for(std::uint8_t i = 0; i < hand_size; i++) {
|
||||||
|
if(_hands[_turn][i].rank == card.rank && _hands[_turn][i].suit == card.suit) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
template<std::size_t num_suits, player_t num_players, std::size_t hand_size, uint8_t max_draw_pile_size>
|
template<std::size_t num_suits, player_t num_players, std::size_t hand_size, uint8_t max_draw_pile_size>
|
||||||
std::ostream &operator<<(std::ostream &os, const HanabiState<num_suits, num_players, hand_size, max_draw_pile_size> hanabi_state) {
|
std::ostream &operator<<(std::ostream &os, const HanabiState<num_suits, num_players, hand_size, max_draw_pile_size> hanabi_state) {
|
||||||
os << "Stacks: " << hanabi_state._stacks << std::endl;
|
os << "Stacks: " << hanabi_state._stacks << std::endl;
|
||||||
|
|
4
main.cpp
4
main.cpp
|
@ -38,7 +38,7 @@ void test_game() {
|
||||||
assert(state == state2);
|
assert(state == state2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void download() { get_game("314159.json"); }
|
void download() { Download::get_game("1004116.json"); }
|
||||||
|
|
||||||
void print_sizes() {
|
void print_sizes() {
|
||||||
std::cout << "size of card -> hand map: " << sizeof(HanabiState<5, 3, 4, 5>)
|
std::cout << "size of card -> hand map: " << sizeof(HanabiState<5, 3, 4, 5>)
|
||||||
|
@ -54,7 +54,7 @@ void print_sizes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Hanabi::test_game();
|
// Hanabi::test_game();
|
||||||
Hanabi::download();
|
Hanabi::download();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue