-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Code dump. TODO: Cleanup & documentation.
- Loading branch information
1 parent
8992f54
commit 0974a84
Showing
27 changed files
with
6,418 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
bazel-* | ||
tables/*.phe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package(default_visibility = ["//visibility:public"]) | ||
|
||
cc_library( | ||
name = "poker_hand_eval", | ||
hdrs = ["poker_hand_eval.h"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
new_git_repository( | ||
name = "git_sparsehash", | ||
build_file_content = """ | ||
cc_library( | ||
name = "sparsehash", | ||
includes = ["."], | ||
hdrs = glob(["sparsehash/**/*"]), | ||
visibility = ["//visibility:public"], | ||
)""", | ||
commit = "47a55825ca3b35eab1ca22b7ab82b9544e32a9af", | ||
remote = "https://github.com/sparsehash/sparsehash-c11.git", | ||
) | ||
|
||
git_repository( | ||
name = "com_github_google_benchmark", | ||
remote = "https://github.com/google/benchmark.git", | ||
tag = "v1.4.0", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
cc_binary( | ||
name = "benchmarks", | ||
srcs = ["benchmarks.cc"], | ||
data = ["//tables:phe"], | ||
linkopts = ["-lpthread"], # "@com_github_google_benchmark//:benchmark" should link to pthread :/ | ||
deps = [ | ||
"//:poker_hand_eval", | ||
"@com_github_google_benchmark//:benchmark", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
#include <algorithm> | ||
#include <array> | ||
#include <iostream> | ||
#include <utility> | ||
#include <vector> | ||
|
||
#include <benchmark/benchmark.h> | ||
|
||
#include "poker_hand_eval.h" | ||
|
||
template <size_t HandSize> | ||
using HandType = std::array<uint32_t, HandSize>; | ||
using DeckType = std::array<uint32_t, 52>; | ||
|
||
template <size_t HandSize> | ||
HandType<HandSize> random_hand() { | ||
static DeckType deck = []() { | ||
DeckType d; | ||
for (size_t i = 0; i < 52; i++) { d[i] = i; } | ||
return d; | ||
}(); | ||
static size_t deal_index = 52; | ||
if (deal_index + HandSize >= 52) { | ||
std::random_shuffle(std::begin(deck), std::end(deck)); | ||
deal_index = 0; | ||
} | ||
|
||
HandType<HandSize> hand; | ||
std::copy_n(std::next(std::begin(deck), deal_index), HandSize, std::begin(hand)); | ||
deal_index += HandSize; | ||
return hand; | ||
} | ||
|
||
void BM_Control5(benchmark::State& state) { | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(random_hand<5>()); | ||
} | ||
} | ||
BENCHMARK(BM_Control5); | ||
|
||
void BM_phe_dfs5(benchmark::State& state) { | ||
PokerHandEval<5> phe("tables/dfs5.phe"); | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(phe.eval(random_hand<5>())); | ||
} | ||
} | ||
BENCHMARK(BM_phe_dfs5); | ||
|
||
void BM_phe_bfs5(benchmark::State& state) { | ||
PokerHandEval<5> phe("tables/bfs5.phe"); | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(phe.eval(random_hand<5>())); | ||
} | ||
} | ||
BENCHMARK(BM_phe_bfs5); | ||
|
||
void BM_phe_veb5(benchmark::State& state) { | ||
PokerHandEval<5> phe("tables/veb5.phe"); | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(phe.eval(random_hand<5>())); | ||
} | ||
} | ||
BENCHMARK(BM_phe_veb5); | ||
|
||
void BM_Control7(benchmark::State& state) { | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(random_hand<7>()); | ||
} | ||
} | ||
BENCHMARK(BM_Control7); | ||
|
||
void BM_phe_dfs7(benchmark::State& state) { | ||
PokerHandEval<7> phe("tables/dfs7.phe"); | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(phe.eval(random_hand<7>())); | ||
} | ||
} | ||
BENCHMARK(BM_phe_dfs7); | ||
|
||
void BM_phe_bfs7(benchmark::State& state) { | ||
PokerHandEval<7> phe("tables/bfs7.phe"); | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(phe.eval(random_hand<7>())); | ||
} | ||
} | ||
BENCHMARK(BM_phe_bfs7); | ||
|
||
void BM_phe_veb7(benchmark::State& state) { | ||
PokerHandEval<7> phe("tables/veb7.phe"); | ||
for (auto _ : state) { | ||
benchmark::DoNotOptimize(phe.eval(random_hand<7>())); | ||
} | ||
} | ||
BENCHMARK(BM_phe_veb7); | ||
|
||
BENCHMARK_MAIN(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package(default_visibility = ["//visibility:private"]) | ||
|
||
cc_binary( | ||
name = "generate_tables", | ||
srcs = ["generate_tables.cc"], | ||
deps = [ | ||
":memory_layout", | ||
":phe", | ||
"//third_party/senzee", | ||
], | ||
) | ||
|
||
cc_library( | ||
name = "fsm", | ||
hdrs = [ | ||
"fsm.h", | ||
"fsm.inl", | ||
], | ||
deps = [ | ||
":common", | ||
"@git_sparsehash//:sparsehash", | ||
], | ||
) | ||
|
||
cc_library( | ||
name = "phe", | ||
hdrs = [ | ||
"phe.h", | ||
"phe.inl", | ||
], | ||
deps = [ | ||
":common", | ||
":fsm", | ||
":memory_layout", | ||
"//:poker_hand_eval", | ||
], | ||
) | ||
|
||
cc_library( | ||
name = "memory_layout", | ||
hdrs = [ | ||
"memory_layout.h", | ||
"memory_layout.inl", | ||
], | ||
deps = [":fsm"], | ||
) | ||
|
||
cc_library( | ||
name = "common", | ||
srcs = ["common.cc"], | ||
hdrs = ["common.h"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#include "generate_tables/common.h" | ||
|
||
#include <sstream> | ||
|
||
namespace poker_eval { | ||
|
||
Hand Hand::decode(EncodedHand encoded) { | ||
static_assert(sizeof(Hand) == sizeof(EncodedHand), | ||
"EncodedHand cannot be reinterpreted as Hand."); | ||
return *reinterpret_cast<Hand*>(&encoded); | ||
} | ||
|
||
EncodedHand Hand::encode() const { | ||
static_assert(sizeof(Hand) == sizeof(EncodedHand), | ||
"Hand cannot be reinterpreted as EncodedHand."); | ||
return *reinterpret_cast<const EncodedHand*>(this); | ||
} | ||
|
||
std::string Hand::debug_string() const { | ||
std::stringstream ss; | ||
ss << "{"; | ||
for (size_t i = 0; i < size; i++) { | ||
if (i > 0) { | ||
ss << ", "; | ||
} | ||
ss << int(cards[i]); | ||
} | ||
ss << "}"; | ||
return ss.str(); | ||
} | ||
|
||
void for_each_hand(uint8_t desired_hand_size, | ||
std::function<void(const Hand&)> fn) { | ||
Hand current_hand; | ||
current_hand.size = desired_hand_size; | ||
|
||
for (uint8_t i = 0; i < desired_hand_size; ++i) { | ||
current_hand.cards[i] = i; | ||
} | ||
fn(current_hand); | ||
|
||
if (desired_hand_size == 0) { | ||
return; | ||
} | ||
|
||
while (true) { | ||
int i = desired_hand_size - 1; | ||
++current_hand.cards[i]; | ||
|
||
while (current_hand.cards[i] > 52 + i - desired_hand_size) { | ||
--i; | ||
if (i < 0) { | ||
return; | ||
} | ||
++current_hand.cards[i]; | ||
} | ||
|
||
for (; i < desired_hand_size - 1; ++i) { | ||
current_hand.cards[i + 1] = current_hand.cards[i] + 1; | ||
} | ||
|
||
fn(current_hand); | ||
} | ||
} | ||
|
||
} // namespace poker_eval |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#pragma once | ||
|
||
#include <array> | ||
#include <cstdint> | ||
#include <functional> | ||
|
||
namespace poker_eval { | ||
|
||
// Cards are represented as integers in the range [0, 52). | ||
// The interpretation of those values are at the discretion of the | ||
// EvalFn, defined below. | ||
using Card = uint8_t; | ||
|
||
// For simplicity and efficiency, a hand of cards is defined to have seven | ||
// or fewer cards. | ||
// Since each card takes one byte (with one byte reserved for size), a hand can | ||
// be encoded into an eight byte structure. | ||
using EncodedHand = uint64_t; | ||
|
||
// A score is the valuation of a complete hand of cards, as provided by the | ||
// EvalFn, defined below. | ||
// Following previous convention and to more easily fit into a flattened | ||
// finite-state-machine, scores are defined to be 32-bit unsigned integers. | ||
using Score = uint32_t; | ||
|
||
// A union of EncodedHand and Score, used to label nodes in the | ||
// finite-state-machine. | ||
// EncodedHand are used for non-terminal nodes. | ||
// Scores are used for terminal nodes. | ||
// | ||
// Lucky for us, both are defined as uint64_t, so we don't need to delve into | ||
// real union shenanigans. | ||
// If we decide to switch Score to be a double, this might need to become a real | ||
// union. | ||
using HandOrScore = uint64_t; | ||
|
||
// Hand is a container of up-to seven Cards. | ||
struct Hand { | ||
// The number of Cards in the `cards` field that are meaningfully populated. | ||
// The remaining entries are garbage memory. | ||
uint8_t size = 0; | ||
// The set of cards in the hand. | ||
// These should be kept sorted. This struct does not guarantee this invariant. | ||
Card cards[7] = {0, 0, 0, 0, 0, 0, 0}; | ||
|
||
// Constructs a Hand object from an encoded version of the hand. | ||
static Hand decode(EncodedHand encoded); | ||
|
||
// Returns an encoded version of this hand. | ||
EncodedHand encode() const; | ||
|
||
// Returns a human-readable string describing the content of this hand. | ||
std::string debug_string() const; | ||
}; | ||
|
||
// Efficient associative container, keyed off cards. | ||
template <typename T> | ||
using MapCardTo = std::array<T, 52>; | ||
|
||
// Function that returns the valuation of a completed hand of cards. | ||
// This is used as a bootstrap to construct a more efficient evaluator. | ||
// This is the only place where card values, integers in the range [0, 52), | ||
// are given an interpretation. | ||
using EvalFn = std::function<Score(const Hand&)>; | ||
|
||
// Utility method that executes a given callback for each valid hand of a given | ||
// size. | ||
void for_each_hand(uint8_t desired_hand_size, | ||
std::function<void(const Hand&)> fn); | ||
|
||
} // namespace poker_eval |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#pragma once | ||
|
||
#include <unordered_map> | ||
#include <vector> | ||
|
||
#include "generate_tables/common.h" | ||
|
||
namespace poker_eval { | ||
|
||
// A finite-state-machine that uses cards, as defined in common.h, as the input | ||
// alphabet. The states are representative hands from the equivalence class of | ||
// possible scores. | ||
// After hand_size hops, the state becomes the score. | ||
// | ||
// fsm[card_0][card_1][card_2]...[card_(max_hand_size-1)] -> score | ||
using FSM = std::unordered_map<EncodedHand, MapCardTo<HandOrScore>>; | ||
|
||
// Builds a finite-state-machine for hands of the current size, using the given | ||
// evaluation function. | ||
// | ||
// Note: for efficiency reasons, hand representations are compacted and | ||
// hand_size cannot exceed seven. | ||
template <uint8_t hand_size> | ||
FSM build_fsm(EvalFn eval_fn); | ||
|
||
} // namespace poker_eval | ||
|
||
#include "generate_tables/fsm.inl" |
Oops, something went wrong.