From b25cf775e74d86e0af37ef95952cc9dd94c8a402 Mon Sep 17 00:00:00 2001 From: Marc Lanctot Date: Sat, 12 Oct 2024 10:48:32 -0230 Subject: [PATCH] Remove german_whist_foregame --- docs/games.md | 1 - open_spiel/games/CMakeLists.txt | 5 - .../german_whist_build_ttable.cc | 44 - .../german_whist_endgame.cc | 742 -------------- .../german_whist_foregame.cc | 721 -------------- .../german_whist_foregame.h | 167 ---- .../german_whist_foregame_test.cc | 36 - .../playthroughs/german_whist_foregame.txt | 905 ------------------ open_spiel/python/tests/pyspiel_test.py | 1 - 9 files changed, 2622 deletions(-) delete mode 100644 open_spiel/games/german_whist_foregame/german_whist_build_ttable.cc delete mode 100644 open_spiel/games/german_whist_foregame/german_whist_endgame.cc delete mode 100644 open_spiel/games/german_whist_foregame/german_whist_foregame.cc delete mode 100644 open_spiel/games/german_whist_foregame/german_whist_foregame.h delete mode 100644 open_spiel/games/german_whist_foregame/german_whist_foregame_test.cc delete mode 100644 open_spiel/integration_tests/playthroughs/german_whist_foregame.txt diff --git a/docs/games.md b/docs/games.md index 9fe12dd9f3..043bf6d97a 100644 --- a/docs/games.md +++ b/docs/games.md @@ -34,7 +34,6 @@ Status | Game 🔶 | [Dou Dizhu](https://en.wikipedia.org/wiki/Dou_dizhu) | 3 | ❌ | ❌ | A three-player games where one player (dizhu) plays against a team of two (peasants). 🔶 | [Euchre](https://en.wikipedia.org/wiki/Euchre) | 4 | ❌ | ❌ | Trick-taking card game where players compete in pairs. 🟢 | [First-price Sealed-Bid Auction](https://en.wikipedia.org/wiki/First-price_sealed-bid_auction) | 2-10 | ❌ | ❌ | Agents submit bids simultaneously; highest bid wins, and that's the price paid. -🔶 | [German Whist](https://en.wikipedia.org/wiki/German_Whist) | 2 | ❌ | ❌ | Two-player trick-taking card game. 🟢 | [Gin Rummy](https://en.wikipedia.org/wiki/Gin_rummy) | 2 | ❌ | ❌ | Players score points by forming specific sets with the cards in their hands. 🟢 | [Go](https://en.wikipedia.org/wiki/Go_\(game\)) | 2 | ✅ | ✅ | Players place tokens on the board with the goal of encircling territory. 🟢 | [Goofspiel](https://en.wikipedia.org/wiki/Goofspiel) | 2-10 | ❌ | ❌ | Players bid with their cards to win other cards. diff --git a/open_spiel/games/CMakeLists.txt b/open_spiel/games/CMakeLists.txt index 74fa00272f..3ada4eeaca 100644 --- a/open_spiel/games/CMakeLists.txt +++ b/open_spiel/games/CMakeLists.txt @@ -438,11 +438,6 @@ add_executable(garnet_test mfg/garnet_test.cc ${OPEN_SPIEL_OBJECTS} $) add_test(garnet_test garnet_test) -add_executable(german_whist_foregame_test german_whist_foregame/german_whist_foregame_test.cc ${OPEN_SPIEL_OBJECTS} - $) -add_test(german_whist_foregame_test german_whist_foregame_test) -add_executable(german_whist_build_ttable german_whist_foregame/german_whist_build_ttable.cc ${OPEN_SPIEL_OBJECTS}) - add_executable(gin_rummy_test gin_rummy/gin_rummy_test.cc ${OPEN_SPIEL_OBJECTS} $) add_test(gin_rummy_test gin_rummy_test) diff --git a/open_spiel/games/german_whist_foregame/german_whist_build_ttable.cc b/open_spiel/games/german_whist_foregame/german_whist_build_ttable.cc deleted file mode 100644 index 6297b5535e..0000000000 --- a/open_spiel/games/german_whist_foregame/german_whist_build_ttable.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2024 DeepMind Technologies Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include "open_spiel/games/german_whist_foregame/german_whist_foregame.h" - -int main() { - std::vector> bin_coeffs = - open_spiel::german_whist_foregame::BinCoeffs( - 2 * open_spiel::german_whist_foregame::kNumRanks); - const uint32_t hard_threads = - 8; // set this to take advantage of more cores on your machine// - open_spiel::german_whist_foregame::vectorNa tablebase = - open_spiel::german_whist_foregame::BuildTablebase(bin_coeffs, - hard_threads); - std::random_device rd; - int num_samples = 100; - if (open_spiel::german_whist_foregame::TestTablebase(num_samples, rd(), - tablebase, bin_coeffs)) { - std::cout << "Tablebase accurate" << std::endl; - } else { - std::cout << "Tablebase inaccurate" << std::endl; - } - std::cout << "Starting Saving Tablebase" << std::endl; - open_spiel::german_whist_foregame::StoreTTable("TTable13.txt", tablebase); - std::cout << "Finished Saving Tablebase" << std::endl; - - return 0; -} diff --git a/open_spiel/games/german_whist_foregame/german_whist_endgame.cc b/open_spiel/games/german_whist_foregame/german_whist_endgame.cc deleted file mode 100644 index 96dd9b6d9b..0000000000 --- a/open_spiel/games/german_whist_foregame/german_whist_endgame.cc +++ /dev/null @@ -1,742 +0,0 @@ -// Copyright 2024 DeepMind Technologies Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Source Code for an Executable Generating an Endgame Tablebase for German -// Whist -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "open_spiel/games/german_whist_foregame/german_whist_foregame.h" -#include "open_spiel/utils/thread.h" - -namespace open_spiel { -namespace german_whist_foregame { - -struct Pair { - char index; - char value; - Pair(char index_, char value_) { - index = index_; - value = value_; - } - bool operator<(const Pair& pair) const { return value < pair.value; } -}; - -struct ActionStruct { - uint32_t index; - unsigned char suit; - bool player; - ActionStruct(uint32_t index_, unsigned char suit_, bool player_) { - index = index_; - suit = suit_; - player = player_; - } -}; -struct ActionValue { - ActionStruct action; - int value; - bool operator<(const ActionValue& aval) const { return value < aval.value; } -}; - -class Node { - private: - uint32_t cards_; - std::array suit_masks_; - char total_tricks_; - char trump_; - char score_; - char moves_; - bool player_; - std::vector history_; - uint64_t key_; - - public: - Node(uint32_t cards, std::array suit_masks, char trump, - bool player) { - cards_ = cards; - suit_masks_ = suit_masks; - total_tricks_ = popcnt_u32(cards); - trump_ = trump; - moves_ = 0; - player_ = player; - score_ = 0; - history_ = {}; - }; - bool Player() { return player_; }; - char Score() { return score_; }; - char Moves() { return moves_; }; - bool IsTerminal() { return (moves_ == 2 * total_tricks_); } - char RemainingTricks() { return (char)(total_tricks_ - (moves_ >> 1)); } - char TotalTricks() { return total_tricks_; } - uint32_t Cards() { return cards_; } - std::array SuitMasks() { return suit_masks_; } - uint64_t GetNodeKey() { return key_; } - bool Trick(ActionStruct lead, ActionStruct follow) { - // true if leader won// - return (lead.suit != follow.suit && lead.suit == trump_) || - (lead.suit == follow.suit && lead.index <= follow.index); - } - - void RemoveCard(ActionStruct action) { - // Removes card from cards_// - uint32_t mask_b = ~0; - mask_b = bzhi_u32(mask_b, action.index); - uint32_t mask_a = ~mask_b; - mask_a = blsr_u32(mask_a); - uint32_t copy_a = cards_ & mask_a; - uint32_t copy_b = cards_ & mask_b; - copy_a = copy_a >> 1; - cards_ = copy_a | copy_b; - // decrements appropriate suits// - suit_masks_[action.suit] = blsr_u32(suit_masks_[action.suit]) >> 1; - char suit = action.suit; - suit++; - while (suit < kNumSuits) { - suit_masks_[suit] = suit_masks_[suit] >> 1; - suit++; - } - } - void InsertCard(ActionStruct action) { - // inserts card into cards_// - uint32_t mask_b = ~0; - mask_b = bzhi_u32(mask_b, action.index); - uint32_t mask_a = ~mask_b; - uint32_t copy_b = cards_ & mask_b; - uint32_t copy_a = cards_ & mask_a; - copy_a = copy_a << 1; - uint32_t card = action.player << action.index; - cards_ = card | copy_a | copy_b; - // increments appropriate suits// - uint32_t new_suit = - (suit_masks_[action.suit] & mask_b) | (1 << action.index); - suit_masks_[action.suit] = - ((suit_masks_[action.suit] & mask_a) << 1) | new_suit; - char suit = action.suit; - suit++; - while (suit < kNumSuits) { - suit_masks_[suit] = suit_masks_[suit] << 1; - suit++; - } - } - void UpdateNodeKey() { - // recasts the cards and suitlengths into quasi-canonical form// - // least sig part of 32bit card is trump, then suits in ascending length// - - // note this canonical form does not take advantage of all isomorphisms// - // suppose a game is transformed as follows: all card bits flipped and the - // player bit flipped, ie player 1 has the lead and has player 0s cards from - // the original game// this implies player 1 achieves the minimax value of - // the original game ie the value is remaining tricks - value of the - // original game for this transformed game// also does not take advantage of - // single suit isomorphism. Namely all single suit games with the same card - // distribution are isomorphic. Currently this considers all trump, all no - // trump games as distinct// - uint64_t suit_sig = 0; - char trump_length = popcnt_u32(suit_masks_[trump_]); - if (trump_length > kNumRanks) { - throw; - } - std::vector non_trump_lengths; - for (char i = 0; i < kNumSuits; ++i) { - if (i != trump_) { - char length = popcnt_u32(suit_masks_[i]); - uint32_t sig = suit_masks_[i] & cards_; - if (suit_masks_[i] != 0) { - sig = (sig >> (tzcnt_u32(suit_masks_[i]))); - } - if (length > kNumRanks) { - throw 1; - } - non_trump_lengths.push_back(Triple{i, length, sig}); - } - } - // sorting takes advantage of two isomorphisms namely nontrump suits of - // nonequal length can be exchanged and the value of the game does not - // change// and this more complicated suppose two games with two or more - // (non_trump)suits of equal length, permuting those suits should not change - // the value of solved game ie it is an isomorphism// - std::sort(non_trump_lengths.begin(), non_trump_lengths.end()); - suit_sig = suit_sig | trump_length; - for (size_t i = 0; i < non_trump_lengths.size(); ++i) { - suit_sig = - suit_sig | ((uint64_t)non_trump_lengths[i].length << (4 * (i + 1))); - } - suit_sig = suit_sig << 32; - std::array suit_cards; - suit_cards[0] = cards_ & suit_masks_[trump_]; - if (suit_masks_[trump_] != 0) { - suit_cards[0] = suit_cards[0] >> tzcnt_u32(suit_masks_[trump_]); - } - uint32_t sum = popcnt_u32(suit_masks_[trump_]); - uint32_t cards = 0 | suit_cards[0]; - for (size_t i = 0; i < non_trump_lengths.size(); ++i) { - suit_cards[i] = cards_ & suit_masks_[non_trump_lengths[i].index]; - uint32_t val = 0; - if (suit_masks_[non_trump_lengths[i].index] != 0) { - val = tzcnt_u32(suit_masks_[non_trump_lengths[i].index]); - } - suit_cards[i] = suit_cards[i] >> val; - suit_cards[i] = suit_cards[i] << sum; - sum += popcnt_u32(suit_masks_[non_trump_lengths[i].index]); - cards = cards | suit_cards[i]; - } - // cards = cards | (player_ << 31); - key_ = suit_sig | (uint64_t)cards; -#ifdef DEBUG_KEY - std::cout << "CARDS_ " << cards_ << std::endl; - std::cout << "CARDS " << cards << std::endl; - std::cout << "SUIT MASKS " << std::endl; - for (int i = 0; i < kNumSuits; ++i) { - std::cout << suit_masks_[i] << std::endl; - } - std::cout << "SUIT_SIG " << suit_sig << std::endl; - std::cout << "KEY " << key_ << std::endl; -#endif - } - uint64_t AltKey() { - uint32_t mask = bzhi_u32(~0, 2 * RemainingTricks()); - return key_ ^ (uint64_t)mask; - } - // Move Ordering Heuristics// - // These could Definitely be improved, very hacky// - int LeadOrdering(ActionStruct action) { - char suit = action.suit; - uint32_t copy_cards = cards_; - if (player_ == 0) { - copy_cards = ~copy_cards; - } - uint32_t suit_cards = copy_cards & suit_masks_[suit]; - uint32_t mask = suit_cards & ~(suit_cards >> 1); - // represents out of the stategically inequivalent cards in a suit that a - // player holds, what rank is it, rank 0 is highest rank etc// - int suit_rank = popcnt_u32(bzhi_u32(mask, action.index)); - ApplyAction(action); - std::vector moves = LegalActions(); - UndoAction(action); - int sum = 0; - for (size_t i = 0; i < moves.size(); ++i) { - sum += Trick(action, moves[i]); - } - if (sum == moves.size()) { - return action.suit == trump_ - ? 0 - suit_rank - : -1 * kNumRanks - - suit_rank; // intriguing this seems to produce small - // perfomance increase// - } - if (sum == 0) { - return 2 * kNumRanks - suit_rank; - } else { - return 1 * kNumRanks - suit_rank; - } - } - int FollowOrdering(ActionStruct action) { - ActionStruct lead = history_.back(); - // follow ordering for fast cut offs// - // win as cheaply as possible, followed by lose as cheaply as possible - char suit = action.suit; - uint32_t copy_cards = cards_; - if (player_ == 0) { - copy_cards = ~copy_cards; - } - uint32_t suit_cards = copy_cards & suit_masks_[suit]; - uint32_t mask = suit_cards & ~(suit_cards >> 1); - // represents out of the stategically inequivalent cards in a suit that a - // player holds, what rank is it, rank 0 is highest rank etc// - int suit_rank = popcnt_u32(bzhi_u32(mask, action.index)); - if (!Trick(lead, action)) { - return -kNumRanks - suit_rank; - } else { - return -suit_rank; - } - } - - std::vector LegalActions() { - // Features// - // Move fusion// - std::vector out; - out.reserve(kNumRanks); - uint32_t copy_cards = cards_; - std::array player_suit_masks; - if (player_ == 0) { - copy_cards = ~copy_cards; - } - for (size_t i = 0; i < kNumSuits; ++i) { - uint32_t suit_cards = copy_cards & suit_masks_[i]; - player_suit_masks[i] = suit_cards & ~(suit_cards >> 1); -#ifdef DEBUG - std::cout << "Cards " << cards_ << std::endl; - std::cout << "Suit Mask " << i << " " << suit_masks_[i] << std::endl; - std::cout << "Player " << player_ << " suit mask " << (int)i << " " - << player_suit_masks[i] << std::endl; -#endif - } - for (char i = 0; i < kNumSuits; ++i) { - uint32_t suit_mask = player_suit_masks[i]; - bool lead = (moves_ % 2 == 0); - bool follow = (moves_ % 2 == 1); - bool correct_suit = 0; - bool void_in_suit = 0; - if (follow == true) { - correct_suit = (history_.back().suit == i); - void_in_suit = (player_suit_masks[history_.back().suit] == 0); - } - if ((lead || (follow && (correct_suit || void_in_suit)))) { - while (suit_mask != 0) { - uint32_t best = tzcnt_u32(suit_mask); - out.push_back(ActionStruct(best, i, player_)); - suit_mask = blsr_u32(suit_mask); - } - } - } -#ifdef DEBUG - std::cout << "Player " << player_ << " MoveGen " << std::endl; - for (size_t i = 0; i < out.size(); ++i) { - std::cout << out[i].index << " " << (int)out[i].suit << std::endl; - } -#endif - return out; - } - void ApplyAction(ActionStruct action) { -#ifdef DEBUG - std::cout << "Player " << player_ << " ApplyAction " << action.index << " " - << (int)action.suit << std::endl; -#endif - if (moves_ % 2 == 1) { - ActionStruct lead = history_.back(); - bool winner = !((Trick(lead, action)) ^ lead.player); -#ifdef DEBUG - std::cout << "Player " << winner << " won this trick" << std::endl; -#endif - score_ += (winner == 0); - player_ = (winner); - } else { - player_ = !player_; - } -#ifdef DEBUG - assert((suit_masks_[0] & suit_masks_[1]) == 0); - assert((suit_masks_[0] & suit_masks_[2]) == 0); - assert((suit_masks_[0] & suit_masks_[3]) == 0); - assert((suit_masks_[1] & suit_masks_[2]) == 0); - assert((suit_masks_[1] & suit_masks_[3]) == 0); - assert((suit_masks_[2] & suit_masks_[3]) == 0); -#endif - RemoveCard(action); - moves_++; - history_.push_back(action); - } - void UndoAction(ActionStruct action) { - if (moves_ % 2 == 0) { - ActionStruct lead = history_[history_.size() - 2]; - ActionStruct follow = history_[history_.size() - 1]; - bool winner = !(Trick(lead, follow) ^ lead.player); - score_ -= (winner == 0); - } - InsertCard(action); - moves_--; - player_ = history_.back().player; - history_.pop_back(); -#ifdef DEBUG - std::cout << "Player " << player_ << " UndoAction " << action.index << " " - << (int)action.suit << std::endl; -#endif - } -}; - -// solvers below -int AlphaBeta(Node* node, int alpha, int beta) { - // fail soft ab search// - // uses move ordering to speed up search// - if (node->IsTerminal()) { - return node->Score(); - } - // move ordering code// - std::vector actions = node->LegalActions(); - std::vector temp; - temp.reserve(kNumRanks); - for (int i = 0; i < actions.size(); ++i) { - if (node->Moves() % 2 == 0) { - temp.push_back({actions[i], node->LeadOrdering(actions[i])}); - } else { - temp.push_back({actions[i], node->FollowOrdering(actions[i])}); - } - } - std::sort(temp.begin(), temp.end()); - for (int i = 0; i < temp.size(); ++i) { - actions[i] = temp[i].action; - } - // alpha beta search// - if (node->Player() == 0) { - int val = 0; - for (int i = 0; i < actions.size(); ++i) { - node->ApplyAction(actions[i]); - val = std::max(val, AlphaBeta(node, alpha, beta)); - node->UndoAction(actions[i]); - alpha = std::max(val, alpha); - if (val >= beta) { - break; - } - } - return val; - } else if (node->Player() == 1) { - int val = node->TotalTricks(); - for (int i = 0; i < actions.size(); ++i) { - node->ApplyAction(actions[i]); - val = std::min(val, AlphaBeta(node, alpha, beta)); - node->UndoAction(actions[i]); - beta = std::min(val, beta); - if (val <= alpha) { - break; - } - } - return val; - } - return -1; -}; - -// Helper Functions// - -// Credit to computationalcombinatorics.wordpress.com -// hideous code for generating the next colexicographical combination// -bool NextColex(std::vector& v, int k) { - int num = 0; - for (int i = 0; i < v.size(); ++i) { - if (i == v.size() - 1) { - v[i] = v[i] + 1; - if (v[i] > k - v.size() + i) { - return false; - } - num = i; - break; - } else if (v[i + 1] - v[i] > 1 && v[i + 1] != i) { - v[i] = v[i] + 1; - if (v[i] > k - v.size() + i) { - return false; - } - num = i; - break; - } - } - for (int i = 0; i < num; ++i) { - v[i] = i; - } - return true; -} - -char IncrementalAlphaBetaMemoryIso( - Node* node, char alpha, char beta, int depth, const vectorNa* TTable, - const std::unordered_map* SuitRanks, - const std::vector>& bin_coeffs) { - // fail soft ab search - char val = 0; - uint64_t key = 0; - bool player = node->Player(); - if (node->IsTerminal()) { - return node->Score(); - } - if (node->Moves() % 2 == 0 && depth == 0) { - node->UpdateNodeKey(); - key = (player) ? node->AltKey() : node->GetNodeKey(); - uint32_t cards = key & bzhi_u64(~0, 32); - uint32_t colex = HalfColexer(cards, &bin_coeffs); - uint32_t suits = (key & (~0 ^ bzhi_u64(~0, 32))) >> 32; - uint32_t suit_rank = SuitRanks->at(suits); - char value = (player) - ? node->RemainingTricks() - TTable->Get(colex, suit_rank) - : TTable->Get(colex, suit_rank); - return value + node->Score(); - } else if (node->Player() == 0) { - val = 0; - std::vector actions = node->LegalActions(); - for (int i = 0; i < actions.size(); ++i) { - node->ApplyAction(actions[i]); - val = std::max( - val, IncrementalAlphaBetaMemoryIso(node, alpha, beta, depth - 1, - TTable, SuitRanks, bin_coeffs)); - node->UndoAction(actions[i]); - alpha = std::max(val, alpha); - if (val >= beta) { - break; - } - } - } else if (node->Player() == 1) { - val = node->TotalTricks(); - std::vector actions = node->LegalActions(); - for (int i = 0; i < actions.size(); ++i) { - node->ApplyAction(actions[i]); - val = std::min( - val, IncrementalAlphaBetaMemoryIso(node, alpha, beta, depth - 1, - TTable, SuitRanks, bin_coeffs)); - node->UndoAction(actions[i]); - beta = std::min(val, beta); - if (val <= alpha) { - break; - } - } - } - return val; -}; - -std::vector GWhistGenerator(int num, unsigned int seed) { - // generates pseudorandom endgames// - std::vector out; - out.reserve(num); - std::mt19937 g(seed); - std::array nums; - for (int i = 0; i < 2 * kNumRanks; ++i) { - nums[i] = i; - } - for (int i = 0; i < num; ++i) { - std::shuffle(nums.begin(), nums.end(), g); - uint32_t cards = 0; - std::array suits; - for (int j = 0; j < kNumRanks; ++j) { - cards = cards | (1 << nums[j]); - } - int sum = 0; - std::vector suit_lengths = {0, 0, 0, 0}; - for (int j = 0; j < kNumSuits - 1; ++j) { - int max = std::min(kNumRanks, 2 * kNumRanks - sum); - int min = std::max(0, (j - 1) * kNumRanks - sum); - std::uniform_int_distribution<> distrib(min, max); - suit_lengths[j] = distrib(g); - sum += suit_lengths[j]; - } - suit_lengths[kNumSuits - 1] = 2 * kNumRanks - sum; - sum = 0; - for (int j = 0; j < kNumSuits; ++j) { - sum += suit_lengths[j]; - if (suit_lengths[j] > kNumRanks) { - throw; - } - } - if (sum != 2 * kNumRanks) { - for (int j = 0; j < suit_lengths.size(); ++j) { - std::cout << suit_lengths[j] << " " << std::endl; - } - throw; - } - int cum_sum = 0; - for (int j = 0; j < kNumSuits; ++j) { - if (j == 0) { - suits[j] = bzhi_u32(~0, suit_lengths[j]); - } else { - suits[j] = - (bzhi_u32(~0, suit_lengths[j] + cum_sum)) ^ bzhi_u32(~0, cum_sum); - } - cum_sum += suit_lengths[j]; - } - out.push_back(Node(cards, suits, 0, false)); -#ifdef DEBUG - std::cout << popcnt_u32(cards) << " " - << popcnt_u32(suits[0]) + popcnt_u32(suits[1]) + - popcnt_u32(suits[2]) + popcnt_u32(suits[3]) - << std::endl; - std::cout << cards << " " << suits[0] << " " << suits[1] << " " << suits[2] - << " " << suits[3] << std::endl; -#endif - } - return out; -} - -void ThreadSolver(int size_endgames, vectorNa* outTTable, - const vectorNa* TTable, - const std::vector>& bin_coeffs, - const std::vector& suit_splits, - const std::unordered_map& SuitRanks, - size_t start_id, size_t end_id) { - // takes endgames solved to depth d-1 and returns endgames solved to depth d - // // - std::vector combination; - combination.reserve(size_endgames); - for (int i = 0; i < size_endgames; ++i) { - combination.push_back(i); - } - bool control = true; - int count = 0; - uint32_t cards = 0; - for (int i = 0; i < combination.size(); ++i) { - cards = cards | (1 << combination[i]); - } - while (count < start_id) { - NextColex(combination, 2 * size_endgames); - count++; - } - while (count < end_id && control) { - uint32_t cards = 0; - for (int i = 0; i < combination.size(); ++i) { - cards = cards | (1 << combination[i]); - } - for (int i = 0; i < suit_splits.size(); ++i) { - std::array suit_arr; - suit_arr[0] = bzhi_u32(~0, suit_splits[i] & 0b1111); - uint32_t sum = suit_splits[i] & 0b1111; - for (int j = 1; j < kNumSuits; ++j) { - uint32_t mask = bzhi_u32(~0, sum); - sum += (suit_splits[i] & (0b1111 << (4 * j))) >> 4 * j; - suit_arr[j] = bzhi_u32(~0, sum); - suit_arr[j] = suit_arr[j] ^ mask; - } - Node node(cards, suit_arr, 0, false); - char result = IncrementalAlphaBetaMemoryIso( - &node, 0, size_endgames, 2, TTable, &SuitRanks, bin_coeffs); - outTTable->Set(count, i, result); - } - control = NextColex(combination, 2 * size_endgames); - count++; - } -} -vectorNa RetroSolver(int size_endgames, vectorNa* TTable, - const std::vector>& bin_coeffs, - const uint32_t hard_threads) { - // takes endgames solved to depth d-1 and returns endgames solved to depth d - // // - vectorNa outTTable = InitialiseTTable(size_endgames, bin_coeffs); - std::vector suit_splits = GenQuads(size_endgames); - std::unordered_map SuitRanks; - GenSuitRankingsRel(size_endgames - 1, &SuitRanks); - std::vector combination; - combination.reserve(size_endgames); - for (int i = 0; i < size_endgames; ++i) { - combination.push_back(i); - } - uint32_t v_length = (suit_splits.size() >> 1) + 1; - uint32_t min_block_size = 256; - uint32_t num_threads = 1; - uint32_t num_outers = outTTable.GetOuterSize(); - // a haphazard attempt to mitigate false sharing// - for (uint32_t i = hard_threads; i >= 1; i--) { - if ((num_outers * v_length / i) >= min_block_size) { - num_threads = i; - break; - } - } - std::vector threads = {}; - for (int i = 0; i < num_threads; ++i) { - uint32_t block_size = num_outers / num_threads; - uint32_t start_id; - uint32_t end_id; - if (num_threads == 1) { - start_id = 0; - end_id = num_outers; - } else if (i == num_threads - 1) { - start_id = block_size * (num_threads - 1); - end_id = num_outers; - } else { - start_id = block_size * i; - end_id = block_size * (i + 1); - } - threads.emplace_back([&, start_id, end_id]() { - ThreadSolver(size_endgames, &outTTable, TTable, std::ref(bin_coeffs), - std::ref(suit_splits), std::ref(SuitRanks), start_id, - end_id); - }); - } - for (int i = 0; i < num_threads; ++i) { - threads[i].join(); - } - return outTTable; -} - -bool TestRetroSolve(int samples, int depth, uint32_t seed, - const std::vector>& bin_coeffs, - const uint32_t hard_threads) { - // Tests endgame solution with TTable vs raw seach - std::vector nodes = GWhistGenerator(samples, seed); - vectorNa v; - for (int i = 1; i <= depth; ++i) { - v = RetroSolver(i, &v, bin_coeffs, hard_threads); - } - std::unordered_map SuitRanks; - GenSuitRankingsRel(depth, &SuitRanks); - for (auto it = nodes.begin(); it != nodes.end(); ++it) { - char abm_unsafe = IncrementalAlphaBetaMemoryIso(&*it, 0, kNumRanks, - 2 * (kNumRanks - depth), &v, - &SuitRanks, bin_coeffs); - char abm_safe = AlphaBeta(&*it, 0, kNumRanks); - if (abm_unsafe != abm_safe) { - return false; - } - } - return true; -} - -vectorNa BuildTablebase(const std::vector>& bin_coeffs, - uint32_t hard_threads) { - vectorNa v; - std::cout << "Building Tablebase" - << "\n"; - for (int i = 1; i <= kNumRanks; ++i) { - v = RetroSolver(i, &v, bin_coeffs, hard_threads); - std::cout << "Done " << i << "\n"; - } - std::cout << "Built Tablebase" - << "\n"; - return v; -} - -bool TestTablebase(int samples, uint32_t seed, const vectorNa& table_base, - const std::vector>& bin_coeffs) { - std::vector nodes = GWhistGenerator(samples, seed); - std::unordered_map SuitRanks; - GenSuitRankingsRel(kNumRanks, &SuitRanks); - for (auto it = nodes.begin(); it != nodes.end(); ++it) { - char abm_unsafe = IncrementalAlphaBetaMemoryIso( - &*it, 0, kNumRanks, 0, &table_base, &SuitRanks, bin_coeffs); - char abm_safe = AlphaBeta(&*it, 0, kNumRanks); - if (abm_unsafe != abm_safe) { - return false; - } - } - return true; -} - -void StoreTTable(const std::string& filename, const vectorNa& solution) { - // stores solution into a text file// - std::ofstream file(filename); - for (int i = 0; i < solution.GetOuterSize(); ++i) { - for (int j = 0; j < solution.GetInnerSize(); ++j) { - file.put(solution.GetChar(i, j)); - } - } - file.close(); -} - -bool TestTTableStorage(std::string filename, const vectorNa& v, int depth, - const std::vector>& bin_coeffs) { - // Tests storage fidelity// - StoreTTable(filename, v); - vectorNa new_v = LoadTTable(filename, depth, bin_coeffs); - for (int i = 0; i < v.GetOuterSize(); ++i) { - for (int j = 0; j < v.GetInnerSize(); ++j) { - if (v.GetChar(i, j) != new_v.GetChar(i, j)) { - return false; - } - } - } - return true; -} - -} // namespace german_whist_foregame -} // namespace open_spiel diff --git a/open_spiel/games/german_whist_foregame/german_whist_foregame.cc b/open_spiel/games/german_whist_foregame/german_whist_foregame.cc deleted file mode 100644 index 897bf2bae4..0000000000 --- a/open_spiel/games/german_whist_foregame/german_whist_foregame.cc +++ /dev/null @@ -1,721 +0,0 @@ -// Copyright 2024 DeepMind Technologies Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "open_spiel/games/german_whist_foregame/german_whist_foregame.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "open_spiel/game_parameters.h" -#include "open_spiel/observer.h" -#include "open_spiel/spiel.h" -#include "open_spiel/spiel_globals.h" -#include "open_spiel/spiel_utils.h" -// define BMI2 only if your system supports BMI2 intrinsics, modify compiler -// flags so that bmi2 instructions are compiled// #define __BMI2__ -#ifdef __BMI2__ -#include -#endif -namespace open_spiel { -namespace german_whist_foregame { - -// set this to the path you expect TTable to be once you have made it so -// recompilation is not necessary// -const char* kTTablePath = ""; - -uint32_t tzcnt_u32(uint32_t a) { return __builtin_ctz(a); } -uint64_t tzcnt_u64(uint64_t a) { return __builtin_ctzll(a); } -uint32_t bzhi_u32(uint32_t a, uint32_t b) { return a & ((1u << b) - 1); } -uint64_t bzhi_u64(uint64_t a, uint64_t b) { return a & ((1ULL << b) - 1); } -uint32_t blsr_u32(uint32_t a) { return (a - 1) & a; } -uint64_t blsr_u64(uint64_t a) { return (a - 1) & a; } -uint32_t popcnt_u32(uint32_t a) { return __builtin_popcount(a); } -uint64_t popcnt_u64(uint64_t a) { return __builtin_popcountll(a); } -// the pext bithack is a lot slower than the bmi2 intrinsic, and even with bmi2 -// support enabled this will not compile down to a pext instruction// -uint64_t pext_u64(uint64_t x, uint64_t m) { -#ifdef __BMI2__ - return _pext_u64(x, m); -#endif -#ifndef __BMI2__ - uint64_t r = 0; - uint64_t s = 0; - uint64_t b = 0; - do { - b = m & 1; - r = r | ((x & b) << s); - s = s + b; - x = x >> 1; - m = m >> 1; - } while (m != 0); - return r; -#endif -} - -bool Triple::operator<(const Triple& triple) const { - return (length < triple.length) || - (length == triple.length && sig < triple.sig); -} - -inline int CardRank(int card, int suit) { - uint64_t card_mask = ((uint64_t)1 << card); - card_mask = (card_mask >> (suit * kNumRanks)); - return tzcnt_u64(card_mask); -} -inline int CardSuit(int card) { - uint64_t card_mask = ((uint64_t)1 << card); - for (int i = 0; i < kNumSuits; ++i) { - if (popcnt_u64(card_mask & kSuitMasks[i]) == 1) { - return i; - } - } - return kNumSuits; -} -std::string CardString(int card) { - int suit = CardSuit(card); - return {kSuitChar[suit], kRankChar[CardRank(card, suit)]}; -} - -std::vector GenQuads(int size_endgames) { - // Generates Suit splittings for endgames of a certain size// - std::vector v; - for (char i = 0; i <= std::min(size_endgames * 2, kNumRanks); ++i) { - int sum = size_endgames * 2 - i; - for (char j = 0; j <= std::min(sum, kNumRanks); ++j) { - for (char k = std::max((int)j, sum - j - kNumRanks); - k <= std::min(sum - j, kNumRanks); ++k) { - char l = sum - j - k; - if (l < k) { - break; - } else { - uint32_t num = 0; - num = num | (i); - num = num | (j << 4); - num = num | (k << 8); - num = num | (l << 12); - v.push_back(num); - } - } - } - } - return v; -} -std::vector> BinCoeffs(uint32_t max_n) { - // tabulates binomial coefficients// - std::vector> C(max_n + 1, - std::vector(max_n + 1)); - for (uint32_t i = 1; i <= max_n; ++i) { - C[0][i] = 0; - } - for (uint32_t i = 0; i <= max_n; ++i) { - C[i][0] = 1; - } - for (uint32_t i = 1; i <= max_n; ++i) { - for (uint32_t j = 1; j <= max_n; ++j) { - C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; - } - } - return C; -} -uint32_t HalfColexer(uint32_t cards, - const std::vector>* bin_coeffs) { - // returns the colexicographical ranking of a combination of indices where the - // the size of the combination is half that of the set of indices// - uint32_t out = 0; - uint32_t count = 0; - while (cards != 0) { - uint32_t ind = tzcnt_u32(cards); - uint32_t val = bin_coeffs->at(ind)[count + 1]; - out += val; - cards = blsr_u32(cards); - count++; - } - return out; -} -void GenSuitRankingsRel(uint32_t size, - std::unordered_map* Ranks) { - // Generates ranking Table for suit splittings for endgames of a certain - // size// - std::vector v = GenQuads(size); - for (uint32_t i = 0; i < v.size(); ++i) { - Ranks->insert({v[i], i}); - } -} - -vectorNa::vectorNa(size_t card_combs, size_t suit_splits, char val) { - data = std::vector(card_combs * ((suit_splits >> 1) + 1), val); - inner_size = (suit_splits >> 1) + 1; - outer_size = card_combs; -} -vectorNa::vectorNa() { - data = {}; - inner_size = 0; - outer_size = 0; -} -size_t vectorNa::size() const { return data.size(); } -size_t vectorNa::GetInnerSize() const { return inner_size; } -size_t vectorNa::GetOuterSize() const { return outer_size; } -char const& vectorNa::operator[](size_t index) const { return data[index]; } -char vectorNa::GetChar(size_t i, size_t j) const { - return data[i * inner_size + j]; -} -void vectorNa::SetChar(size_t i, size_t j, char value) { - data[i * inner_size + j] = value; -} -char vectorNa::Get(size_t i, size_t j) const { - int remainder = j & 0b1; - if (remainder == 0) { - return 0b1111 & data[i * inner_size + (j >> 1)]; - } else { - return ((0b11110000 & data[i * inner_size + (j >> 1)]) >> 4); - } -} -void vectorNa::Set(size_t i, size_t j, char value) { - int remainder = j & 0b1; - if (remainder == 0) { - char datastore = 0b11110000 & data[i * inner_size + (j >> 1)]; - data[i * inner_size + (j >> 1)] = datastore | value; - } else { - char datastore = (0b1111 & data[i * inner_size + (j >> 1)]); - data[i * inner_size + (j >> 1)] = datastore | (value << 4); - } -} -vectorNa InitialiseTTable( - int size, const std::vector>& bin_coeffs) { - // initialises TTable for a certain depth// - size_t suit_size = GenQuads(size).size(); - return vectorNa(bin_coeffs[2 * size][size], suit_size, 0); -} -vectorNa LoadTTable(const std::string filename, int depth, - const std::vector>& bin_coeffs) { - // loads solution from a text file into a vector for use// - std::cout << "Loading Tablebase" - << "\n"; - vectorNa v = InitialiseTTable(depth, bin_coeffs); - std::ifstream file(filename, std::ios::binary); - if (!file.is_open()) { - std::cout << "Failed to load Tablebase" - << "\n"; - std::cout << "Tablebase will be set to all 0" - << "\n"; - file.close(); - return v; - } else { - char c; - for (int i = 0; i < v.GetOuterSize(); ++i) { - for (int j = 0; j < v.GetInnerSize(); ++j) { - file.get(c); - v.SetChar(i, j, c); - } - } - file.close(); - std::cout << "Tablebase Loaded" - << "\n"; - return v; - } -} - -// Default parameters. - -namespace { // namespace -// Facts about the game -const GameType kGameType{ - /*short_name=*/"german_whist_foregame", - /*long_name=*/"german_whist_foregame", - GameType::Dynamics::kSequential, - GameType::ChanceMode::kExplicitStochastic, - GameType::Information::kImperfectInformation, - GameType::Utility::kZeroSum, - GameType::RewardModel::kTerminal, - /*max_num_players=*/2, - /*min_num_players=*/2, - /*provides_information_state_string=*/true, - /*provides_information_state_tensor=*/false, - /*provides_observation_string=*/true, - /*provides_observation_tensor=*/false, -}; - -std::shared_ptr Factory(const GameParameters& params) { - return std::shared_ptr(new GWhistFGame(params)); -} - -REGISTER_SPIEL_GAME(kGameType, Factory); -} // namespace - -GWhistFGame::GWhistFGame(const GameParameters& params) - : Game(kGameType, params) { - bin_coeffs_ = BinCoeffs(2 * kNumRanks); - std::unordered_map temp; - GenSuitRankingsRel(13, &temp); - suit_ranks_ = temp; - ttable_ = LoadTTable(kTTablePath, 13, bin_coeffs_); -}; -std::unique_ptr GWhistFGame::NewInitialState() const { - const auto ptr = - std::dynamic_pointer_cast(shared_from_this()); - return std::make_unique(ptr); -} - -GWhistFState::GWhistFState(std::shared_ptr game) - : State(game) { - player_ = kChancePlayerId; - move_number_ = 0; - trump_ = -1; - deck_ = bzhi_u64(~0, kNumRanks * kNumSuits); - discard_ = 0; - hands_ = {0, 0}; - history_.reserve(78); - ttable_ = &(game->ttable_); - suit_ranks_ = &(game->suit_ranks_); - bin_coeffs_ = &(game->bin_coeffs_); -} -bool GWhistFState::Trick(int lead, int follow) const { - int lead_suit = CardSuit(lead); - int follow_suit = CardSuit(follow); - int lead_rank = CardRank(lead, lead_suit); - int follow_rank = CardRank(follow, follow_suit); - return (lead_suit == follow_suit && lead_rank < follow_rank) || - (lead_suit != follow_suit && follow_suit != trump_); -} -bool GWhistFState::IsTerminal() const { return (popcnt_u64(deck_) == 0); } -uint64_t GWhistFState::EndgameKey(int player_to_move) const { - // generates a 64 bit unsigned int where the first 32 are the suit ownerships - // from the perspective of the opponent using canonical rankings// example: if - // Spade suit is to_move = A3, opp =2, suit = 0b100 least significant part of - // first 32 bits is the trump suit, then the remaining suits ascending length - // order. - uint64_t cards_in_play = hands_[0] | hands_[1]; - std::vector suit_lengths = {}; - int opp = (player_to_move == 0) ? 1 : 0; - // sort trump suits by length,then sig// - for (int i = 0; i < kNumSuits; ++i) { - if (i != trump_) { - uint64_t sig = - pext_u64(hands_[opp] & kSuitMasks[i], cards_in_play & kSuitMasks[i]); - suit_lengths.push_back( - Triple{static_cast(i), - static_cast(popcnt_u64(kSuitMasks[i] & cards_in_play)), - static_cast(sig)}); - } - } - std::sort(suit_lengths.begin(), suit_lengths.end()); - std::array hand0; - std::array hand1; - hand0[0] = pext_u64(hands_[0], kSuitMasks[trump_]); - hand1[0] = pext_u64(hands_[1], kSuitMasks[trump_]); - for (int i = 0; i < kNumSuits - 1; ++i) { - hand0[i + 1] = pext_u64(hands_[0], kSuitMasks[suit_lengths[i].index]); - hand1[i + 1] = pext_u64(hands_[1], kSuitMasks[suit_lengths[i].index]); - } - std::array hands_shuffled = {0, 0}; - for (int i = 0; i < kNumSuits; ++i) { - hands_shuffled[0] = hands_shuffled[0] | (hand0[i] << (kNumRanks * i)); - hands_shuffled[1] = hands_shuffled[1] | (hand1[i] << (kNumRanks * i)); - } - uint64_t suit_sig = 0; - suit_sig = popcnt_u64(kSuitMasks[trump_] & cards_in_play); - for (int i = 0; i < kNumSuits - 1; ++i) { - suit_sig = suit_sig | ((uint64_t)suit_lengths[i].length << (4 * (i + 1))); - } - suit_sig = (suit_sig << 32); - cards_in_play = hands_shuffled[0] | hands_shuffled[1]; - uint64_t cards = pext_u64(hands_shuffled[opp], cards_in_play); - uint64_t key = cards | suit_sig; - return key; -} -std::vector GWhistFState::Returns() const { - if (IsTerminal()) { - std::vector out = {0, 0}; - int lead_win = Trick(history_[move_number_ - 3].action, - history_[move_number_ - 2].action); - int player_to_move = (lead_win) ? history_[move_number_ - 3].player - : history_[move_number_ - 2].player; - int opp = (player_to_move == 0) ? 1 : 0; - uint64_t key = EndgameKey(player_to_move); - uint32_t cards = (key & bzhi_u64(~0, 32)); - uint32_t colex = HalfColexer(cards, bin_coeffs_); - uint32_t suits = (key & (~0 ^ bzhi_u64(~0, 32))) >> 32; - uint32_t suit_rank = suit_ranks_->at(suits); - char value = ttable_->Get(colex, suit_rank); - out[player_to_move] = 2 * value - kNumRanks; - out[opp] = -out[player_to_move]; - return out; - } else { - std::vector out = {0, 0}; - return out; - } -} - -int GWhistFState::CurrentPlayer() const { return player_; } - -std::vector> GWhistFState::ChanceOutcomes() const { - std::vector> outcomes; - std::vector legal_actions = LegalActions(); - for (int i = 0; i < legal_actions.size(); ++i) { - std::pair pair; - pair.first = legal_actions[i]; - pair.second = 1.0 / legal_actions.size(); - outcomes.push_back(pair); - } - return outcomes; -} -std::string GWhistFState::ActionToString(Player player, Action move) const { - return CardString(move); -} -std::string GWhistFState::ToString() const { - std::string out; - for (int i = 0; i < history_.size(); ++i) { - out += ActionToString(history_[i].player, history_[i].action); - out += "\n"; - } - return out; -} -std::unique_ptr GWhistFState::Clone() const { - return std::unique_ptr(new GWhistFState(*this)); -} - -std::string GWhistFState::StateToString() const { - // doesnt use history in case of a resampled state with unreconciled history// - std::string out; - uint64_t copy_deck = deck_; - uint64_t copy_discard = discard_; - std::array copy_hands = hands_; - std::vector deck_cards; - std::vector player0_cards; - std::vector player1_cards; - std::vector discard; - while (copy_deck != 0) { - deck_cards.push_back(tzcnt_u64(copy_deck)); - copy_deck = blsr_u64(copy_deck); - } - while (copy_discard != 0) { - discard.push_back(tzcnt_u64(copy_discard)); - copy_discard = blsr_u64(copy_discard); - } - - while (copy_hands[0] != 0) { - player0_cards.push_back(tzcnt_u64(copy_hands[0])); - copy_hands[0] = blsr_u64(copy_hands[0]); - } - while (copy_hands[1] != 0) { - player1_cards.push_back(tzcnt_u64(copy_hands[1])); - copy_hands[1] = blsr_u64(copy_hands[1]); - } - out += "Deck \n"; - for (int i = 0; i < deck_cards.size(); ++i) { - out += CardString(deck_cards[i]) + "\n"; - } - out += "Discard \n"; - for (int i = 0; i < discard.size(); ++i) { - out += CardString(discard[i]) + "\n"; - } - - for (int i = 0; i < 2; ++i) { - out += "Player " + std::to_string(i) + "\n"; - std::vector var; - if (i == 0) { - var = player0_cards; - } else { - var = player1_cards; - } - for (int j = 0; j < var.size(); ++j) { - out += CardString(var[j]) + "\n"; - } - } - return out; -} -std::string GWhistFState::InformationStateString(Player player) const { - // THIS IS WHAT A PLAYER IS SHOWN WHEN PLAYING// - SPIEL_CHECK_TRUE(player >= 0 && player < 2); - std::string p = std::to_string(player) + ","; - std::string cur_hand = ""; - std::string observations = ""; - std::vector v_hand = {}; - uint64_t p_hand = hands_[player]; - while (p_hand != 0) { - v_hand.push_back(tzcnt_u64(p_hand)); - p_hand = blsr_u64(p_hand); - } - std::sort(v_hand.begin(), v_hand.end()); - for (int i = 0; i < v_hand.size(); ++i) { - cur_hand = cur_hand + CardString(v_hand[i]); - cur_hand = cur_hand + ","; - } - cur_hand += "\n"; - for (int i = 2 * kNumRanks; i < history_.size(); ++i) { - int index = (i - 2 * kNumRanks) % 4; - switch (index) { - case 0: - observations = - observations + "c_public:" + CardString(history_[i].action) + ","; - break; - case 1: - observations = observations + "p" + std::to_string(history_[i].player) + - ":" + CardString(history_[i].action) + ","; - break; - case 2: - observations = observations + "p" + std::to_string(history_[i].player) + - ":" + CardString(history_[i].action) + ","; - break; - case 3: - int lead_win = Trick(history_[i - 2].action, history_[i - 1].action); - int loser = ((lead_win) ^ (history_[i - 2].player == 0)) ? 0 : 1; - if (loser == player) { - observations = observations + - "c_observed:" + CardString(history_[i].action) + "\n"; - } else { - observations = observations + "c_unobserved:" + "\n"; - } - break; - } - } - return p + cur_hand + observations; -} -std::unique_ptr GWhistFState::ResampleFromInfostate( - int player_id, std::function rng) const { - // only valid when called from a position where a player can act// - auto resampled_state = std::unique_ptr(new GWhistFState(*this)); - // seeding mt19937// - std::random_device rd; - std::mt19937 gen(rd()); - uint64_t necessary_cards = 0; - for (int i = 2 * kNumRanks; i < history_.size(); i += 4) { - // face up cards from deck// - necessary_cards = - (necessary_cards | static_cast(1) << history_[i].action); - } - int move_index = move_number_ - ((kNumRanks * kNumSuits) / 2); - int move_remainder = move_index % 4; - int opp = (player_id == 0) ? 1 : 0; - int recent_faceup = move_number_ - move_remainder; - uint64_t recent_faceup_card = - (static_cast(1) << history_[recent_faceup].action); - // if a face up card from the deck is not in players hand or discard it must - // be in opps unless it is the most recent face up// - necessary_cards = (necessary_cards & - (~(hands_[player_id] | discard_ | recent_faceup_card))); - // sufficient cards are all cards not in players hand,the discard, or the - // recent face up// - uint64_t sufficient_cards = - (bzhi_u64(~0, kNumRanks * kNumSuits) ^ - (hands_[player_id] | discard_ | recent_faceup_card)); - // sufficient_cards are not necessary // - sufficient_cards = (sufficient_cards & (~(necessary_cards))); - // we must now take into account the observation of voids// - std::array when_voided = {0, 0, 0, 0}; - std::array voids = {-1, -1, -1, -1}; - std::vector opp_dealt_hidden; - for (int i = 2 * kNumRanks; i < history_.size(); ++i) { - if (history_[i - 1].player == player_id && history_[i].player == (opp) && - CardSuit(history_[i - 1].action) != CardSuit(history_[i].action)) { - when_voided[CardSuit(history_[i - 1].action)] = i - 1; - } - if (history_[i - 1].player == player_id && history_[i].player == (opp) && - Trick(history_[i - 1].action, history_[i].action)) { - opp_dealt_hidden.push_back(i - 1); - } - if (history_[i - 1].player == (opp) && history_[i].player == (player_id) && - !Trick(history_[i - 1].action, history_[i].action)) { - opp_dealt_hidden.push_back(i - 1); - } - } - // now voids contains the number of hidden cards dealt to opp since it showed - // a void in that suit, i.e the maximum number of cards held in that suit// if - // the suit is unvoided, then this number is -1// - for (int i = 0; i < kNumSuits; ++i) { - if (when_voided[i] != 0) { - voids[i] = 0; - for (int j = 0; j < opp_dealt_hidden.size(); ++j) { - if (opp_dealt_hidden[j] >= when_voided[i]) { - voids[i] += 1; - } - } - } - } - // we now perform a sequence of shuffles to generate a possible opponent hand, - // and make no attempt to reconcile the history with this new deal// - int nec = popcnt_u64(necessary_cards); - for (int i = 0; i < kNumSuits; ++i) { - if (voids[i] != -1 && - popcnt_u64(sufficient_cards & kSuitMasks[i]) > voids[i]) { - uint64_t suit_subset = (sufficient_cards & kSuitMasks[i]); - std::vector temp; - while (suit_subset != 0) { - temp.push_back(tzcnt_u64(suit_subset)); - suit_subset = blsr_u64(suit_subset); - } - std::shuffle(temp.begin(), temp.end(), gen); - sufficient_cards = (sufficient_cards & ~(kSuitMasks[i])); - for (int j = 0; j < voids[i]; ++j) { - sufficient_cards = - (sufficient_cards | static_cast(1) << temp[j]); - } - } - } - // finally generating a possible hand for opponent// - std::vector hand_vec; - while (sufficient_cards != 0) { - hand_vec.push_back(tzcnt_u64(sufficient_cards)); - sufficient_cards = blsr_u64(sufficient_cards); - } - std::shuffle(hand_vec.begin(), hand_vec.end(), gen); - uint64_t suff_hand = 0; - uint64_t opp_hand = 0; - for (int i = 0; i < popcnt_u64(hands_[opp]) - nec; ++i) { - suff_hand = suff_hand | (static_cast(1) << hand_vec[i]); - } - opp_hand = suff_hand | necessary_cards; - resampled_state->hands_[opp] = opp_hand; - resampled_state->deck_ = - bzhi_u64(~0, kNumRanks * kNumSuits) ^ - (discard_ | opp_hand | hands_[player_id] | recent_faceup_card); - return resampled_state; -} -std::string GWhistFState::ObservationString(Player player) const { - // note this is a lie, this is not the observation state string but it is used - // for ISMCTS to label nodes// - SPIEL_CHECK_TRUE(player >= 0 && player < 2); - std::string p = "p" + std::to_string(player) + ","; - std::string cur_hand = ""; - std::string public_info = ""; - uint64_t p_hand = hands_[player]; - std::vector v_hand = {}; - while (p_hand != 0) { - v_hand.push_back(tzcnt_u64(p_hand)); - p_hand = blsr_u64(p_hand); - } - std::sort(v_hand.begin(), v_hand.end()); - for (int i = 0; i < v_hand.size(); ++i) { - cur_hand = cur_hand + CardString(v_hand[i]) + ","; - } - for (int i = 2 * kNumRanks; i < history_.size(); ++i) { - int index = (i - 2 * kNumRanks) % 4; - if (index != 3) { - public_info = public_info + std::to_string(history_[i].player) + ":" + - CardString(history_[i].action) + ","; - } - } - return p + cur_hand + public_info; -} - -std::vector GWhistFState::LegalActions() const { - std::vector actions; - if (IsTerminal()) return {}; - if (IsChanceNode()) { - actions.reserve(popcnt_u64(deck_)); - uint64_t copy_deck = deck_; - while (copy_deck != 0) { - actions.push_back(tzcnt_u64(copy_deck)); - copy_deck = blsr_u64(copy_deck); - } - } else { - // lead// - actions.reserve(kNumRanks); - if (history_.back().player == kChancePlayerId) { - uint64_t copy_hand = hands_[player_]; - while (copy_hand != 0) { - actions.push_back(tzcnt_u64(copy_hand)); - copy_hand = blsr_u64(copy_hand); - } - } else { - // follow // - uint64_t copy_hand = - hands_[player_] & kSuitMasks[CardSuit(history_.back().action)]; - if (copy_hand == 0) { - copy_hand = hands_[player_]; - } - while (copy_hand != 0) { - actions.push_back(tzcnt_u64(copy_hand)); - copy_hand = blsr_u64(copy_hand); - } - } - } - return actions; -} - -void GWhistFState::DoApplyAction(Action move) { - // initial deal// - if (move_number_ < (kNumSuits * kNumRanks) / 2) { - hands_[move_number_ % 2] = - (hands_[move_number_ % 2] | ((uint64_t)1 << move)); - deck_ = (deck_ ^ ((uint64_t)1 << move)); - } else if (move_number_ == (kNumSuits * kNumRanks / 2)) { - trump_ = CardSuit(move); - deck_ = (deck_ ^ ((uint64_t)1 << move)); - player_ = 0; - } else if (move_number_ > (kNumSuits * kNumRanks) / 2) { - // cardplay // - int move_index = (move_number_ - ((kNumSuits * kNumRanks) / 2)) % 4; - bool lead_win; - int winner; - int loser; - switch (move_index) { - case 0: - // revealing face up card// - deck_ = (deck_ ^ ((uint64_t)1 << move)); - lead_win = Trick(history_[move_number_ - 3].action, - history_[move_number_ - 2].action); - winner = - ((lead_win) ^ (history_[move_number_ - 3].player == 0)) ? 1 : 0; - player_ = winner; - break; - case 1: - // establishing lead// - discard_ = (discard_ | ((uint64_t)1 << move)); - hands_[player_] = (hands_[player_] ^ ((uint64_t)1 << move)); - (player_ == 0) ? player_ = 1 : player_ = 0; - break; - case 2: - // following and awarding face up// - discard_ = (discard_ | ((uint64_t)1 << move)); - hands_[player_] = (hands_[player_] ^ ((uint64_t)1 << move)); - lead_win = Trick(history_[move_number_ - 1].action, move); - winner = - ((lead_win) ^ (history_[move_number_ - 1].player == 0)) ? 1 : 0; - hands_[winner] = (hands_[winner] | - ((uint64_t)1 << history_[move_number_ - 2].action)); - player_ = kChancePlayerId; - break; - case 3: - // awarding face down// - deck_ = (deck_ ^ ((uint64_t)1 << move)); - lead_win = Trick(history_[move_number_ - 2].action, - history_[move_number_ - 1].action); - loser = ((lead_win) ^ (history_[move_number_ - 2].player == 0)) ? 0 : 1; - hands_[loser] = (hands_[loser] | ((uint64_t)1 << move)); - if (IsTerminal()) { - player_ = kTerminalPlayerId; - } - break; - } - } -#ifdef DEBUG - std::cout << ActionToString(player_start, move) << std::endl; - std::cout << move << std::endl; -#endif -} - -} // namespace german_whist_foregame -} // namespace open_spiel diff --git a/open_spiel/games/german_whist_foregame/german_whist_foregame.h b/open_spiel/games/german_whist_foregame/german_whist_foregame.h deleted file mode 100644 index 5bb1a22c9b..0000000000 --- a/open_spiel/games/german_whist_foregame/german_whist_foregame.h +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2024 DeepMind Technologies Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OPEN_SPIEL_GAMES_GERMAN_WHIST_FOREGAME_H -#define OPEN_SPIEL_GAMES_GERMAN_WHIST_FOREGAME_H - -#include -#include -#include -#include -#include -#include -#include - -#include "open_spiel/abseil-cpp/absl/types/optional.h" -#include "open_spiel/spiel.h" -#include "open_spiel/spiel_utils.h" - -// The imperfect information part of 2 player whist variant -// https://en.wikipedia.org/wiki/German_Whist - -namespace open_spiel { -namespace german_whist_foregame { - -class GWhistFGame; -class GWhistFObserver; - -inline constexpr int kNumRanks = 13; -inline constexpr int kNumSuits = 4; -inline constexpr char kRankChar[] = "AKQJT98765432"; -inline constexpr char kSuitChar[] = "CDHS"; - -extern const char* kTTablePath; - -// Reimplementing bmi2 intrinsics with bit operations that will work on all -// platforms// -uint32_t tzcnt_u32(uint32_t a); -uint64_t tzcnt_u64(uint64_t a); -uint32_t bzhi_u32(uint32_t a, uint32_t b); -uint64_t bzhi_u64(uint64_t a, uint64_t b); -uint32_t blsr_u32(uint32_t a); -uint64_t blsr_u64(uint64_t a); -uint32_t popcnt_u32(uint32_t a); -uint64_t popcnt_u64(uint64_t a); -uint64_t pext_u64(uint64_t x, uint64_t m); - -// containers of cards are 64 bits,with the least significant 52 bits being the -// suits CDHS,with the least sig bit of each suit being the highest rank card// -// this container of masks is used to extract only the cards from a suit// -inline const std::array kSuitMasks = { - bzhi_u64(~0, kNumRanks), - bzhi_u64(~0, 2 * kNumRanks) ^ bzhi_u64(~0, kNumRanks), - bzhi_u64(~0, 3 * kNumRanks) ^ bzhi_u64(~0, 2 * kNumRanks), - bzhi_u64(~0, 4 * kNumRanks) ^ bzhi_u64(~0, 3 * kNumRanks)}; - -struct Triple { - char index; - char length; - uint32_t sig; - bool operator<(const Triple& triple) const; -}; -std::vector GenQuads(int size_endgames); -std::vector> BinCoeffs(uint32_t max_n); -uint32_t HalfColexer(uint32_t cards, - const std::vector>* bin_coeffs); -void GenSuitRankingsRel(uint32_t size, - std::unordered_map* Ranks); -class vectorNa { - private: - std::vector data; - size_t inner_size; - size_t outer_size; - - public: - vectorNa(size_t card_combs, size_t suit_splits, char val); - vectorNa(); - size_t size() const; - size_t GetInnerSize() const; - size_t GetOuterSize() const; - char const& operator[](size_t index) const; - char GetChar(size_t i, size_t j) const; - void SetChar(size_t i, size_t j, char value); - char Get(size_t i, size_t j) const; - void Set(size_t i, size_t j, char value); -}; - -vectorNa InitialiseTTable(int size, - const std::vector>& bin_coeffs); -vectorNa LoadTTable(const std::string filename, int depth, - const std::vector>& bin_coeffs); -vectorNa BuildTablebase(const std::vector>& bin_coeffs, - uint32_t hard_threads); -bool TestTablebase(int samples, uint32_t seed, const vectorNa& table_base, - const std::vector>& bin_coeffs); -void StoreTTable(const std::string& filename, const vectorNa& solution); - -class GWhistFGame : public Game { - public: - explicit GWhistFGame(const GameParameters& params); - int NumDistinctActions() const override { return kNumRanks * kNumSuits; } - std::unique_ptr NewInitialState() const override; - int MaxChanceOutcomes() const override { return kNumRanks * kNumSuits; } - int NumPlayers() const override { return num_players_; } - double MinUtility() const override { return -kNumRanks; }; - double MaxUtility() const override { return kNumRanks; }; - absl::optional UtilitySum() const override { return 0; }; - int MaxGameLength() const override { return kNumRanks * (kNumSuits + 2); }; - int MaxChanceNodesInHistory() const override { - return kNumRanks * kNumSuits; - }; - vectorNa ttable_; - std::unordered_map suit_ranks_; - std::vector> bin_coeffs_; - - private: - // Number of players. - int num_players_ = 2; -}; -class GWhistFState : public State { - public: - explicit GWhistFState(std::shared_ptr game); - GWhistFState(const GWhistFState&) = default; - Player CurrentPlayer() const override; - std::string ActionToString(Player player, Action move) const override; - std::string ToString() const override; - bool IsTerminal() const override; - std::vector Returns() const override; - std::unique_ptr Clone() const override; - ActionsAndProbs ChanceOutcomes() const override; - std::vector LegalActions() const override; - std::string InformationStateString(Player player) const override; - std::string ObservationString(Player player) const override; - std::unique_ptr ResampleFromInfostate( - int player_id, std::function rng) const override; - std::string StateToString() const; - uint64_t EndgameKey(int player_to_move) const; - - protected: - void DoApplyAction(Action move) override; - - private: - uint64_t deck_; - uint64_t discard_; - const vectorNa* ttable_; - const std::unordered_map* suit_ranks_; - const std::vector>* bin_coeffs_; - std::array hands_; - int player_; - int trump_; - bool Trick(int lead, int follow) const; -}; - -} // namespace german_whist_foregame -} // namespace open_spiel - -#endif // OPEN_SPIEL_GAMES_GERMAN_WHIST_FOREGAME_H diff --git a/open_spiel/games/german_whist_foregame/german_whist_foregame_test.cc b/open_spiel/games/german_whist_foregame/german_whist_foregame_test.cc deleted file mode 100644 index d28227d13a..0000000000 --- a/open_spiel/games/german_whist_foregame/german_whist_foregame_test.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2024 DeepMind Technologies Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "open_spiel/spiel.h" -#include "open_spiel/tests/basic_tests.h" - -namespace open_spiel { -namespace german_whist_foregame { -namespace { - -namespace testing = open_spiel::testing; - -void BasicGermanWhistForegameTests() { - testing::LoadGameTest("german_whist_foregame"); - testing::RandomSimTest(*LoadGame("german_whist_foregame"), 100, false, true); -} - -} // namespace -} // namespace german_whist_foregame -} // namespace open_spiel - -int main(int argc, char **argv) { - open_spiel::german_whist_foregame::BasicGermanWhistForegameTests(); - // open_spiel::testing::ResampleInfostateTest(*open_spiel::LoadGame("german_whist_foregame"),*num_sims=*10); -} diff --git a/open_spiel/integration_tests/playthroughs/german_whist_foregame.txt b/open_spiel/integration_tests/playthroughs/german_whist_foregame.txt deleted file mode 100644 index 2ddb6973f8..0000000000 --- a/open_spiel/integration_tests/playthroughs/german_whist_foregame.txt +++ /dev/null @@ -1,905 +0,0 @@ -game: german_whist_foregame - -GameType.chance_mode = ChanceMode.EXPLICIT_STOCHASTIC -GameType.dynamics = Dynamics.SEQUENTIAL -GameType.information = Information.IMPERFECT_INFORMATION -GameType.long_name = "german_whist_foregame" -GameType.max_num_players = 2 -GameType.min_num_players = 2 -GameType.parameter_specification = [] -GameType.provides_information_state_string = True -GameType.provides_information_state_tensor = False -GameType.provides_observation_string = True -GameType.provides_observation_tensor = False -GameType.provides_factored_observation_string = False -GameType.reward_model = RewardModel.TERMINAL -GameType.short_name = "german_whist_foregame" -GameType.utility = Utility.ZERO_SUM - -NumDistinctActions() = 52 -PolicyTensorShape() = [52] -MaxChanceOutcomes() = 52 -GetParameters() = {} -NumPlayers() = 2 -MinUtility() = -13.0 -MaxUtility() = 13.0 -UtilitySum() = 0.0 -MaxGameLength() = 78 -ToString() = "german_whist_foregame()" - -# State 0 -IsTerminal() = False -History() = [] -HistoryString() = "" -IsChanceNode() = True -IsSimultaneousNode() = False -CurrentPlayer() = -1 -InformationStateString(0) = "0,\n" -InformationStateString(1) = "1,\n" -ObservationString(0) = "p0," -ObservationString(1) = "p1," -ChanceOutcomes() = [(0,0.0192308), (1,0.0192308), (2,0.0192308), (3,0.0192308), (4,0.0192308), (5,0.0192308), (6,0.0192308), (7,0.0192308), (8,0.0192308), (9,0.0192308), (10,0.0192308), (11,0.0192308), (12,0.0192308), (13,0.0192308), (14,0.0192308), (15,0.0192308), (16,0.0192308), (17,0.0192308), (18,0.0192308), (19,0.0192308), (20,0.0192308), (21,0.0192308), (22,0.0192308), (23,0.0192308), (24,0.0192308), (25,0.0192308), (26,0.0192308), (27,0.0192308), (28,0.0192308), (29,0.0192308), (30,0.0192308), (31,0.0192308), (32,0.0192308), (33,0.0192308), (34,0.0192308), (35,0.0192308), (36,0.0192308), (37,0.0192308), (38,0.0192308), (39,0.0192308), (40,0.0192308), (41,0.0192308), (42,0.0192308), (43,0.0192308), (44,0.0192308), (45,0.0192308), (46,0.0192308), (47,0.0192308), (48,0.0192308), (49,0.0192308), (50,0.0192308), (51,0.0192308)] -LegalActions() = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51] -StringLegalActions() = ["CA", "CK", "CQ", "CJ", "CT", "C9", "C8", "C7", "C6", "C5", "C4", "C3", "C2", "DA", "DK", "DQ", "DJ", "DT", "D9", "D8", "D7", "D6", "D5", "D4", "D3", "D2", "HA", "HK", "HQ", "HJ", "HT", "H9", "H8", "H7", "H6", "H5", "H4", "H3", "H2", "SA", "SK", "SQ", "SJ", "ST", "S9", "S8", "S7", "S6", "S5", "S4", "S3", "S2"] - -# Apply action "H4" -action: 36 - -# State 1 -# H4 -IsTerminal() = False -History() = [36] -HistoryString() = "36" -IsChanceNode() = True -IsSimultaneousNode() = False -CurrentPlayer() = -1 -InformationStateString(0) = "0,H4,\n" -InformationStateString(1) = "1,\n" -ObservationString(0) = "p0,H4," -ObservationString(1) = "p1," -ChanceOutcomes() = [(0,0.0196078), (1,0.0196078), (2,0.0196078), (3,0.0196078), (4,0.0196078), (5,0.0196078), (6,0.0196078), (7,0.0196078), (8,0.0196078), (9,0.0196078), (10,0.0196078), (11,0.0196078), (12,0.0196078), (13,0.0196078), (14,0.0196078), (15,0.0196078), (16,0.0196078), (17,0.0196078), (18,0.0196078), (19,0.0196078), (20,0.0196078), (21,0.0196078), (22,0.0196078), (23,0.0196078), (24,0.0196078), (25,0.0196078), (26,0.0196078), (27,0.0196078), (28,0.0196078), (29,0.0196078), (30,0.0196078), (31,0.0196078), (32,0.0196078), (33,0.0196078), (34,0.0196078), (35,0.0196078), (37,0.0196078), (38,0.0196078), (39,0.0196078), (40,0.0196078), (41,0.0196078), (42,0.0196078), (43,0.0196078), (44,0.0196078), (45,0.0196078), (46,0.0196078), (47,0.0196078), (48,0.0196078), (49,0.0196078), (50,0.0196078), (51,0.0196078)] -LegalActions() = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51] -StringLegalActions() = ["CA", "CK", "CQ", "CJ", "CT", "C9", "C8", "C7", "C6", "C5", "C4", "C3", "C2", "DA", "DK", "DQ", "DJ", "DT", "D9", "D8", "D7", "D6", "D5", "D4", "D3", "D2", "HA", "HK", "HQ", "HJ", "HT", "H9", "H8", "H7", "H6", "H5", "H3", "H2", "SA", "SK", "SQ", "SJ", "ST", "S9", "S8", "S7", "S6", "S5", "S4", "S3", "S2"] - -# Apply action "HT" -action: 30 - -# State 2 -# Apply action "C6" -action: 8 - -# State 3 -# Apply action "H5" -action: 35 - -# State 4 -# Apply action "CK" -action: 1 - -# State 5 -# Apply action "S5" -action: 48 - -# State 6 -# Apply action "S4" -action: 49 - -# State 7 -# Apply action "H3" -action: 37 - -# State 8 -# Apply action "S6" -action: 47 - -# State 9 -# Apply action "CT" -action: 4 - -# State 10 -# Apply action "C5" -action: 9 - -# State 11 -# Apply action "C8" -action: 6 - -# State 12 -# Apply action "CJ" -action: 3 - -# State 13 -# Apply action "D3" -action: 24 - -# State 14 -# Apply action "H9" -action: 31 - -# State 15 -# Apply action "D8" -action: 19 - -# State 16 -# Apply action "C9" -action: 5 - -# State 17 -# Apply action "HA" -action: 26 - -# State 18 -# Apply action "SQ" -action: 41 - -# State 19 -# Apply action "S8" -action: 45 - -# State 20 -# Apply action "ST" -action: 43 - -# State 21 -# Apply action "C4" -action: 10 - -# State 22 -# Apply action "H6" -action: 34 - -# State 23 -# Apply action "S9" -action: 44 - -# State 24 -# Apply action "C3" -action: 11 - -# State 25 -# Apply action "DJ" -action: 16 - -# State 26 -# Apply action "SJ" -action: 42 - -# State 27 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 0 -InformationStateString(0) = "0,CK,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S6,S4,\nc_public:SJ," -InformationStateString(1) = "1,CT,C8,C4,DJ,D8,D3,HA,HT,H5,H3,S9,S8,S5,\nc_public:SJ," -ObservationString(0) = "p0,CK,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S6,S4,-1:SJ," -ObservationString(1) = "p1,CT,C8,C4,DJ,D8,D3,HA,HT,H5,H3,S9,S8,S5,-1:SJ," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [1, 3, 5, 8, 9, 11, 31, 34, 36, 41, 43, 47, 49] -StringLegalActions() = ["CK", "CJ", "C9", "C6", "C5", "C3", "H9", "H6", "H4", "SQ", "ST", "S6", "S4"] - -# Apply action "S6" -action: 47 - -# State 28 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 1 -InformationStateString(0) = "0,CK,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S4,\nc_public:SJ,p0:S6," -InformationStateString(1) = "1,CT,C8,C4,DJ,D8,D3,HA,HT,H5,H3,S9,S8,S5,\nc_public:SJ,p0:S6," -ObservationString(0) = "p0,CK,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S4,-1:SJ,0:S6," -ObservationString(1) = "p1,CT,C8,C4,DJ,D8,D3,HA,HT,H5,H3,S9,S8,S5,-1:SJ,0:S6," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [44, 45, 48] -StringLegalActions() = ["S9", "S8", "S5"] - -# Apply action "S9" -action: 44 - -# State 29 -# Apply action "CQ" -action: 2 - -# State 30 -# Apply action "S2" -action: 51 - -# State 31 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -# S9 -# CQ -# S2 -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 1 -InformationStateString(0) = "0,CK,CQ,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S4,\nc_public:SJ,p0:S6,p1:S9,c_observed:CQ\nc_public:S2," -InformationStateString(1) = "1,CT,C8,C4,DJ,D8,D3,HA,HT,H5,H3,SJ,S8,S5,\nc_public:SJ,p0:S6,p1:S9,c_unobserved:\nc_public:S2," -ObservationString(0) = "p0,CK,CQ,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S4,-1:SJ,0:S6,1:S9,-1:S2," -ObservationString(1) = "p1,CT,C8,C4,DJ,D8,D3,HA,HT,H5,H3,SJ,S8,S5,-1:SJ,0:S6,1:S9,-1:S2," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [4, 6, 10, 16, 19, 24, 26, 30, 35, 37, 42, 45, 48] -StringLegalActions() = ["CT", "C8", "C4", "DJ", "D8", "D3", "HA", "HT", "H5", "H3", "SJ", "S8", "S5"] - -# Apply action "D3" -action: 24 - -# State 32 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -# S9 -# CQ -# S2 -# D3 -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 0 -InformationStateString(0) = "0,CK,CQ,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S4,\nc_public:SJ,p0:S6,p1:S9,c_observed:CQ\nc_public:S2,p1:D3," -InformationStateString(1) = "1,CT,C8,C4,DJ,D8,HA,HT,H5,H3,SJ,S8,S5,\nc_public:SJ,p0:S6,p1:S9,c_unobserved:\nc_public:S2,p1:D3," -ObservationString(0) = "p0,CK,CQ,CJ,C9,C6,C5,C3,H9,H6,H4,SQ,ST,S4,-1:SJ,0:S6,1:S9,-1:S2,1:D3," -ObservationString(1) = "p1,CT,C8,C4,DJ,D8,HA,HT,H5,H3,SJ,S8,S5,-1:SJ,0:S6,1:S9,-1:S2,1:D3," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [1, 2, 3, 5, 8, 9, 11, 31, 34, 36, 41, 43, 49] -StringLegalActions() = ["CK", "CQ", "CJ", "C9", "C6", "C5", "C3", "H9", "H6", "H4", "SQ", "ST", "S4"] - -# Apply action "H9" -action: 31 - -# State 33 -# Apply action "D5" -action: 22 - -# State 34 -# Apply action "C2" -action: 12 - -# State 35 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -# S9 -# CQ -# S2 -# D3 -# H9 -# D5 -# C2 -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 1 -InformationStateString(0) = "0,CK,CQ,CJ,C9,C6,C5,C3,D5,H6,H4,SQ,ST,S4,\nc_public:SJ,p0:S6,p1:S9,c_observed:CQ\nc_public:S2,p1:D3,p0:H9,c_observed:D5\nc_public:C2," -InformationStateString(1) = "1,CT,C8,C4,DJ,D8,HA,HT,H5,H3,SJ,S8,S5,S2,\nc_public:SJ,p0:S6,p1:S9,c_unobserved:\nc_public:S2,p1:D3,p0:H9,c_unobserved:\nc_public:C2," -ObservationString(0) = "p0,CK,CQ,CJ,C9,C6,C5,C3,D5,H6,H4,SQ,ST,S4,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2," -ObservationString(1) = "p1,CT,C8,C4,DJ,D8,HA,HT,H5,H3,SJ,S8,S5,S2,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [4, 6, 10, 16, 19, 26, 30, 35, 37, 42, 45, 48, 51] -StringLegalActions() = ["CT", "C8", "C4", "DJ", "D8", "HA", "HT", "H5", "H3", "SJ", "S8", "S5", "S2"] - -# Apply action "S5" -action: 48 - -# State 36 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -# S9 -# CQ -# S2 -# D3 -# H9 -# D5 -# C2 -# S5 -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 0 -InformationStateString(0) = "0,CK,CQ,CJ,C9,C6,C5,C3,D5,H6,H4,SQ,ST,S4,\nc_public:SJ,p0:S6,p1:S9,c_observed:CQ\nc_public:S2,p1:D3,p0:H9,c_observed:D5\nc_public:C2,p1:S5," -InformationStateString(1) = "1,CT,C8,C4,DJ,D8,HA,HT,H5,H3,SJ,S8,S2,\nc_public:SJ,p0:S6,p1:S9,c_unobserved:\nc_public:S2,p1:D3,p0:H9,c_unobserved:\nc_public:C2,p1:S5," -ObservationString(0) = "p0,CK,CQ,CJ,C9,C6,C5,C3,D5,H6,H4,SQ,ST,S4,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5," -ObservationString(1) = "p1,CT,C8,C4,DJ,D8,HA,HT,H5,H3,SJ,S8,S2,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [41, 43, 49] -StringLegalActions() = ["SQ", "ST", "S4"] - -# Apply action "S4" -action: 49 - -# State 37 -# Apply action "D4" -action: 23 - -# State 38 -# Apply action "H7" -action: 33 - -# State 39 -# Apply action "HA" -action: 26 - -# State 40 -# Apply action "H6" -action: 34 - -# State 41 -# Apply action "H2" -action: 38 - -# State 42 -# Apply action "SK" -action: 40 - -# State 43 -# Apply action "S2" -action: 51 - -# State 44 -# Apply action "ST" -action: 43 - -# State 45 -# Apply action "C7" -action: 7 - -# State 46 -# Apply action "DK" -action: 14 - -# State 47 -# Apply action "H2" -action: 38 - -# State 48 -# Apply action "HT" -action: 30 - -# State 49 -# Apply action "D7" -action: 20 - -# State 50 -# Apply action "HK" -action: 27 - -# State 51 -# Apply action "C7" -action: 7 - -# State 52 -# Apply action "CJ" -action: 3 - -# State 53 -# Apply action "D6" -action: 21 - -# State 54 -# Apply action "DA" -action: 13 - -# State 55 -# Apply action "H4" -action: 36 - -# State 56 -# Apply action "H7" -action: 33 - -# State 57 -# Apply action "SA" -action: 39 - -# State 58 -# Apply action "D9" -action: 18 - -# State 59 -# Apply action "D6" -action: 21 - -# State 60 -# Apply action "D7" -action: 20 - -# State 61 -# Apply action "DT" -action: 17 - -# State 62 -# Apply action "D2" -action: 25 - -# State 63 -# Apply action "D4" -action: 23 - -# State 64 -# Apply action "DA" -action: 13 - -# State 65 -# Apply action "S3" -action: 50 - -# State 66 -# Apply action "HQ" -action: 28 - -# State 67 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -# S9 -# CQ -# S2 -# D3 -# H9 -# D5 -# C2 -# S5 -# S4 -# D4 -# H7 -# HA -# H6 -# H2 -# SK -# S2 -# ST -# C7 -# DK -# H2 -# HT -# D7 -# HK -# C7 -# CJ -# D6 -# DA -# H4 -# H7 -# SA -# D9 -# D6 -# D7 -# DT -# D2 -# D4 -# DA -# S3 -# HQ -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48, 49, 23, 33, 26, 34, 38, 40, 51, 43, 7, 14, 38, 30, 20, 27, 7, 3, 21, 13, 36, 33, 39, 18, 21, 20, 17, 25, 23, 13, 50, 28] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48, 49, 23, 33, 26, 34, 38, 40, 51, 43, 7, 14, 38, 30, 20, 27, 7, 3, 21, 13, 36, 33, 39, 18, 21, 20, 17, 25, 23, 13, 50, 28" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 1 -InformationStateString(0) = "0,CK,CQ,C9,C6,C5,C3,D9,D5,HK,SA,SK,SQ,S3,\nc_public:SJ,p0:S6,p1:S9,c_observed:CQ\nc_public:S2,p1:D3,p0:H9,c_observed:D5\nc_public:C2,p1:S5,p0:S4,c_observed:D4\nc_public:H7,p1:HA,p0:H6,c_observed:H2\nc_public:SK,p1:S2,p0:ST,c_unobserved:\nc_public:DK,p0:H2,p1:HT,c_observed:D7\nc_public:HK,p1:C7,p0:CJ,c_unobserved:\nc_public:DA,p0:H4,p1:H7,c_observed:SA\nc_public:D9,p1:D6,p0:D7,c_unobserved:\nc_public:D2,p0:D4,p1:DA,c_observed:S3\nc_public:HQ," -InformationStateString(1) = "1,CT,C8,C4,C2,DK,DJ,DT,D8,D2,H5,H3,SJ,S8,\nc_public:SJ,p0:S6,p1:S9,c_unobserved:\nc_public:S2,p1:D3,p0:H9,c_unobserved:\nc_public:C2,p1:S5,p0:S4,c_unobserved:\nc_public:H7,p1:HA,p0:H6,c_unobserved:\nc_public:SK,p1:S2,p0:ST,c_observed:C7\nc_public:DK,p0:H2,p1:HT,c_unobserved:\nc_public:HK,p1:C7,p0:CJ,c_observed:D6\nc_public:DA,p0:H4,p1:H7,c_unobserved:\nc_public:D9,p1:D6,p0:D7,c_observed:DT\nc_public:D2,p0:D4,p1:DA,c_unobserved:\nc_public:HQ," -ObservationString(0) = "p0,CK,CQ,C9,C6,C5,C3,D9,D5,HK,SA,SK,SQ,S3,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5,0:S4,-1:H7,1:HA,0:H6,-1:SK,1:S2,0:ST,-1:DK,0:H2,1:HT,-1:HK,1:C7,0:CJ,-1:DA,0:H4,1:H7,-1:D9,1:D6,0:D7,-1:D2,0:D4,1:DA,-1:HQ," -ObservationString(1) = "p1,CT,C8,C4,C2,DK,DJ,DT,D8,D2,H5,H3,SJ,S8,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5,0:S4,-1:H7,1:HA,0:H6,-1:SK,1:S2,0:ST,-1:DK,0:H2,1:HT,-1:HK,1:C7,0:CJ,-1:DA,0:H4,1:H7,-1:D9,1:D6,0:D7,-1:D2,0:D4,1:DA,-1:HQ," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [4, 6, 10, 12, 14, 16, 17, 19, 25, 35, 37, 42, 45] -StringLegalActions() = ["CT", "C8", "C4", "C2", "DK", "DJ", "DT", "D8", "D2", "H5", "H3", "SJ", "S8"] - -# Apply action "S8" -action: 45 - -# State 68 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -# S9 -# CQ -# S2 -# D3 -# H9 -# D5 -# C2 -# S5 -# S4 -# D4 -# H7 -# HA -# H6 -# H2 -# SK -# S2 -# ST -# C7 -# DK -# H2 -# HT -# D7 -# HK -# C7 -# CJ -# D6 -# DA -# H4 -# H7 -# SA -# D9 -# D6 -# D7 -# DT -# D2 -# D4 -# DA -# S3 -# HQ -# S8 -IsTerminal() = False -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48, 49, 23, 33, 26, 34, 38, 40, 51, 43, 7, 14, 38, 30, 20, 27, 7, 3, 21, 13, 36, 33, 39, 18, 21, 20, 17, 25, 23, 13, 50, 28, 45] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48, 49, 23, 33, 26, 34, 38, 40, 51, 43, 7, 14, 38, 30, 20, 27, 7, 3, 21, 13, 36, 33, 39, 18, 21, 20, 17, 25, 23, 13, 50, 28, 45" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = 0 -InformationStateString(0) = "0,CK,CQ,C9,C6,C5,C3,D9,D5,HK,SA,SK,SQ,S3,\nc_public:SJ,p0:S6,p1:S9,c_observed:CQ\nc_public:S2,p1:D3,p0:H9,c_observed:D5\nc_public:C2,p1:S5,p0:S4,c_observed:D4\nc_public:H7,p1:HA,p0:H6,c_observed:H2\nc_public:SK,p1:S2,p0:ST,c_unobserved:\nc_public:DK,p0:H2,p1:HT,c_observed:D7\nc_public:HK,p1:C7,p0:CJ,c_unobserved:\nc_public:DA,p0:H4,p1:H7,c_observed:SA\nc_public:D9,p1:D6,p0:D7,c_unobserved:\nc_public:D2,p0:D4,p1:DA,c_observed:S3\nc_public:HQ,p1:S8," -InformationStateString(1) = "1,CT,C8,C4,C2,DK,DJ,DT,D8,D2,H5,H3,SJ,\nc_public:SJ,p0:S6,p1:S9,c_unobserved:\nc_public:S2,p1:D3,p0:H9,c_unobserved:\nc_public:C2,p1:S5,p0:S4,c_unobserved:\nc_public:H7,p1:HA,p0:H6,c_unobserved:\nc_public:SK,p1:S2,p0:ST,c_observed:C7\nc_public:DK,p0:H2,p1:HT,c_unobserved:\nc_public:HK,p1:C7,p0:CJ,c_observed:D6\nc_public:DA,p0:H4,p1:H7,c_unobserved:\nc_public:D9,p1:D6,p0:D7,c_observed:DT\nc_public:D2,p0:D4,p1:DA,c_unobserved:\nc_public:HQ,p1:S8," -ObservationString(0) = "p0,CK,CQ,C9,C6,C5,C3,D9,D5,HK,SA,SK,SQ,S3,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5,0:S4,-1:H7,1:HA,0:H6,-1:SK,1:S2,0:ST,-1:DK,0:H2,1:HT,-1:HK,1:C7,0:CJ,-1:DA,0:H4,1:H7,-1:D9,1:D6,0:D7,-1:D2,0:D4,1:DA,-1:HQ,1:S8," -ObservationString(1) = "p1,CT,C8,C4,C2,DK,DJ,DT,D8,D2,H5,H3,SJ,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5,0:S4,-1:H7,1:HA,0:H6,-1:SK,1:S2,0:ST,-1:DK,0:H2,1:HT,-1:HK,1:C7,0:CJ,-1:DA,0:H4,1:H7,-1:D9,1:D6,0:D7,-1:D2,0:D4,1:DA,-1:HQ,1:S8," -Rewards() = [0, 0] -Returns() = [0, 0] -LegalActions() = [39, 40, 41, 50] -StringLegalActions() = ["SA", "SK", "SQ", "S3"] - -# Apply action "SK" -action: 40 - -# State 69 -# Apply action "S7" -action: 46 - -# State 70 -# Apply action "DQ" -action: 15 - -# State 71 -# Apply action "S3" -action: 50 - -# State 72 -# Apply action "S7" -action: 46 - -# State 73 -# Apply action "H8" -action: 32 - -# State 74 -# Apply action "CA" -action: 0 - -# State 75 -# Apply action "C4" -action: 10 - -# State 76 -# Apply action "CQ" -action: 2 - -# State 77 -# Apply action "HJ" -action: 29 - -# State 78 -# H4 -# HT -# C6 -# H5 -# CK -# S5 -# S4 -# H3 -# S6 -# CT -# C5 -# C8 -# CJ -# D3 -# H9 -# D8 -# C9 -# HA -# SQ -# S8 -# ST -# C4 -# H6 -# S9 -# C3 -# DJ -# SJ -# S6 -# S9 -# CQ -# S2 -# D3 -# H9 -# D5 -# C2 -# S5 -# S4 -# D4 -# H7 -# HA -# H6 -# H2 -# SK -# S2 -# ST -# C7 -# DK -# H2 -# HT -# D7 -# HK -# C7 -# CJ -# D6 -# DA -# H4 -# H7 -# SA -# D9 -# D6 -# D7 -# DT -# D2 -# D4 -# DA -# S3 -# HQ -# S8 -# SK -# S7 -# DQ -# S3 -# S7 -# H8 -# CA -# C4 -# CQ -# HJ -IsTerminal() = True -History() = [36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48, 49, 23, 33, 26, 34, 38, 40, 51, 43, 7, 14, 38, 30, 20, 27, 7, 3, 21, 13, 36, 33, 39, 18, 21, 20, 17, 25, 23, 13, 50, 28, 45, 40, 46, 15, 50, 46, 32, 0, 10, 2, 29] -HistoryString() = "36, 30, 8, 35, 1, 48, 49, 37, 47, 4, 9, 6, 3, 24, 31, 19, 5, 26, 41, 45, 43, 10, 34, 44, 11, 16, 42, 47, 44, 2, 51, 24, 31, 22, 12, 48, 49, 23, 33, 26, 34, 38, 40, 51, 43, 7, 14, 38, 30, 20, 27, 7, 3, 21, 13, 36, 33, 39, 18, 21, 20, 17, 25, 23, 13, 50, 28, 45, 40, 46, 15, 50, 46, 32, 0, 10, 2, 29" -IsChanceNode() = False -IsSimultaneousNode() = False -CurrentPlayer() = -4 -InformationStateString(0) = "0,CA,CK,C9,C6,C5,C3,D9,D5,HK,HQ,H8,SA,SQ,\nc_public:SJ,p0:S6,p1:S9,c_observed:CQ\nc_public:S2,p1:D3,p0:H9,c_observed:D5\nc_public:C2,p1:S5,p0:S4,c_observed:D4\nc_public:H7,p1:HA,p0:H6,c_observed:H2\nc_public:SK,p1:S2,p0:ST,c_unobserved:\nc_public:DK,p0:H2,p1:HT,c_observed:D7\nc_public:HK,p1:C7,p0:CJ,c_unobserved:\nc_public:DA,p0:H4,p1:H7,c_observed:SA\nc_public:D9,p1:D6,p0:D7,c_unobserved:\nc_public:D2,p0:D4,p1:DA,c_observed:S3\nc_public:HQ,p1:S8,p0:SK,c_unobserved:\nc_public:DQ,p0:S3,p1:S7,c_observed:H8\nc_public:CA,p1:C4,p0:CQ,c_unobserved:\n" -InformationStateString(1) = "1,CT,C8,C2,DK,DQ,DJ,DT,D8,D2,HJ,H5,H3,SJ,\nc_public:SJ,p0:S6,p1:S9,c_unobserved:\nc_public:S2,p1:D3,p0:H9,c_unobserved:\nc_public:C2,p1:S5,p0:S4,c_unobserved:\nc_public:H7,p1:HA,p0:H6,c_unobserved:\nc_public:SK,p1:S2,p0:ST,c_observed:C7\nc_public:DK,p0:H2,p1:HT,c_unobserved:\nc_public:HK,p1:C7,p0:CJ,c_observed:D6\nc_public:DA,p0:H4,p1:H7,c_unobserved:\nc_public:D9,p1:D6,p0:D7,c_observed:DT\nc_public:D2,p0:D4,p1:DA,c_unobserved:\nc_public:HQ,p1:S8,p0:SK,c_observed:S7\nc_public:DQ,p0:S3,p1:S7,c_unobserved:\nc_public:CA,p1:C4,p0:CQ,c_observed:HJ\n" -ObservationString(0) = "p0,CA,CK,C9,C6,C5,C3,D9,D5,HK,HQ,H8,SA,SQ,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5,0:S4,-1:H7,1:HA,0:H6,-1:SK,1:S2,0:ST,-1:DK,0:H2,1:HT,-1:HK,1:C7,0:CJ,-1:DA,0:H4,1:H7,-1:D9,1:D6,0:D7,-1:D2,0:D4,1:DA,-1:HQ,1:S8,0:SK,-1:DQ,0:S3,1:S7,-1:CA,1:C4,0:CQ," -ObservationString(1) = "p1,CT,C8,C2,DK,DQ,DJ,DT,D8,D2,HJ,H5,H3,SJ,-1:SJ,0:S6,1:S9,-1:S2,1:D3,0:H9,-1:C2,1:S5,0:S4,-1:H7,1:HA,0:H6,-1:SK,1:S2,0:ST,-1:DK,0:H2,1:HT,-1:HK,1:C7,0:CJ,-1:DA,0:H4,1:H7,-1:D9,1:D6,0:D7,-1:D2,0:D4,1:DA,-1:HQ,1:S8,0:SK,-1:DQ,0:S3,1:S7,-1:CA,1:C4,0:CQ," -Rewards() = [-13, 13] -Returns() = [-13, 13] diff --git a/open_spiel/python/tests/pyspiel_test.py b/open_spiel/python/tests/pyspiel_test.py index 57af5138d3..f34ad4f153 100644 --- a/open_spiel/python/tests/pyspiel_test.py +++ b/open_spiel/python/tests/pyspiel_test.py @@ -58,7 +58,6 @@ "efg_game", "euchre", "first_sealed_auction", - "german_whist_foregame", "gin_rummy", "go", "goofspiel",