implement downloading games and parsing deck
This commit is contained in:
parent
362d121930
commit
262d909f3d
3 changed files with 130 additions and 44 deletions
76
download.h
Normal file
76
download.h
Normal 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
|
78
game_state.h
78
game_state.h
|
@ -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
|
20
main.cpp
20
main.cpp
|
@ -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;
|
||||||
}
|
}
|
Loading…
Reference in a new issue