text: from hanabi_sim import *
  from collections import Counter
  
  class HanabiExpert(object):
      def __init__(self, game):
          self.game = game
  
      def get_action(self, state):
          remaining_counter = Counter()
          for card in state.deck.cards() + [x.card for x in state.player_hands[state.cur_player]]:
              remaining_counter[card] += 1
          all_remaining = set()
          for card in state.deck.cards() + [x.card for hand in state.player_hands for x in hand]:
              all_remaining.add(card)
  
          game = self.game
          valid_actions = game.get_valid_actions(state)
          features = game.get_state_vector(state)
  
          # Check over own cards
          playable_idx = set()
          dead_idx = set()
          indispensable_idx = set()
          for card_idx, card in enumerate(state.player_hands[state.cur_player]):
              if card is None:
                  raise RuntimeError("Attempting to play with less than the maximum number of cards.")
              number_vector = [1 if num in card.possible_numbers else 0 for num in range(1, game.max_number+1)]
              color_vector = [1 if color in card.possible_colors else 0 for color in game.colors]
  
              total_possibilities = 0
              indispensable_poss = 0
              playable_poss = 0
              dead_poss = 0
              for possible_card in remaining_counter:
                  if remaining_counter[possible_card] > 0:
                      if possible_card.number in card.possible_numbers and possible_card.color in card.possible_colors:
                          total_possibilities += 1
                          if state.played_numbers[possible_card.color] + 1 == possible_card.number:
                              playable_poss += 1
                          dead = False
                          # Check if dead
                          if state.played_numbers[possible_card.color] >= possible_card.number:
                              dead_poss += 1
                              dead = True
                          else:
                              for num in range(state.played_numbers[possible_card.color]+1, possible_card.number):
                                  if Card(num, possible_card.color) not in all_remaining:
                                      dead_poss += 1
                                      dead = True
                                      break
                          # Note: indispensable is mutually exclusive with dead
                          if not dead and remaining_counter[possible_card] == 1:
                              indispensable_poss += 1
              if total_possibilities == 0:
                  raise RuntimeError("There are zero possibilities for what this card can be. Something went wrong.")
              playable = int(playable_poss == total_possibilities)
              indispensable = int(indispensable_poss == total_possibilities)
              dead = int(dead_poss == total_possibilities)
              if playable == 1:
                  playable_idx.add(card_idx)
              if indispensable == 1:
                  indispensable_idx.add(card_idx)
              if dead_idx == 1:
                  dead_idx.add(card_idx)
  
          if len(playable_idx) > 0:
              action = list(playable_idx)[0]
              return action
  
          def hint_to_idx(hint_type, person, hint):
              if hint_type == 'color':
                  action = game.cards_per_player * 2 + (game.max_number + game.num_colors) * person + game.max_number + game.colors.index(hint)
              else:
                  action = game.cards_per_player * 2 + (game.max_number + game.num_colors) * person + hint - 1
              return action
  
          # Pick hint
          if state.hint_tokens > 0:
              tier1_hints = set()
              tier2_hints = set()
              tier3_hints = set()
              unhinted_numbers = []
              for other_player_num in range(1, game.num_players):
                  player_id = (state.cur_player + other_player_num) % game.num_players
                  for hinted_card in state.player_hands[player_id]:
                      if hinted_card.number == state.played_numbers[hinted_card.color] + 1:
                          # Playable
                          if hinted_card.number_hint and not hinted_card.color_hint:
                              tier1_hints.add(('color', other_player_num-1, hinted_card.color))
                          if hinted_card.color_hint and not hinted_card.number_hint:
                              tier1_hints.add(('number', other_player_num-1, hinted_card.number))
                          if not hinted_card.number_hint and not hinted_card.number_hint:
                              tier2_hints.add(('number', other_player_num-1, hinted_card.number))
                      if not hinted_card.number_hint and hinted_card.number > min(state.played_numbers.values()):
                          tier3_hints.add(('number', other_player_num-1, hinted_card.number))
  
              if len(tier1_hints) > 0:
                  hint_type, person, hint = list(tier1_hints)[0]
                  return hint_to_idx(hint_type, person, hint)
              if len(tier2_hints) > 0:
                  hint_type, person, hint = list(tier2_hints)[0]
                  return hint_to_idx(hint_type, person, hint)
              if len(tier3_hints) > 0:
                  hint_type, person, hint = list(sorted(list(tier3_hints)))[0]
                  return hint_to_idx(hint_type, person, hint)
  
          # Pick safe discard
          if len(dead_idx) > 0:
              discard = list(dead_idx)[0]
          elif len(indispensable_idx) < game.cards_per_player:
              for idx in range(game.cards_per_player):
                  if idx not in indispensable_idx:
                      discard = idx
          elif len(valid_actions) > 2 * game.cards_per_player:
              # try to give any hint possible
              return sorted(valid_actions)[2 * game.cards_per_player]
          else:
              discard = game.cards_per_player - 1
  
          return discard + game.cards_per_player
  