diff --git a/docs/games.md b/docs/games.md index 817e69d0f5..48df0bbd51 100644 --- a/docs/games.md +++ b/docs/games.md @@ -93,6 +93,7 @@ | 🔶 | [Skat (simplified | : : bidding)](#skat-simplified-bidding) : | 🔶 | [Solitaire (K+)](#solitaire-k) | +| 🔶 | [Spades](#spades) | | 🔶 | [Team Dominoes](#team-dominoes) | | 🟢 | [Tic-Tac-Toe](#tic-tac-toe) | | 🟢 | [Tiny Bridge](#tiny-bridge) | @@ -864,6 +865,16 @@ * [Wikipedia](https://en.wikipedia.org/wiki/Klondike_\(solitaire\)) and [Bjarnason et al. '07, Searching solitaire in real time](http://web.engr.oregonstate.edu/~afern/papers/solitaire.pdf) +### Spades + +* A four-player card game. +* Card game. +* Traditional game. +* Non-deterministic. +* Imperfect information. +* 4 players. +* [Wikipedia](https://en.wikipedia.org/wiki/Spades_\(card_game\)) + ### Team Dominoes * Team version of dominoes. diff --git a/open_spiel/games/CMakeLists.txt b/open_spiel/games/CMakeLists.txt index 02cf17e78c..6d374d9a77 100644 --- a/open_spiel/games/CMakeLists.txt +++ b/open_spiel/games/CMakeLists.txt @@ -161,6 +161,10 @@ set(GAME_SOURCES skat/skat.h solitaire/solitaire.cc solitaire/solitaire.h + spades/spades.cc + spades/spades.h + spades/spades_scoring.cc + spades/spades_scoring.h stones_and_gems/stones_and_gems.cc stones_and_gems/stones_and_gems.h tarok/tarok.cc @@ -579,6 +583,10 @@ add_executable(solitaire_test solitaire/solitaire_test.cc ${OPEN_SPIEL_OBJECTS} $) add_test(solitaire_test solitaire_test) +add_executable(spades_test spades/spades_test.cc ${OPEN_SPIEL_OBJECTS} + $) +add_test(spades_test spades_test) + add_executable(stones_and_gems_test stones_and_gems/stones_and_gems_test.cc ${OPEN_SPIEL_OBJECTS} $) diff --git a/open_spiel/games/spades/spades.cc b/open_spiel/games/spades/spades.cc new file mode 100644 index 0000000000..ddb8482ab3 --- /dev/null +++ b/open_spiel/games/spades/spades.cc @@ -0,0 +1,611 @@ +// Copyright 2019 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/spades/spades.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "open_spiel/abseil-cpp/absl/strings/str_cat.h" +#include "open_spiel/abseil-cpp/absl/strings/str_format.h" +#include "open_spiel/abseil-cpp/absl/types/optional.h" +#include "open_spiel/abseil-cpp/absl/types/span.h" +#include "open_spiel/game_parameters.h" +#include "open_spiel/games/spades/spades_scoring.h" +#include "open_spiel/observer.h" +#include "open_spiel/spiel.h" +#include "open_spiel/spiel_globals.h" +#include "open_spiel/spiel_utils.h" + +// Our preferred version of the double_dummy_solver defines a DDS_EXTERNAL +// macro to add a prefix to the exported symbols to avoid name clashes. +// In order to compile with versions of the double_dummy_solver which do not +// do this, we define DDS_EXTERNAL as an identity if it isn't already defined. +#ifndef DDS_EXTERNAL +#define DDS_EXTERNAL(x) x +#endif + +namespace open_spiel { +namespace spades { +namespace { + +enum Seat { kNorth, kEast, kSouth, kWest }; + +const GameType kGameType{ + /*short_name=*/"spades", + /*long_name=*/"Partnership Spades", + GameType::Dynamics::kSequential, + GameType::ChanceMode::kExplicitStochastic, + GameType::Information::kImperfectInformation, + GameType::Utility::kGeneralSum, + GameType::RewardModel::kTerminal, + /*max_num_players=*/kNumPlayers, + /*min_num_players=*/kNumPlayers, + /*provides_information_state_string=*/false, + /*provides_information_state_tensor=*/false, + /*provides_observation_string=*/true, + /*provides_observation_tensor=*/true, + /*parameter_specification=*/ + { + // Whether to end the game early if score gets too low + {"use_mercy_rule", GameParameter(true)}, + // If using mercy rule, the threshold of negative points + {"mercy_threshold", GameParameter(-350)}, + // Amount of points needed to win the game + {"win_threshold", GameParameter(500)}, + // Partnership's current scores + // (can infer bags from last digit) + {"score_partnership_0", GameParameter(0)}, + {"score_partnership_1", GameParameter(0)}, + // Number of played tricks in observation tensor + {"num_tricks", GameParameter(2)}, + }}; + +std::shared_ptr Factory(const GameParameters& params) { + return std::shared_ptr(new SpadesGame(params)); +} + +REGISTER_SPIEL_GAME(kGameType, Factory); + +RegisterSingleTensorObserver single_tensor(kGameType.short_name); + +// Cards are represented suit * number of cards per suit + rank +Suit CardSuit(int card) { return Suit(card / 13); } +int CardRank(int card) { return card % 13; } +int Card(Suit suit, int rank) { + return static_cast(suit) * kNumCardsPerSuit + rank; +} + +constexpr char kRankChar[] = "23456789TJQKA"; +constexpr char kSuitChar[] = "CDHS"; + +std::string CardString(int card) { + return {kSuitChar[static_cast(CardSuit(card))], + kRankChar[CardRank(card)]}; +} + +std::string BidString(int bid) { + if (bid == 0) return "Nil"; + return std::to_string(bid); +} + +// There are two partnerships: players 0 and 2 versus players 1 and 3. +// We call 0 and 2 partnership 0, and 1 and 3 partnership 1. +int Partnership(Player player) { return player & 1; } +int Partner(Player player) { return (player + 2) % 4; } +} // namespace + +SpadesGame::SpadesGame(const GameParameters& params) + : Game(kGameType, params) {} + +SpadesState::SpadesState(std::shared_ptr game, bool use_mercy_rule, + int mercy_threshold, int win_threshold, + int score_partnership_0, int score_partnership_1, + int num_tricks) + : State(game), + use_mercy_rule_(use_mercy_rule), + mercy_threshold_(mercy_threshold), + win_threshold_(win_threshold), + current_scores_{score_partnership_0, score_partnership_1}, + num_tricks_(num_tricks) { + possible_contracts_.fill(true); +} + +std::string SpadesState::ActionToString(Player player, Action action) const { + return (action < kBiddingActionBase) ? CardString(action) + : BidString(action - kBiddingActionBase); +} + +std::string SpadesState::ToString() const { + std::string rv = absl::StrCat(FormatDeal()); + if (history_.size() > kNumCards) + absl::StrAppend(&rv, FormatAuction(/*trailing_query=*/false)); + if (num_cards_played_ > 0) absl::StrAppend(&rv, FormatPlay()); + if (IsTerminal()) absl::StrAppend(&rv, FormatResult()); + return rv; +} + +std::array FormatHand( + int player, bool mark_voids, + const std::array, kNumCards>& deal) { + std::array cards; + for (int suit = 0; suit < kNumSuits; ++suit) { + cards[suit].push_back(kSuitChar[suit]); + cards[suit].push_back(' '); + bool is_void = true; + for (int rank = kNumCardsPerSuit - 1; rank >= 0; --rank) { + if (player == deal[Card(Suit(suit), rank)]) { + cards[suit].push_back(kRankChar[rank]); + is_void = false; + } + } + if (is_void && mark_voids) absl::StrAppend(&cards[suit], "none"); + } + return cards; +} + +std::string SpadesState::ObservationString(Player player) const { + SPIEL_CHECK_GE(player, 0); + SPIEL_CHECK_LT(player, num_players_); + if (IsTerminal()) return ToString(); + std::string rv = ""; + auto cards = FormatHand(player, /*mark_voids=*/true, holder_); + for (int suit = kNumSuits - 1; suit >= 0; --suit) + absl::StrAppend(&rv, cards[suit], "\n"); + if (history_.size() > kNumCards) + absl::StrAppend( + &rv, FormatAuction(/*trailing_query=*/phase_ == Phase::kAuction && + player == CurrentPlayer())); + if (num_cards_played_ > 0) absl::StrAppend(&rv, FormatPlay()); + return rv; +} + +std::array, kNumCards> SpadesState::OriginalDeal() + const { + SPIEL_CHECK_GE(history_.size(), kNumCards); + std::array, kNumCards> deal; + for (int i = 0; i < kNumCards; ++i) + deal[history_[i].action] = (i % kNumPlayers); + return deal; +} + +std::string SpadesState::FormatDeal() const { + std::array, kNumPlayers> cards; + if (IsTerminal()) { + // Include all cards in the terminal state to make reviewing the deal easier + auto deal = OriginalDeal(); + for (auto player : {kNorth, kEast, kSouth, kWest}) { + cards[player] = FormatHand(player, /*mark_voids=*/false, deal); + } + } else { + for (auto player : {kNorth, kEast, kSouth, kWest}) { + cards[player] = FormatHand(player, /*mark_voids=*/false, holder_); + } + } + constexpr int kColumnWidth = 8; + std::string padding(kColumnWidth, ' '); + std::string rv; + for (int suit = kNumSuits - 1; suit >= 0; --suit) + absl::StrAppend(&rv, padding, cards[kNorth][suit], "\n"); + for (int suit = kNumSuits - 1; suit >= 0; --suit) + absl::StrAppend(&rv, absl::StrFormat("%-8s", cards[kWest][suit]), padding, + cards[kEast][suit], "\n"); + for (int suit = kNumSuits - 1; suit >= 0; --suit) + absl::StrAppend(&rv, padding, cards[kSouth][suit], "\n"); + return rv; +} + +std::string SpadesState::FormatAuction(bool trailing_query) const { + SPIEL_CHECK_GT(history_.size(), kNumCards); + std::string rv = "\nNorth East South West "; + for (int i = kNumCards; i < history_.size() - num_cards_played_; ++i) { + if (i % kNumPlayers == 0) rv.push_back('\n'); + absl::StrAppend( + &rv, absl::StrFormat( + "%-6s", BidString(history_[i].action - kBiddingActionBase))); + } + if (trailing_query) { + if ((history_.size() - num_cards_played_) % kNumPlayers == kNumPlayers - 1) + rv.push_back('\n'); + rv.push_back('?'); + } + return rv; +} + +std::string SpadesState::FormatPlay() const { + SPIEL_CHECK_GT(num_cards_played_, 0); + std::string rv = "\n\nN E S W N E S"; + Trick trick{kInvalidPlayer, 0}; + Player player = kFirstPlayer; + for (int i = 0; i < num_cards_played_; ++i) { + if (i % kNumPlayers == 0) { + if (i > 0) player = trick.Winner(); + absl::StrAppend(&rv, "\n", std::string(3 * player, ' ')); + } else { + player = (1 + player) % kNumPlayers; + } + const int card = history_[history_.size() - num_cards_played_ + i].action; + if (i % kNumPlayers == 0) { + trick = Trick(player, card); + } else { + trick.Play(player, card); + } + absl::StrAppend(&rv, CardString(card), " "); + } + absl::StrAppend(&rv, "\n\nTricks taken:\n\n", "North East South West\n", + absl::StrFormat("%-6d", num_player_tricks_[0]), + absl::StrFormat("%-6d", num_player_tricks_[1]), + absl::StrFormat("%-6d", num_player_tricks_[2]), + absl::StrFormat("%-6d", num_player_tricks_[3]), "\n"); + return rv; +} + +std::string SpadesState::FormatResult() const { + SPIEL_CHECK_TRUE(IsTerminal()); + std::string rv; + absl::StrAppend(&rv, "\nScore: N/S ", returns_[kNorth], " E/W ", + returns_[kEast]); + return rv; +} + +void SpadesState::ObservationTensor(Player player, + absl::Span values) const { + SPIEL_CHECK_EQ(values.size(), game_->ObservationTensorSize()); + WriteObservationTensor(player, values); +} + +void SpadesState::WriteObservationTensor(Player player, + absl::Span values) const { + SPIEL_CHECK_GE(player, 0); + SPIEL_CHECK_LT(player, num_players_); + + std::fill(values.begin(), values.end(), 0.0); + if (phase_ == Phase::kDeal) return; + auto ptr = values.begin(); + + // Mark bidding or playing phase + ptr[static_cast(phase_) - 1] = 1; + ptr += kPhaseInfoSize; + + if (num_cards_played_ > 0) { + // Observation for play phase + + // Contracts + for (int i = 0; i < kNumPlayers; i++) { + ptr[contracts_[i]] = 1; + ptr += kNumBids; + } + + // Our remaining cards. + for (int i = 0; i < kNumCards; ++i) + if (holder_[i] == player) ptr[i] = 1; + ptr += kNumCards; + + // Indexing into history for recent tricks. + int current_trick = num_cards_played_ / kNumPlayers; + int this_trick_cards_played = num_cards_played_ % kNumPlayers; + int this_trick_start = history_.size() - this_trick_cards_played; + + // Current trick + if (phase_ != Phase::kGameOver) { + int leader = tricks_[current_trick].Leader(); + for (int i = 0; i < this_trick_cards_played; ++i) { + int card = history_[this_trick_start + i].action; + int relative_player = (i + leader + kNumPlayers - player) % kNumPlayers; + ptr[relative_player * kNumCards + card] = 1; + } + } + + ptr += kNumPlayers * kNumCards; + + // Previous tricks + for (int j = current_trick - 1; + j >= std::max(0, current_trick - num_tricks_ + 1); --j) { + int leader = tricks_[j].Leader(); + for (int i = 0; i < kNumPlayers; ++i) { + int card = + history_[this_trick_start - kNumPlayers * (current_trick - j) + i] + .action; + int relative_player = (i + leader + kNumPlayers - player) % kNumPlayers; + ptr[relative_player * kNumCards + card] = 1; + } + ptr += kNumPlayers * kNumCards; + } + + // Move pointer for future tricks to have a fixed size tensor + if (num_tricks_ > current_trick + 1) { + ptr += kNumPlayers * kNumCards * (num_tricks_ - current_trick - 1); + } + + // Number of tricks taken by each side. + for (int i = 0; i < kNumPlayers; i++) { + ptr[num_player_tricks_[i]] = 1; + ptr += kNumTricks; + } + + int kPlayTensorSize = SpadesGame::GetPlayTensorSize(num_tricks_); + SPIEL_CHECK_EQ(std::distance(values.begin(), ptr), + kPlayTensorSize + kPhaseInfoSize); + SPIEL_CHECK_LE(std::distance(values.begin(), ptr), values.size()); + } else { + // Observation for auction + + // Bids made so far + for (int i = 0; i < kNumPlayers; i++) { + // If player has bid, mark it + if (contracts_[i] >= 0) { + ptr[contracts_[i]] = 1; + } + ptr += kNumBids; + } + + // Our cards. + for (int i = 0; i < kNumCards; ++i) + if (holder_[i] == player) ptr[i] = 1; + ptr += kNumCards; + SPIEL_CHECK_EQ(std::distance(values.begin(), ptr), + kAuctionTensorSize + kPhaseInfoSize); + SPIEL_CHECK_LE(std::distance(values.begin(), ptr), values.size()); + } +} + +std::vector SpadesState::PublicObservationTensor() const { + SPIEL_CHECK_TRUE(phase_ == Phase::kAuction); + std::vector rv(kPublicInfoTensorSize); + auto ptr = rv.begin(); + // Bids made so far + for (int i = 0; i < kNumPlayers; i++) { + // If player has bid, mark it + if (contracts_[i] >= 0) { + ptr[contracts_[i]] = 1; + } + ptr += kNumBids; + } + return rv; +} + +std::vector SpadesState::PrivateObservationTensor(Player player) const { + std::vector rv(kNumCards); + for (int i = 0; i < kNumCards; ++i) + if (holder_[i] == player) rv[i] = 1; + return rv; +} + +std::vector SpadesState::LegalActions() const { + switch (phase_) { + case Phase::kDeal: + return DealLegalActions(); + case Phase::kAuction: + return BiddingLegalActions(); + case Phase::kPlay: + return PlayLegalActions(); + default: + return {}; + } +} + +std::vector SpadesState::DealLegalActions() const { + std::vector legal_actions; + legal_actions.reserve(kNumCards - history_.size()); + for (int i = 0; i < kNumCards; ++i) { + if (!holder_[i].has_value()) legal_actions.push_back(i); + } + return legal_actions; +} + +std::vector SpadesState::BiddingLegalActions() const { + std::vector legal_actions; + legal_actions.reserve(kNumBids); + int partner_bid = contracts_[Partner(current_player_)]; + + if (partner_bid >= 0) { + // Combined bid between partners cannot be more than 13 + for (int bid = 0; bid < kNumBids - partner_bid; ++bid) { + legal_actions.push_back(kBiddingActionBase + bid); + } + } else { + for (int bid = 0; bid < kNumBids; ++bid) { + legal_actions.push_back(kBiddingActionBase + bid); + } + } + + return legal_actions; +} + +std::vector SpadesState::PlayLegalActions() const { + std::vector legal_actions; + legal_actions.reserve(kNumCardsPerHand - num_cards_played_ / kNumPlayers); + + // Check if we can follow suit. + if (num_cards_played_ % kNumPlayers != 0) { + auto suit = CurrentTrick().LedSuit(); + for (int rank = 0; rank < kNumCardsPerSuit; ++rank) { + if (holder_[Card(suit, rank)] == current_player_) { + legal_actions.push_back(Card(suit, rank)); + } + } + } else if (num_cards_played_ % kNumPlayers == 0 && !is_spades_broken_) { + // If leading, and spades have not been broken, play any other suit if + // possible. + for (int suit = 0 /*kClubs*/; suit < 3 /*kSpades*/; ++suit) { + for (int rank = 0; rank < kNumCardsPerSuit; ++rank) { + if (holder_[Card(Suit(suit), rank)] == current_player_) { + legal_actions.push_back(Card(Suit(suit), rank)); + } + } + } + } + if (!legal_actions.empty()) return legal_actions; + + // Otherwise, we can play any of our cards. + for (int card = 0; card < kNumCards; ++card) { + if (holder_[card] == current_player_) legal_actions.push_back(card); + } + return legal_actions; +} + +std::vector> SpadesState::ChanceOutcomes() const { + std::vector> outcomes; + int num_cards_remaining = kNumCards - history_.size(); + outcomes.reserve(num_cards_remaining); + const double p = 1.0 / static_cast(num_cards_remaining); + for (int card = 0; card < kNumCards; ++card) { + if (!holder_[card].has_value()) outcomes.emplace_back(card, p); + } + return outcomes; +} + +void SpadesState::DoApplyAction(Action action) { + switch (phase_) { + case Phase::kDeal: + return ApplyDealAction(action); + case Phase::kAuction: + return ApplyBiddingAction(action - kBiddingActionBase); + case Phase::kPlay: + return ApplyPlayAction(action); + case Phase::kGameOver: + SpielFatalError("Cannot act in terminal states"); + } +} + +void SpadesState::ApplyDealAction(int card) { + holder_[card] = (history_.size() % kNumPlayers); + if (history_.size() == kNumCards - 1) { + phase_ = Phase::kAuction; + current_player_ = kFirstPlayer; + } +} + +void SpadesState::ApplyBiddingAction(int bid) { + // A bid was made. + const int partner = Partner(current_player_); + SPIEL_CHECK_TRUE(contracts_[partner] == -1 || + bid + contracts_[partner] <= 13); + contracts_[current_player_] = bid; + + // Mark off possible_contracts for this player's other bids + std::fill( + possible_contracts_.begin() + (current_player_ * kNumBids), + possible_contracts_.begin() + (current_player_ * kNumBids) + kNumBids, + false); + // If partner hasn't bid, mark off partner's possible bids that would go past + // 13 + if (contracts_[partner] == -1 && bid > 0) { + std::fill( + possible_contracts_.begin() + (partner * kNumBids) + kNumBids - bid, + possible_contracts_.begin() + (partner * kNumBids) + kNumBids, false); + } + + // And now mark this bid as the player's contract + possible_contracts_[current_player_ * kNumBids + bid] = true; + + current_player_ = (current_player_ + 1) % kNumPlayers; + + // After 4 bids, end the auction. + if (std::all_of(contracts_.begin(), contracts_.end(), + [](int x) { return x != -1; })) { + phase_ = Phase::kPlay; + } +} + +void SpadesState::ApplyPlayAction(int card) { + SPIEL_CHECK_TRUE(holder_[card] == current_player_); + holder_[card] = absl::nullopt; + if (num_cards_played_ % kNumPlayers == 0) { + CurrentTrick() = Trick(current_player_, card); + } else { + CurrentTrick().Play(current_player_, card); + } + const Player winner = CurrentTrick().Winner(); + ++num_cards_played_; + if (num_cards_played_ % kNumPlayers == 0) { + current_player_ = winner; + ++num_player_tricks_[current_player_]; + } else { + current_player_ = (current_player_ + 1) % kNumPlayers; + } + if (num_cards_played_ == kNumCards) { + phase_ = Phase::kGameOver; + ScoreUp(); + } +} + +Player SpadesState::CurrentPlayer() const { + if (phase_ == Phase::kDeal) { + return kChancePlayerId; + } else if (phase_ == Phase::kGameOver) { + return kTerminalPlayerId; + } else { + return current_player_; + } +} + +void SpadesState::ScoreUp() { + std::array scores = + Score(contracts_, num_player_tricks_, current_scores_); + for (int pl = 0; pl < kNumPlayers; ++pl) { + returns_[pl] = scores[Partnership(pl)]; + } +} + +Trick::Trick(Player leader, int card) + : led_suit_(CardSuit(card)), + winning_suit_(CardSuit(card)), + winning_rank_(CardRank(card)), + leader_(leader), + winning_player_(leader) {} + +void Trick::Play(Player player, int card) { + if (CardSuit(card) == winning_suit_) { + if (CardRank(card) > winning_rank_) { + winning_rank_ = CardRank(card); + winning_player_ = player; + } + } else if (CardSuit(card) == Suit(3) /*kSpades*/) { + winning_suit_ = Suit(3) /*kSpades*/; + winning_rank_ = CardRank(card); + winning_player_ = player; + } +} + +std::string SpadesState::Serialize() const { + std::string serialized = State::Serialize(); + return serialized; +} + +std::unique_ptr SpadesGame::DeserializeState( + const std::string& str) const { + return Game::DeserializeState(str); +} + +std::array SpadesState::ContractIndexes() const { + SPIEL_CHECK_TRUE(phase_ == Phase::kPlay || phase_ == Phase::kGameOver); + std::array contract_indexes; + for (int i = 0; i < kNumPlayers; ++i) { + contract_indexes[i] = (i * kNumBids) + contracts_[i]; + } + return contract_indexes; +} + +std::string SpadesGame::ContractString(int bid) const { + return (bid == 0) ? "Nil" : std::to_string(bid); +} + +} // namespace spades +} // namespace open_spiel diff --git a/open_spiel/games/spades/spades.h b/open_spiel/games/spades/spades.h new file mode 100644 index 0000000000..66842bc37f --- /dev/null +++ b/open_spiel/games/spades/spades.h @@ -0,0 +1,246 @@ +// Copyright 2019 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_SPADES_H_ +#define OPEN_SPIEL_GAMES_SPADES_H_ + +// The full game of partnership spades. +// See https://dkmgames.com/CardSharp/Spades/SpadesHelp.php +// This is played by four players in two partnerships; it consists of a bidding +// phase followed by a play phase. The bidding phase determines the contracts +// for the play phase. The contract consists of: +// - Each player bidding how many tricks they can take. +// - If a player bids 'Nil' (meaning '0'), then they have a special condition +// for points +// based on whether they can avoid taking any tricks. +// +// There is then a play phase, in which 13 tricks are allocated between the +// two partnerships. Each partnership gains 10 times their combined contract +// if the partners are able to collectively take at least as many tricks as that +// combined contract, otherwise the partnership loses 10 times their combined +// contract. +// +// Any tricks taken in excess of a partnership's combined contract are worth 1 +// point and considered a 'bag' - for every 10 bags collected over the course of +// the game, the partnership is penalized 100 points. +// +// In the case of a Nil bid, if that partner avoids taking any tricks during the +// round, the partnership gains a 100 point bonus. Conversely, if that partner +// takes any tricks, the partnership will lose 100 points (but these tricks +// still count toward the other partner's contract). +// +// The action space is as follows: +// 0..51 Cards, used for both dealing (chance events) and play; +// 52+ Bids (Nil, 1-13) used during the bidding phase. +// +// During the bidding phase, every player will have 1 turn for making a bid. +// During the play phase, every play will have 13 turns for playing a card. + +#include +#include +#include +#include +#include +#include +#include "open_spiel/games/spades/spades_scoring.h" +#include "open_spiel/abseil-cpp/absl/types/optional.h" +#include "open_spiel/abseil-cpp/absl/types/span.h" +#include "open_spiel/game_parameters.h" +#include "open_spiel/spiel.h" +#include "open_spiel/spiel_globals.h" +#include "open_spiel/spiel_utils.h" + +namespace open_spiel { +namespace spades { + +inline constexpr int kBiddingActionBase = kNumCards; // First bidding action. +inline constexpr int kAuctionTensorSize = + kNumPlayers * kNumBids + kNumCards; // Our hand +inline constexpr int kPhaseInfoSize = 2; // Bidding (auction) and Playing +inline constexpr int kPublicInfoTensorSize = + kAuctionTensorSize // The auction + - kNumCards; // But not any player's cards +inline constexpr int kMaxAuctionLength = 4; +inline constexpr Player kFirstPlayer = 0; +enum class Suit { kClubs = 0, kDiamonds = 1, kHearts = 2, kSpades = 3 }; + +// State of a single trick. +class Trick { + public: + Trick() : Trick{kInvalidPlayer, 0} {} + Trick(Player leader, int card); + void Play(Player player, int card); + Suit LedSuit() const { return led_suit_; } + Player Winner() const { return winning_player_; } + Player Leader() const { return leader_; } + + private: + Suit led_suit_; + Suit winning_suit_; + int winning_rank_; + Player leader_; + Player winning_player_; +}; + +// State of an in-play game. Can be any phase of the game. +class SpadesState : public State { + public: + SpadesState(std::shared_ptr game, bool use_mercy_rule, + int mercy_threshold, int win_threshold, int score_partnership_0, + int score_partnership_1, int num_tricks); + Player CurrentPlayer() const override; + std::string ActionToString(Player player, Action action) const override; + std::string ToString() const override; + bool IsTerminal() const override { return phase_ == Phase::kGameOver; } + std::vector Returns() const override { return returns_; } + std::string ObservationString(Player player) const override; + void WriteObservationTensor(Player player, absl::Span values) const; + void ObservationTensor(Player player, + absl::Span values) const override; + std::unique_ptr Clone() const override { + return std::unique_ptr(new SpadesState(*this)); + } + std::vector LegalActions() const override; + std::vector> ChanceOutcomes() const override; + std::string Serialize() const override; + + // If the state is terminal, returns the indexes of the final contracts, into + // the arrays returned by PossibleFinalContracts and ScoreByContract. + std::array ContractIndexes() const; + + // Returns a mask indicating which final contracts are possible. + std::array PossibleContracts() const { + return possible_contracts_; + } + + // Private information tensor per player. + std::vector PrivateObservationTensor(Player player) const; + + // Public information. + std::vector PublicObservationTensor() const; + + // Current phase. + int CurrentPhase() const { return static_cast(phase_); } + + protected: + void DoApplyAction(Action action) override; + + private: + enum class Phase { kDeal, kAuction, kPlay, kGameOver }; + + std::vector DealLegalActions() const; + std::vector BiddingLegalActions() const; + std::vector PlayLegalActions() const; + void ApplyDealAction(int card); + void ApplyBiddingAction(int bid); + void ApplyPlayAction(int card); + + void ComputeScoreByContract() const; + void ScoreUp(); + Trick& CurrentTrick() { return tricks_[num_cards_played_ / kNumPlayers]; } + const Trick& CurrentTrick() const { + return tricks_[num_cards_played_ / kNumPlayers]; + } + std::array, kNumCards> OriginalDeal() const; + std::string FormatDeal() const; + std::string FormatAuction(bool trailing_query) const; + std::string FormatPlay() const; + std::string FormatResult() const; + + const bool use_mercy_rule_; + const int mercy_threshold_; + const int win_threshold_; + const std::array current_scores_; + const int num_tricks_; + + std::array num_player_tricks_ = {0, 0, 0, 0}; + int num_cards_played_ = 0; + Player current_player_ = 0; // During the play phase, the hand to play. + Phase phase_ = Phase::kDeal; + std::array contracts_ = {-1, -1, -1, -1}; + std::array tricks_{}; + std::vector returns_ = std::vector(kNumPlayers); + std::array, kNumCards> holder_{}; + std::array + possible_contracts_; // Array of bids 0-13 for each player (so 4x14 size) + bool is_spades_broken_ = false; +}; + +class SpadesGame : public Game { + public: + explicit SpadesGame(const GameParameters& params); + int NumDistinctActions() const override { + return kBiddingActionBase + kNumBids; + } + int MaxChanceOutcomes() const override { return kNumCards; } + std::unique_ptr NewInitialState() const override { + return std::unique_ptr(new SpadesState( + shared_from_this(), UseMercyRule(), MercyThreshold(), WinThreshold(), + PartnershipScore(0), PartnershipScore(1), NumTricks())); + } + int NumPlayers() const override { return kNumPlayers; } + double MinUtility() const override { return -kMaxScore; } + double MaxUtility() const override { return kMaxScore; } + + static int GetPlayTensorSize(int num_tricks) { + return kNumBids * kNumPlayers // What each player's contract is + + kNumCards // Our remaining cards + + num_tricks * kNumPlayers * kNumCards // Number of played tricks + + kNumTricks * kNumPlayers; // Number of tricks each player has won + } + + std::vector ObservationTensorShape() const override { + return {kPhaseInfoSize + + std::max(GetPlayTensorSize(NumTricks()), kAuctionTensorSize)}; + } + + int MaxGameLength() const override { return kMaxAuctionLength + kNumCards; } + int MaxChanceNodesInHistory() const override { return kNumCards; } + + std::unique_ptr DeserializeState( + const std::string& str) const override; + + // How many contracts there are. + int NumPossibleContracts() const { return kNumContracts; } + + // A string representation of a contract. + std::string ContractString(int bid) const; + + // Extra observation tensors. + int PrivateObservationTensorSize() const { return kNumCards; } + int PublicObservationTensorSize() const { return kPublicInfoTensorSize; } + + private: + bool UseMercyRule() const { + return ParameterValue("use_mercy_rule", true); + } + + int MercyThreshold() const { + return ParameterValue("mercy_threshold", -350); + } + + int WinThreshold() const { return ParameterValue("win_threshold", 500); } + + int PartnershipScore(int partnership) const { + return partnership ? ParameterValue("score_partnership_1", 0) + : ParameterValue("score_partnership_0", 0); + } + + int NumTricks() const { return ParameterValue("num_tricks", 2); } +}; + +} // namespace spades +} // namespace open_spiel + +#endif // OPEN_SPIEL_GAMES_SPADES_H_ diff --git a/open_spiel/games/spades/spades_scoring.cc b/open_spiel/games/spades/spades_scoring.cc new file mode 100644 index 0000000000..3c5983aeb5 --- /dev/null +++ b/open_spiel/games/spades/spades_scoring.cc @@ -0,0 +1,75 @@ +// Copyright 2019 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/spades/spades_scoring.h" + +#include +namespace open_spiel { +namespace spades { +namespace { + +// Score from contract is 10 times the bid (make contract arg negative if +// failed) +int ScoreContract(int contract) { return contract * 10; } + +// Penalty for accumulating 10 bags (-100 per instance) +int ScoreBagPenalties(int current_score, int overtricks) { + int current_bags = current_score % 10; + current_bags += overtricks; + return -100 * (current_bags / 10); +} + +// Bonus/penalty for succeeding/failing a Nil bid +int ScoreNil(int tricks) { return (tricks > 0) ? -100 : 100; } +} // namespace + +std::array Score( + const std::array contracts, + const std::array taken_tricks, + const std::array current_scores) { + std::array round_scores = {0, 0}; + + for (int pship = 0; pship < kNumPartnerships; ++pship) { + int contract = contracts[pship] + contracts[pship + 2]; + int contract_result = + (taken_tricks[pship] + taken_tricks[pship + 2]) - contract; + int bonuses = 0; + int contract_score = 0; + + // Score any nils + if (contracts[pship] == 0) { + bonuses += ScoreNil(taken_tricks[pship]); + } + if (contracts[pship + 2] == 0) { + bonuses += ScoreNil(taken_tricks[pship + 2]); + } + + // Score contracts and check for bag penalties + if (contract_result < 0) { + contract_score = ScoreContract(-contract); + } else { + contract_score = ScoreContract(contract); + + bonuses += contract_result + // Each overtrick (bag) is worth 1 point + ScoreBagPenalties(current_scores[pship], contract_result); + } + + round_scores[pship] = contract_score + bonuses; + } + + return round_scores; +} + +} // namespace spades +} // namespace open_spiel diff --git a/open_spiel/games/spades/spades_scoring.h b/open_spiel/games/spades/spades_scoring.h new file mode 100644 index 0000000000..79ae1586d2 --- /dev/null +++ b/open_spiel/games/spades/spades_scoring.h @@ -0,0 +1,64 @@ +// Copyright 2019 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_SPADES_SPADES_SCORING_H_ +#define OPEN_SPIEL_GAMES_SPADES_SPADES_SCORING_H_ + +// Scoring for partnership spades. +// See https://dkmgames.com/CardSharp/Spades/SpadesHelp.php + +#include +#include + +namespace open_spiel { +namespace spades { + +inline constexpr int kNumPlayers = 4; +constexpr char kPlayerChar[] = "NESW"; + +inline constexpr int kNumSuits = 4; +inline constexpr int kNumCardsPerSuit = 13; +inline constexpr int kNumPartnerships = 2; +inline constexpr int kNumBids = 14; // Bids can be from 0 to 13 tricks +inline constexpr int kNumCards = kNumSuits * kNumCardsPerSuit; +inline constexpr int kNumCardsPerHand = kNumCards / kNumPlayers; +inline constexpr int kNumTricks = kNumCardsPerHand; +inline constexpr int kMaxScore = 230; // Bid 13 (130) + Nil (100) + +std::array Score( + const std::array contracts, + const std::array taken_tricks, + const std::array current_scores); + +// All possible contracts. +inline constexpr int kNumContracts = kNumBids * kNumPlayers; + +constexpr std::array AllContracts() { + std::array contracts = {}; + int bid = 0; + for (int i = 0; i < kNumContracts; ++i) { + contracts[i] = bid++; + if (bid > kNumBids) { + bid = 0; + } + } + + return contracts; +} +inline constexpr std::array kAllContracts = AllContracts(); + +} // namespace spades +} // namespace open_spiel + +#endif // OPEN_SPIEL_GAMES_SPADES_SPADES_SCORING_H_ diff --git a/open_spiel/games/spades/spades_test.cc b/open_spiel/games/spades/spades_test.cc new file mode 100644 index 0000000000..73ab3eb880 --- /dev/null +++ b/open_spiel/games/spades/spades_test.cc @@ -0,0 +1,46 @@ +// Copyright 2019 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/spades/spades_scoring.h" +#include "open_spiel/spiel.h" +#include "open_spiel/spiel_utils.h" +#include "open_spiel/tests/basic_tests.h" + +namespace open_spiel { +namespace spades { +namespace { + +void ScoringTests() { + // Score returns difference in score (reward), not new overall score + SPIEL_CHECK_EQ(Score({4, 5, 5, 0}, {5, 3, 5, 0}, {0, 0})[0], 91); + SPIEL_CHECK_EQ(Score({13, 5, 0, 1}, {4, 6, 1, 2}, {0, 0})[0], -230); + SPIEL_CHECK_EQ(Score({3, 3, 3, 2}, {4, 2, 5, 2}, {99, 0})[0], -37); + SPIEL_CHECK_EQ(Score({2, 3, 3, 3}, {2, 4, 2, 5}, {0, 99})[1], -37); +} + +void BasicGameTests() { + testing::LoadGameTest("spades"); + testing::RandomSimTest(*LoadGame("spades"), 3); + testing::RandomSimTest( + *LoadGame("spades(score_partnership_0=59,score_partnership_1=99)"), 3); +} + +} // namespace +} // namespace spades +} // namespace open_spiel + +int main(int argc, char** argv) { + open_spiel::spades::ScoringTests(); + open_spiel::spades::BasicGameTests(); +} diff --git a/open_spiel/integration_tests/playthroughs/spades.txt b/open_spiel/integration_tests/playthroughs/spades.txt new file mode 100644 index 0000000000..668932472b --- /dev/null +++ b/open_spiel/integration_tests/playthroughs/spades.txt @@ -0,0 +1,1245 @@ +game: spades + +GameType.chance_mode = ChanceMode.EXPLICIT_STOCHASTIC +GameType.dynamics = Dynamics.SEQUENTIAL +GameType.information = Information.IMPERFECT_INFORMATION +GameType.long_name = "Partnership Spades" +GameType.max_num_players = 4 +GameType.min_num_players = 4 +GameType.parameter_specification = ["mercy_threshold", "num_tricks", "score_partnership_0", "score_partnership_1", "use_mercy_rule", "win_threshold"] +GameType.provides_information_state_string = False +GameType.provides_information_state_tensor = False +GameType.provides_observation_string = True +GameType.provides_observation_tensor = True +GameType.provides_factored_observation_string = False +GameType.reward_model = RewardModel.TERMINAL +GameType.short_name = "spades" +GameType.utility = Utility.GENERAL_SUM + +NumDistinctActions() = 66 +PolicyTensorShape() = [66] +MaxChanceOutcomes() = 52 +GetParameters() = {mercy_threshold=-350,num_tricks=2,score_partnership_0=0,score_partnership_1=0,use_mercy_rule=True,win_threshold=500} +NumPlayers() = 4 +MinUtility() = -230.0 +MaxUtility() = 230.0 +UtilitySum() = None +ObservationTensorShape() = [578] +ObservationTensorLayout() = TensorLayout.CHW +ObservationTensorSize() = 578 +MaxGameLength() = 56 +ToString() = "spades()" + +# State 0 +# S +# H +# D +# C +# S S +# H H +# D D +# C C +# S +# H +# D +# C +IsTerminal() = False +History() = [] +HistoryString() = "" +IsChanceNode() = True +IsSimultaneousNode() = False +CurrentPlayer() = -1 +ObservationString(0) = "S none\nH none\nD none\nC none\n" +ObservationString(1) = "S none\nH none\nD none\nC none\n" +ObservationString(2) = "S none\nH none\nD none\nC none\n" +ObservationString(3) = "S none\nH none\nD none\nC none\n" +ObservationTensor(0): zeros(578) +ObservationTensor(1): zeros(578) +ObservationTensor(2): zeros(578) +ObservationTensor(3): zeros(578) +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() = ["C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CT", "CJ", "CQ", "CK", "CA", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DT", "DJ", "DQ", "DK", "DA", "H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", "HT", "HJ", "HQ", "HK", "HA", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "ST", "SJ", "SQ", "SK", "SA"] + +# Apply action "H5" +action: 29 + +# State 1 +# S +# H 5 +# D +# C +# S S +# H H +# D D +# C C +# S +# H +# D +# C +IsTerminal() = False +History() = [29] +HistoryString() = "29" +IsChanceNode() = True +IsSimultaneousNode() = False +CurrentPlayer() = -1 +ObservationString(0) = "S none\nH 5\nD none\nC none\n" +ObservationString(1) = "S none\nH none\nD none\nC none\n" +ObservationString(2) = "S none\nH none\nD none\nC none\n" +ObservationString(3) = "S none\nH none\nD none\nC none\n" +ObservationTensor(0): zeros(578) +ObservationTensor(1): zeros(578) +ObservationTensor(2): zeros(578) +ObservationTensor(3): zeros(578) +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), (30,0.0196078), (31,0.0196078), (32,0.0196078), (33,0.0196078), (34,0.0196078), (35,0.0196078), (36,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, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51] +StringLegalActions() = ["C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CT", "CJ", "CQ", "CK", "CA", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DT", "DJ", "DQ", "DK", "DA", "H2", "H3", "H4", "H6", "H7", "H8", "H9", "HT", "HJ", "HQ", "HK", "HA", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "ST", "SJ", "SQ", "SK", "SA"] + +# Apply action "H2" +action: 26 + +# State 2 +# Apply action "H9" +action: 33 + +# State 3 +# Apply action "S4" +action: 41 + +# State 4 +# Apply action "C7" +action: 5 + +# State 5 +# Apply action "D2" +action: 13 + +# State 6 +# Apply action "SJ" +action: 48 + +# State 7 +# Apply action "S2" +action: 39 + +# State 8 +# Apply action "H4" +action: 28 + +# State 9 +# Apply action "DK" +action: 24 + +# State 10 +# Apply action "H7" +action: 31 + +# State 11 +# Apply action "D3" +action: 14 + +# State 12 +# Apply action "CQ" +action: 10 + +# State 13 +# Apply action "S7" +action: 44 + +# State 14 +# Apply action "DQ" +action: 23 + +# State 15 +# Apply action "H6" +action: 30 + +# State 16 +# Apply action "SQ" +action: 49 + +# State 17 +# Apply action "HK" +action: 37 + +# State 18 +# Apply action "C4" +action: 2 + +# State 19 +# Apply action "S8" +action: 45 + +# State 20 +# Apply action "C8" +action: 6 + +# State 21 +# Apply action "D8" +action: 19 + +# State 22 +# Apply action "HJ" +action: 35 + +# State 23 +# Apply action "SK" +action: 50 + +# State 24 +# Apply action "C9" +action: 7 + +# State 25 +# Apply action "C6" +action: 4 + +# State 26 +# Apply action "S5" +action: 42 + +# State 27 +# Apply action "CT" +action: 8 + +# State 28 +# Apply action "C3" +action: 1 + +# State 29 +# Apply action "C5" +action: 3 + +# State 30 +# Apply action "H8" +action: 32 + +# State 31 +# Apply action "H3" +action: 27 + +# State 32 +# Apply action "HT" +action: 34 + +# State 33 +# Apply action "SA" +action: 51 + +# State 34 +# Apply action "CK" +action: 11 + +# State 35 +# Apply action "C2" +action: 0 + +# State 36 +# Apply action "D7" +action: 18 + +# State 37 +# Apply action "D9" +action: 20 + +# State 38 +# Apply action "HQ" +action: 36 + +# State 39 +# Apply action "ST" +action: 47 + +# State 40 +# Apply action "HA" +action: 38 + +# State 41 +# Apply action "DA" +action: 25 + +# State 42 +# Apply action "D6" +action: 17 + +# State 43 +# Apply action "CA" +action: 12 + +# State 44 +# Apply action "S9" +action: 46 + +# State 45 +# Apply action "S6" +action: 43 + +# State 46 +# Apply action "CJ" +action: 9 + +# State 47 +# Apply action "D4" +action: 15 + +# State 48 +# Apply action "D5" +action: 16 + +# State 49 +# Apply action "DJ" +action: 22 + +# State 50 +# Apply action "DT" +action: 21 + +# State 51 +# Apply action "S3" +action: 40 + +# State 52 +# S Q9 +# H AT54 +# D 75 +# C Q9873 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 65 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 0 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q9873\n" +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 65\n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ4\n" +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n" +ObservationTensor(0): binvec(578, 0x2000000000000004720a00c22024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(1): binvec(578, 0x20000000000000018041ae004181000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(2): binvec(578, 0x20000000000000020504501d8208000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(3): binvec(578, 0x200000000000000808b001201c52000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65] +StringLegalActions() = ["Nil", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"] + +# Apply action "11" +action: 63 + +# State 53 +# S Q9 +# H AT54 +# D 75 +# C Q9873 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 65 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +# +# North East South West +# 11 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 1 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q9873\n\nNorth East South West \n11 " +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 65\n\nNorth East South West \n11 ?" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ4\n\nNorth East South West \n11 " +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n\nNorth East South West \n11 " +ObservationTensor(0): binvec(578, 0x2001000000000004720a00c22024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(1): binvec(578, 0x20010000000000018041ae004181000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(2): binvec(578, 0x20010000000000020504501d8208000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(3): binvec(578, 0x200100000000000808b001201c52000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65] +StringLegalActions() = ["Nil", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"] + +# Apply action "Nil" +action: 52 + +# State 54 +# S Q9 +# H AT54 +# D 75 +# C Q9873 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 65 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +# +# North East South West +# 11 Nil +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 2 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q9873\n\nNorth East South West \n11 Nil " +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 65\n\nNorth East South West \n11 Nil " +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ4\n\nNorth East South West \n11 Nil ?" +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n\nNorth East South West \n11 Nil " +ObservationTensor(0): binvec(578, 0x2001200000000004720a00c22024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(1): binvec(578, 0x20012000000000018041ae004181000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(2): binvec(578, 0x20012000000000020504501d8208000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(3): binvec(578, 0x200120000000000808b001201c52000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [52, 53, 54] +StringLegalActions() = ["Nil", "1", "2"] + +# Apply action "2" +action: 54 + +# State 55 +# S Q9 +# H AT54 +# D 75 +# C Q9873 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 65 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +# +# North East South West +# 11 Nil 2 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 3 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q9873\n\nNorth East South West \n11 Nil 2 " +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 65\n\nNorth East South West \n11 Nil 2 " +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ4\n\nNorth East South West \n11 Nil 2 " +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n\nNorth East South West \n11 Nil 2 \n?" +ObservationTensor(0): binvec(578, 0x2001200020000004720a00c22024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(1): binvec(578, 0x20012000200000018041ae004181000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(2): binvec(578, 0x20012000200000020504501d8208000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(3): binvec(578, 0x200120002000000808b001201c52000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65] +StringLegalActions() = ["Nil", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"] + +# Apply action "6" +action: 58 + +# State 56 +# S Q9 +# H AT54 +# D 75 +# C Q9873 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 65 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +# +# North East South West +# 11 Nil 2 6 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 0 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q9873\n\nNorth East South West \n11 Nil 2 6 " +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 65\n\nNorth East South West \n11 Nil 2 6 " +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ4\n\nNorth East South West \n11 Nil 2 6 " +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n\nNorth East South West \n11 Nil 2 6 " +ObservationTensor(0): binvec(578, 0x1001200020000804720a00c22024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(1): binvec(578, 0x10012000200008018041ae004181000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(2): binvec(578, 0x10012000200008020504501d8208000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +ObservationTensor(3): binvec(578, 0x100120002000080808b001201c52000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [1, 5, 6, 7, 10, 16, 18, 28, 29, 34, 38] +StringLegalActions() = ["C3", "C7", "C8", "C9", "CQ", "D5", "D7", "H4", "H5", "HT", "HA"] + +# Apply action "C7" +action: 5 + +# State 57 +# S Q9 +# H AT54 +# D 75 +# C Q983 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 65 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 +# +# Tricks taken: +# +# North East South West +# 0 0 0 0 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 1 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 65\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ4\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804320a00c22024040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(1): binvec(578, 0x10012000200008018041ae004181000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(2): binvec(578, 0x10012000200008020504501d8208000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(3): binvec(578, 0x100120002000080808b001201c52000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008004002001000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [3, 4] +StringLegalActions() = ["C5", "C6"] + +# Apply action "C5" +action: 3 + +# State 58 +# S Q9 +# H AT54 +# D 75 +# C Q983 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 6 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 +# +# Tricks taken: +# +# North East South West +# 0 0 0 0 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 2 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 6\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ4\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804320a00c22024040000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(1): binvec(578, 0x10012000200008008041ae004181100000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(2): binvec(578, 0x10012000200008020504501d8208000000000000000000000000000400000000000100000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(3): binvec(578, 0x100120002000080808b001201c52000000000000004000000000001000000000000000000000000000000000000000000000000000000000000000000000000000008004002001000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [2, 9, 11] +StringLegalActions() = ["C4", "CJ", "CK"] + +# Apply action "C4" +action: 2 + +# State 59 +# S Q9 +# H AT54 +# D 75 +# C Q983 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 6 +# S J5 +# H QJ987 +# D QT6 +# C KJ +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 +# +# Tricks taken: +# +# North East South West +# 0 0 0 0 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 3 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 6\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationString(3) = "S KT8432\nH 63\nD 43\nC AT2\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 0 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804320a00c22024040000000000010000000000002000000000000000000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(1): binvec(578, 0x10012000200008008041ae004181100000000000020000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(2): binvec(578, 0x10012000200008000504501d8208200000000000000000000000000400000000000100000000000000000000000000000000000000000000000000000000000000008004002001000) +ObservationTensor(3): binvec(578, 0x100120002000080808b001201c52000000000000004000000000001000000000000200000000000000000000000000000000000000000000000000000000000000008004002001000) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [0, 8, 12] +StringLegalActions() = ["C2", "CT", "CA"] + +# Apply action "CA" +action: 12 + +# State 60 +# S Q9 +# H AT54 +# D 75 +# C Q983 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C T2 C 6 +# S J5 +# H QJ987 +# D QT6 +# C KJ +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# +# Tricks taken: +# +# North East South West +# 0 0 0 1 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 3 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 6\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(3) = "S KT8432\nH 63\nD 43\nC T2\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804320a00c22024000000000000000000000000000000000000000000000000000004000000000001000000000000200000000000000080000000008004002000800) +ObservationTensor(1): binvec(578, 0x10012000200008008041ae004181000000000000000000000000000000000000000000000000000010000000000002000000000000000800000000004000000000008004002000800) +ObservationTensor(2): binvec(578, 0x10012000200008000504501d8208000000000000000000000000000000000000000000000000000020000000000000008000000000040000000000010000000000008004002000800) +ObservationTensor(3): binvec(578, 0x1001200020000808083001201c52000000000000000000000000000000000000000000000000000000080000000000400000000000100000000000020000000000008004002000800) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [0, 8, 14, 15, 27, 30] +StringLegalActions() = ["C2", "CT", "D3", "D4", "H3", "H6"] + +# Apply action "D4" +action: 15 + +# State 61 +# S Q9 +# H AT54 +# D 75 +# C Q983 +# S KT8432 S A76 +# H 63 H K2 +# D 3 D AKJ982 +# C T2 C 6 +# S J5 +# H QJ987 +# D QT6 +# C KJ +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 +# +# Tricks taken: +# +# North East South West +# 0 0 0 1 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 0 +ObservationString(0) = "S Q9\nH AT54\nD 75\nC Q983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 6\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(3) = "S KT8432\nH 63\nD 3\nC T2\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804320a00c22024000000000000000000000000000000000000000000100000000004000000000001000000000000200000000000000080000000008004002000800) +ObservationTensor(1): binvec(578, 0x10012000200008008041ae004181000000000000000000000000000001000000000000000000000010000000000002000000000000000800000000004000000000008004002000800) +ObservationTensor(2): binvec(578, 0x10012000200008000504501d8208000000000000000010000000000000000000000000000000000020000000000000008000000000040000000000010000000000008004002000800) +ObservationTensor(3): binvec(578, 0x1001200020000808082001201c52000100000000000000000000000000000000000000000000000000080000000000400000000000100000000000020000000000008004002000800) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [16, 18] +StringLegalActions() = ["D5", "D7"] + +# Apply action "D7" +action: 18 + +# State 62 +# S Q9 +# H AT54 +# D 5 +# C Q983 +# S KT8432 S A76 +# H 63 H K2 +# D 3 D AKJ982 +# C T2 C 6 +# S J5 +# H QJ987 +# D QT6 +# C KJ +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 D7 +# +# Tricks taken: +# +# North East South West +# 0 0 0 1 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 1 +ObservationString(0) = "S Q9\nH AT54\nD 5\nC Q983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(1) = "S A76\nH K2\nD AKJ982\nC 6\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(3) = "S KT8432\nH 63\nD 3\nC T2\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804320800c22024000020000000000000000000000000000000000000100000000004000000000001000000000000200000000000000080000000008004002000800) +ObservationTensor(1): binvec(578, 0x10012000200008008041ae004181000000000000000000000000000001000000000000020000000010000000000002000000000000000800000000004000000000008004002000800) +ObservationTensor(2): binvec(578, 0x10012000200008000504501d8208000000000000000010000000000000200000000000000000000020000000000000008000000000040000000000010000000000008004002000800) +ObservationTensor(3): binvec(578, 0x1001200020000808082001201c52000100000000000002000000000000000000000000000000000000080000000000400000000000100000000000020000000000008004002000800) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [13, 19, 20, 22, 24, 25] +StringLegalActions() = ["D2", "D8", "D9", "DJ", "DK", "DA"] + +# Apply action "D8" +action: 19 + +# State 63 +# S Q9 +# H AT54 +# D 5 +# C Q983 +# S KT8432 S A76 +# H 63 H K2 +# D 3 D AKJ92 +# C T2 C 6 +# S J5 +# H QJ987 +# D QT6 +# C KJ +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 D7 D8 +# +# Tricks taken: +# +# North East South West +# 0 0 0 1 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 2 +ObservationString(0) = "S Q9\nH AT54\nD 5\nC Q983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(1) = "S A76\nH K2\nD AKJ92\nC 6\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(2) = "S J5\nH QJ987\nD QT6\nC KJ\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationString(3) = "S KT8432\nH 63\nD 3\nC T2\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 \n\nTricks taken:\n\nNorth East South West\n0 0 0 1 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804320800c22024000020000000000001000000000000000000000000100000000004000000000001000000000000200000000000000080000000008004002000800) +ObservationTensor(1): binvec(578, 0x10012000200008008040ae004181000010000000000000000000000001000000000000020000000010000000000002000000000000000800000000004000000000008004002000800) +ObservationTensor(2): binvec(578, 0x10012000200008000504501d8208000000000000000010000000000000200000000000010000000020000000000000008000000000040000000000010000000000008004002000800) +ObservationTensor(3): binvec(578, 0x1001200020000808082001201c52000100000000000002000000000000100000000000000000000000080000000000400000000000100000000000020000000000008004002000800) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [17, 21, 23] +StringLegalActions() = ["D6", "DT", "DQ"] + +# Apply action "DT" +action: 21 + +# State 64 +# Apply action "HQ" +action: 36 + +# State 65 +# Apply action "H6" +action: 30 + +# State 66 +# Apply action "H4" +action: 28 + +# State 67 +# Apply action "H2" +action: 26 + +# State 68 +# Apply action "CK" +action: 11 + +# State 69 +# Apply action "CT" +action: 8 + +# State 70 +# Apply action "CQ" +action: 10 + +# State 71 +# Apply action "C6" +action: 4 + +# State 72 +# Apply action "DQ" +action: 23 + +# State 73 +# Apply action "D3" +action: 14 + +# State 74 +# Apply action "D5" +action: 16 + +# State 75 +# Apply action "D9" +action: 20 + +# State 76 +# Apply action "H7" +action: 31 + +# State 77 +# Apply action "H3" +action: 27 + +# State 78 +# Apply action "HT" +action: 34 + +# State 79 +# Apply action "HK" +action: 37 + +# State 80 +# Apply action "DK" +action: 24 + +# State 81 +# Apply action "D6" +action: 17 + +# State 82 +# Apply action "C2" +action: 0 + +# State 83 +# Apply action "H5" +action: 29 + +# State 84 +# Apply action "D2" +action: 13 + +# State 85 +# Apply action "SJ" +action: 48 + +# State 86 +# Apply action "S2" +action: 39 + +# State 87 +# Apply action "S9" +action: 46 + +# State 88 +# Apply action "HJ" +action: 35 + +# State 89 +# Apply action "S8" +action: 45 + +# State 90 +# Apply action "HA" +action: 38 + +# State 91 +# Apply action "DA" +action: 25 + +# State 92 +# S Q +# H +# D +# C 983 +# S KT43 S A76 +# H H +# D D J +# C C +# S 5 +# H 98 +# D +# C J +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 D7 D8 DT +# HQ H6 H4 H2 +# CK CT CQ C6 +# DQ D3 D5 D9 +# H7 H3 HT HK +# DK D6 C2 H5 +# D2 SJ S2 S9 +# HJ S8 HA DA +# +# Tricks taken: +# +# North East South West +# 0 2 5 2 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 3 +ObservationString(0) = "S Q\nH none\nD none\nC 983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(1) = "S A76\nH none\nD J\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(2) = "S 5\nH 98\nD none\nC J\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(3) = "S KT43\nH none\nD none\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804300000000004000000000000000000000000000000000000000000000000000000000000020000000004000000000000001000000000000000408001000100400) +ObservationTensor(1): binvec(578, 0x1001200020000800000020000181000000000000000000000000000000000000000000000000000000000040000000000000010000000000000004000000000020008001000100400) +ObservationTensor(2): binvec(578, 0x10012000200008000400000c0200000000000000000000000000000000000000000000000000000000000000100000000000000040000000000200000000040000008001000100400) +ObservationTensor(3): binvec(578, 0x1001200020000800000000000c12000000000000000000000000000000000000000000000000000000000000000400000000002000000000400000000000000100008001000100400) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [40, 41, 47, 50] +StringLegalActions() = ["S3", "S4", "ST", "SK"] + +# Apply action "ST" +action: 47 + +# State 93 +# S Q +# H +# D +# C 983 +# S K43 S A76 +# H H +# D D J +# C C +# S 5 +# H 98 +# D +# C J +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 D7 D8 DT +# HQ H6 H4 H2 +# CK CT CQ C6 +# DQ D3 D5 D9 +# H7 H3 HT HK +# DK D6 C2 H5 +# D2 SJ S2 S9 +# HJ S8 HA DA +# ST +# +# Tricks taken: +# +# North East South West +# 0 2 5 2 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 0 +ObservationString(0) = "S Q\nH none\nD none\nC 983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(1) = "S A76\nH none\nD J\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(2) = "S 5\nH 98\nD none\nC J\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(3) = "S K43\nH none\nD none\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804300000000004000000000000000000000000000000000000000000000000001000000000020000000004000000000000001000000000000000408001000100400) +ObservationTensor(1): binvec(578, 0x1001200020000800000020000181000000000000000000000000000000000000010000000000000000000040000000000000010000000000000004000000000020008001000100400) +ObservationTensor(2): binvec(578, 0x10012000200008000400000c0200000000000000000000000000100000000000000000000000000000000000100000000000000040000000000200000000040000008001000100400) +ObservationTensor(3): binvec(578, 0x1001200020000800000000000c02000000000001000000000000000000000000000000000000000000000000000400000000002000000000400000000000000100008001000100400) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [49] +StringLegalActions() = ["SQ"] + +# Apply action "SQ" +action: 49 + +# State 94 +# S +# H +# D +# C 983 +# S K43 S A76 +# H H +# D D J +# C C +# S 5 +# H 98 +# D +# C J +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 D7 D8 DT +# HQ H6 H4 H2 +# CK CT CQ C6 +# DQ D3 D5 D9 +# H7 H3 HT HK +# DK D6 C2 H5 +# D2 SJ S2 S9 +# HJ S8 HA DA +# ST SQ +# +# Tricks taken: +# +# North East South West +# 0 2 5 2 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47, 49] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47, 49" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 1 +ObservationString(0) = "S none\nH none\nD none\nC 983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(1) = "S A76\nH none\nD J\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(2) = "S 5\nH 98\nD none\nC J\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(3) = "S K43\nH none\nD none\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804300000000000000000000000400000000000000000000000000000000000001000000000020000000004000000000000001000000000000000408001000100400) +ObservationTensor(1): binvec(578, 0x1001200020000800000020000181000000000000000000000000000000000000010000000000000400000040000000000000010000000000000004000000000020008001000100400) +ObservationTensor(2): binvec(578, 0x10012000200008000400000c0200000000000000000000000000100000000000004000000000000000000000100000000000000040000000000200000000040000008001000100400) +ObservationTensor(3): binvec(578, 0x1001200020000800000000000c02000000000001000000000000040000000000000000000000000000000000000400000000002000000000400000000000000100008001000100400) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [43, 44, 51] +StringLegalActions() = ["S6", "S7", "SA"] + +# Apply action "S6" +action: 43 + +# State 95 +# S +# H +# D +# C 983 +# S K43 S A7 +# H H +# D D J +# C C +# S 5 +# H 98 +# D +# C J +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 D7 D8 DT +# HQ H6 H4 H2 +# CK CT CQ C6 +# DQ D3 D5 D9 +# H7 H3 HT HK +# DK D6 C2 H5 +# D2 SJ S2 S9 +# HJ S8 HA DA +# ST SQ S6 +# +# Tricks taken: +# +# North East South West +# 0 2 5 2 +IsTerminal() = False +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47, 49, 43] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47, 49, 43" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = 2 +ObservationString(0) = "S none\nH none\nD none\nC 983\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(1) = "S A7\nH none\nD J\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(2) = "S 5\nH 98\nD none\nC J\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationString(3) = "S K43\nH none\nD none\nC none\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 \n\nTricks taken:\n\nNorth East South West\n0 2 5 2 \n" +ObservationTensor(0): binvec(578, 0x1001200020000804300000000000000000000000400000000001000000000000000000000000001000000000020000000004000000000000001000000000000000408001000100400) +ObservationTensor(1): binvec(578, 0x1001200020000800000020000081000000000010000000000000000000000000010000000000000400000040000000000000010000000000000004000000000020008001000100400) +ObservationTensor(2): binvec(578, 0x10012000200008000400000c0200000000000000000000000000100000000000004000000000010000000000100000000000000040000000000200000000040000008001000100400) +ObservationTensor(3): binvec(578, 0x1001200020000800000000000c02000000000001000000000000040000000000100000000000000000000000000400000000002000000000400000000000000100008001000100400) +Rewards() = [0, 0, 0, 0] +Returns() = [0, 0, 0, 0] +LegalActions() = [42] +StringLegalActions() = ["S5"] + +# Apply action "S5" +action: 42 + +# State 96 +# Apply action "C8" +action: 6 + +# State 97 +# Apply action "S7" +action: 44 + +# State 98 +# Apply action "CJ" +action: 9 + +# State 99 +# Apply action "SK" +action: 50 + +# State 100 +# Apply action "S4" +action: 41 + +# State 101 +# Apply action "C3" +action: 1 + +# State 102 +# Apply action "SA" +action: 51 + +# State 103 +# Apply action "H8" +action: 32 + +# State 104 +# Apply action "DJ" +action: 22 + +# State 105 +# Apply action "H9" +action: 33 + +# State 106 +# Apply action "S3" +action: 40 + +# State 107 +# Apply action "C9" +action: 7 + +# State 108 +# S Q9 +# H AT54 +# D 75 +# C Q9873 +# S KT8432 S A76 +# H 63 H K2 +# D 43 D AKJ982 +# C AT2 C 65 +# S J5 +# H QJ987 +# D QT6 +# C KJ4 +# +# North East South West +# 11 Nil 2 6 +# +# N E S W N E S +# C7 C5 C4 CA +# D4 D7 D8 DT +# HQ H6 H4 H2 +# CK CT CQ C6 +# DQ D3 D5 D9 +# H7 H3 HT HK +# DK D6 C2 H5 +# D2 SJ S2 S9 +# HJ S8 HA DA +# ST SQ S6 S5 +# C8 S7 CJ SK +# S4 C3 SA H8 +# DJ H9 S3 C9 +# +# Tricks taken: +# +# North East South West +# 1 3 5 4 +# +# Score: N/S -130 E/W -39 +IsTerminal() = True +History() = [29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47, 49, 43, 42, 6, 44, 9, 50, 41, 1, 51, 32, 22, 33, 40, 7] +HistoryString() = "29, 26, 33, 41, 5, 13, 48, 39, 28, 24, 31, 14, 10, 44, 23, 30, 49, 37, 2, 45, 6, 19, 35, 50, 7, 4, 42, 8, 1, 3, 32, 27, 34, 51, 11, 0, 18, 20, 36, 47, 38, 25, 17, 12, 46, 43, 9, 15, 16, 22, 21, 40, 63, 52, 54, 58, 5, 3, 2, 12, 15, 18, 19, 21, 36, 30, 28, 26, 11, 8, 10, 4, 23, 14, 16, 20, 31, 27, 34, 37, 24, 17, 0, 29, 13, 48, 39, 46, 35, 45, 38, 25, 47, 49, 43, 42, 6, 44, 9, 50, 41, 1, 51, 32, 22, 33, 40, 7" +IsChanceNode() = False +IsSimultaneousNode() = False +CurrentPlayer() = -4 +ObservationString(0) = " S Q9\n H AT54\n D 75\n C Q9873\nS KT8432 S A76\nH 63 H K2\nD 43 D AKJ982\nC AT2 C 65\n S J5\n H QJ987\n D QT6\n C KJ4\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 S5 \nC8 S7 CJ SK \n S4 C3 SA H8 \n DJ H9 S3 C9 \n\nTricks taken:\n\nNorth East South West\n1 3 5 4 \n\nScore: N/S -130 E/W -39" +ObservationString(1) = " S Q9\n H AT54\n D 75\n C Q9873\nS KT8432 S A76\nH 63 H K2\nD 43 D AKJ982\nC AT2 C 65\n S J5\n H QJ987\n D QT6\n C KJ4\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 S5 \nC8 S7 CJ SK \n S4 C3 SA H8 \n DJ H9 S3 C9 \n\nTricks taken:\n\nNorth East South West\n1 3 5 4 \n\nScore: N/S -130 E/W -39" +ObservationString(2) = " S Q9\n H AT54\n D 75\n C Q9873\nS KT8432 S A76\nH 63 H K2\nD 43 D AKJ982\nC AT2 C 65\n S J5\n H QJ987\n D QT6\n C KJ4\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 S5 \nC8 S7 CJ SK \n S4 C3 SA H8 \n DJ H9 S3 C9 \n\nTricks taken:\n\nNorth East South West\n1 3 5 4 \n\nScore: N/S -130 E/W -39" +ObservationString(3) = " S Q9\n H AT54\n D 75\n C Q9873\nS KT8432 S A76\nH 63 H K2\nD 43 D AKJ982\nC AT2 C 65\n S J5\n H QJ987\n D QT6\n C KJ4\n\nNorth East South West \n11 Nil 2 6 \n\nN E S W N E S\nC7 C5 C4 CA \n D4 D7 D8 DT \n HQ H6 H4 H2 \n CK CT CQ C6 \n DQ D3 D5 D9 \n H7 H3 HT HK \n DK D6 C2 H5 \n D2 SJ S2 S9 \n HJ S8 HA DA \n ST SQ S6 S5 \nC8 S7 CJ SK \n S4 C3 SA H8 \n DJ H9 S3 C9 \n\nTricks taken:\n\nNorth East South West\n1 3 5 4 \n\nScore: N/S -130 E/W -39" +ObservationTensor(0): binvec(578, 0x801200020000800000000000000000000000000000000000000000000000000000000000000000001000000000000000020000000000000004000000000000008004000800100100) +ObservationTensor(1): binvec(578, 0x801200020000800000000000000000000000000000000000000000000000000000000000000000000000200000000000000040000000000000080001000000000004000800100100) +ObservationTensor(2): binvec(578, 0x801200020000800000000000000000000000000000000000000000000000000000000000000000000000000400000000000000800010000000000000000200000004000800100100) +ObservationTensor(3): binvec(578, 0x801200020000800000000000000000000000000000000000000000000000000000000000000000000000000008000100000000000000002000000000000000400004000800100100) +Rewards() = [-130, -39, -130, -39] +Returns() = [-130, -39, -130, -39] diff --git a/open_spiel/python/tests/pyspiel_test.py b/open_spiel/python/tests/pyspiel_test.py index c25ef77f9f..eb5fba42b0 100644 --- a/open_spiel/python/tests/pyspiel_test.py +++ b/open_spiel/python/tests/pyspiel_test.py @@ -126,6 +126,7 @@ "skat", "start_at", "solitaire", + "spades", "stones_and_gems", "tarok", "tic_tac_toe",