implement downloading games and parsing deck

This commit is contained in:
Maximilian Keßler 2023-08-05 00:34:31 +02:00
parent 362d121930
commit 262d909f3d
Signed by: max
GPG key ID: BCC5A619923C0BA5
3 changed files with 130 additions and 44 deletions

76
download.h Normal file
View file

@ -0,0 +1,76 @@
#ifndef DYNAMIC_PROGRAM_DOWNLOAD_H
#define DYNAMIC_PROGRAM_DOWNLOAD_H
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <cpr/cpr.h>
#include <iostream>
#include "game_state.h"
// This helper function deduces the type and assigns the value with the matching key
template<class T>
void extract( boost::json::object const& obj, T& t, std::string_view key )
{
t = value_to<T>( obj.at( key ) );
}
namespace Hanabi {
Card tag_invoke(boost::json::value_to_tag<Card>,
boost::json::value const &jv) {
Hanabi::Card card{};
boost::json::object const &obj = jv.as_object();
extract(obj, card.rank, "rank");
extract(obj, card.suit, "suitIndex");
card.rank = 5 - card.rank;
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}};
}
}
std::vector<Hanabi::Card> parse_deck(const boost::json::value& deck_json) {
auto deck = boost::json::value_to<std::vector<Hanabi::Card>>(deck_json);
std::array<std::array<Hanabi::rank_t, 5>, 6> next_copy_indices {};
for(size_t i = 0; i < deck.size(); i++) {
auto &card = deck[i];
assert(card.rank < 5);
assert(card.rank >= 0);
assert(card.suit < 6);
assert(card.suit >= 0);
card.index = i;
card.copy = next_copy_indices[card.suit][card.rank];
next_copy_indices[card.suit][card.rank]++;
}
return deck;
}
boost::json::object download_game_json(int game_id) {
std::string request_str = "https://hanab.live/export/" + std::to_string(game_id);
cpr::Response r = cpr::Get(cpr::Url(request_str));
if (r.header["content-type"] != "application/json; charset=utf-8") {
return {};
}
return boost::json::parse(r.text).as_object();
}
void download_game(int game_id) {
boost::json::object game = download_game_json(game_id);
std::cout << game << std::endl;
const auto deck = parse_deck(game.at("deck"));
const unsigned num_players = game.at("players").as_array().size();
for(const auto& card : deck) {
std::cout << card << std::endl;
}
std::cout << num_players;
}
#endif // DYNAMIC_PROGRAM_DOWNLOAD_H

View file

@ -17,6 +17,8 @@
#include <ostream> #include <ostream>
namespace Hanabi {
using rank_t = std::uint8_t; using rank_t = std::uint8_t;
using suit_t = std::uint8_t; using suit_t = std::uint8_t;
using clue_t = std::uint8_t; using clue_t = std::uint8_t;
@ -24,7 +26,6 @@ using player_t = std::int8_t;
using state_t = std::uint32_t; using state_t = std::uint32_t;
/** /**
* We will generally assume that stacks are played from n to 0 * We will generally assume that stacks are played from n to 0
* Playing a 0 will yield a clue * Playing a 0 will yield a clue
@ -40,21 +41,22 @@ constexpr player_t draw_pile = -1;
constexpr player_t trash_or_play_stack = -2; constexpr player_t trash_or_play_stack = -2;
constexpr clue_t max_num_clues = 8; constexpr clue_t max_num_clues = 8;
constexpr std::array<std::string, 5> suit_initials {"r", "y", "g", "b", "p"}; constexpr std::array<std::string, 5> suit_initials{"r", "y", "g", "b", "p"};
struct Card { 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;
const Card operator++(int); const Card operator++(int);
auto operator<=>(const Card&) const = default; auto operator<=>(const Card &) const = default;
}; };
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] << +card.rank;
return os; return os;
} }
@ -78,49 +80,45 @@ constexpr Card y4 = {1, 4, 0};
* - Number of clues * - Number of clues
*/ */
template<std::size_t num_suits> template <std::size_t num_suits> using Stacks = std::array<rank_t, num_suits>;
using Stacks = std::array<rank_t, num_suits>;
template<std::size_t num_suits> template <std::size_t num_suits>
std::ostream& operator<<(std::ostream& os, const Stacks<num_suits>& stacks); std::ostream &operator<<(std::ostream &os, const Stacks<num_suits> &stacks);
struct CardMultiplicity { struct CardMultiplicity {
Card card; Card card;
std::uint8_t multiplicity; std::uint8_t multiplicity;
auto operator<=>(const CardMultiplicity&) const = default; auto operator<=>(const CardMultiplicity &) const = default;
}; };
template<std::size_t num_suits> template <std::size_t num_suits> struct CardPositions {
struct CardPositions {
const player_t & operator[](const Card& card) const; const player_t &operator[](const Card &card) const;
player_t & operator[](const Card& card); player_t &operator[](const Card &card);
auto operator<=>(const CardPositions&) const = default; auto operator<=>(const CardPositions &) const = default;
private: private:
std::array<std::array<std::array<player_t, max_card_duplicity>, starting_card_rank>, num_suits> _card_positions; std::array<
std::array<std::array<player_t, max_card_duplicity>, starting_card_rank>,
num_suits>
_card_positions;
}; };
enum class ActionType { enum class ActionType { discard, clue, play };
discard,
clue,
play
};
struct Action { struct Action {
ActionType type {}; ActionType type{};
Card discarded {}; Card discarded{};
std::uint8_t index {}; std::uint8_t index{};
}; };
template <std::size_t num_suits, std::size_t num_players, std::size_t hand_size,
template <std::size_t num_suits, std::size_t num_players, std::size_t hand_size, std::uint8_t max_draw_pile_size> std::uint8_t max_draw_pile_size>
class HanabiState { class HanabiState {
public: public:
Action clue(); Action clue();
/** /**
* Plays a card from current hand, drawing top card of draw pile and rotating draw pile * Plays a card from current hand, drawing top card of draw pile and rotating draw pile
@ -130,8 +128,7 @@ public:
Action discard(std::uint8_t index); Action discard(std::uint8_t index);
void revert(const Action& action); void revert(const Action &action);
void draw(std::uint8_t index); void draw(std::uint8_t index);
void revert_draw(std::uint8_t index, Card card); void revert_draw(std::uint8_t index, Card card);
@ -141,22 +138,29 @@ public:
player_t _turn{}; player_t _turn{};
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<boost::container::static_vector<Card, hand_size>, num_players>
CardPositions<num_suits> _card_positions {}; _hands{};
std::list<CardMultiplicity> _draw_pile {}; CardPositions<num_suits> _card_positions{};
std::list<CardMultiplicity> _draw_pile{};
// further statistics that we might want to keep track of // further statistics that we might want to keep track of
uint8_t _pace{}; uint8_t _pace{};
auto operator<=>(const HanabiState&) const = default; auto operator<=>(const HanabiState &) const = default;
}; };
template <std::size_t num_suits, std::size_t num_players, std::size_t hand_size, std::uint8_t max_draw_pile_size> template <std::size_t num_suits, std::size_t num_players, std::size_t hand_size,
std::ostream& operator<<(std::ostream& os, HanabiState<num_suits, num_players, hand_size, max_draw_pile_size> hanabi_state); std::uint8_t max_draw_pile_size>
std::ostream &
operator<<(std::ostream &os,
HanabiState<num_suits, num_players, hand_size, max_draw_pile_size>
hanabi_state);
#include "game_state.hpp" #include "game_state.hpp"
template class HanabiState<5, 3, 4, 20>; template class HanabiState<5, 3, 4, 20>;
#endif // DYNAMIC_PROGRAM_GAME_STATE_H }
#endif // DYNAMIC_PROGRAM_GAME_STATE_H

View file

@ -9,13 +9,15 @@
#include <vector> #include <vector>
#include "game_state.h" #include "game_state.h"
#include "download.h"
namespace Hanabi {
void test_game() { void test_game() {
HanabiState<2, 2, 5, 10> state; HanabiState<2, 2, 5, 10> state;
state._stacks[0] = 2; state._stacks[0] = 2;
state._stacks[1] = 3; state._stacks[1] = 3;
Card r41 = {0,4,1}; Card r41 = {0, 4, 1};
state._draw_pile.push_back({r41, 1}); state._draw_pile.push_back({r41, 1});
state._hands[0] = {y0, y1, y2, r0, r1}; state._hands[0] = {y0, y1, y2, r0, r1};
state._hands[1] = {r1, r1, y1, r3, r2}; state._hands[1] = {r1, r1, y1, r3, r2};
@ -36,20 +38,24 @@ void test_game() {
assert(state == state2); assert(state == state2);
} }
void print_sizes() { void download() { download_game(1000000); }
std::cout << "size of card -> hand map: " << sizeof(HanabiState<5,3,4, 5>) << std::endl;
CardSetSpecification<5> test; void print_sizes() {
std::cout << sizeof(CardSetSpecification<8>::AdditionalCardsFlags) << std::endl; std::cout << "size of card -> hand map: " << sizeof(HanabiState<5, 3, 4, 5>)
<< std::endl;
unsigned exp = 32; unsigned exp = 32;
std::cout << "Pair size: " << sizeof(std::pair<std::uint32_t, float>) << std::endl; std::cout << "Pair size: " << sizeof(std::pair<std::uint32_t, float>)
<< std::endl;
std::cout << sizeof(boost::rational<int>) << std::endl; std::cout << sizeof(boost::rational<int>) << std::endl;
std::cout << (1ul << exp) << std::endl; std::cout << (1ul << exp) << std::endl;
} }
}
int main() { int main() {
test_game(); // Hanabi::test_game();
Hanabi::download();
return 0; return 0;
} }