2023-07-04 18:52:59 +02:00
|
|
|
from typing import List
|
2023-05-13 23:09:28 +02:00
|
|
|
|
|
|
|
import hanabi
|
2023-07-04 18:52:59 +02:00
|
|
|
from hanabi import constants
|
|
|
|
from hanabi.live.variants import Variant
|
2023-05-13 23:09:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
class HanabLiveInstance(hanabi.HanabiInstance):
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
deck: List[hanabi.DeckCard],
|
|
|
|
num_players: int,
|
2023-05-14 16:44:23 +02:00
|
|
|
variant_id: int,
|
|
|
|
one_extra_card: bool = False,
|
|
|
|
one_less_card: bool = False,
|
|
|
|
*args, **kwargs
|
2023-05-13 23:09:28 +02:00
|
|
|
):
|
2023-05-14 16:44:23 +02:00
|
|
|
assert 2 <= num_players <= 6
|
|
|
|
hand_size = constants.HAND_SIZES[num_players]
|
|
|
|
if one_less_card:
|
|
|
|
hand_size -= 1
|
|
|
|
if one_extra_card:
|
|
|
|
hand_size += 1
|
|
|
|
|
|
|
|
super().__init__(deck, num_players, hand_size=hand_size, *args, **kwargs)
|
2023-05-13 23:09:28 +02:00
|
|
|
self.variant_id = variant_id
|
|
|
|
self.variant = Variant.from_db(self.variant_id)
|
|
|
|
|
2023-05-19 12:43:49 +02:00
|
|
|
@staticmethod
|
|
|
|
def select_standard_variant_id(instance: hanabi.HanabiInstance):
|
|
|
|
err_msg = "Hanabi instance not supported by hanab.live, cannot convert to HanabLiveInstance: "
|
|
|
|
assert 3 <= instance.num_suits <= 6, \
|
|
|
|
err_msg + "Illegal number of suits ({}) found, must be in range [3,6]".format(instance.num_suits)
|
|
|
|
assert 0 <= instance.num_dark_suits <= 2, \
|
|
|
|
err_msg + "Illegal number of dark suits ({}) found, must be in range [0,2]".format(instance.num_dark_suits)
|
|
|
|
assert 4 <= instance.num_suits - instance.num_dark_suits, \
|
|
|
|
err_msg + "Illegal ratio of dark suits to suits, can have at most {} dark suits with {} total suits".format(
|
|
|
|
max(instance.num_suits - 4, 0), instance.num_suits
|
|
|
|
)
|
|
|
|
return constants.VARIANT_IDS_STANDARD_DISTRIBUTIONS[instance.num_suits][instance.num_dark_suits]
|
|
|
|
|
2023-05-13 23:09:28 +02:00
|
|
|
|
|
|
|
class HanabLiveGameState(hanabi.GameState):
|
2023-06-24 17:23:29 +02:00
|
|
|
def __init__(self, instance: HanabLiveInstance, starting_player: int = 0):
|
|
|
|
super().__init__(instance, starting_player)
|
2023-05-13 23:09:28 +02:00
|
|
|
self.instance: HanabLiveInstance = instance
|
|
|
|
|
|
|
|
def make_action(self, action):
|
|
|
|
match action.type:
|
|
|
|
case hanabi.ActionType.ColorClue | hanabi.ActionType.RankClue:
|
|
|
|
assert(self.clues > 0)
|
|
|
|
self.actions.append(action)
|
|
|
|
self.clues -= self.instance.clue_increment
|
|
|
|
self._make_turn()
|
|
|
|
# TODO: could check that the clue specified is in fact legal
|
|
|
|
case hanabi.ActionType.Play:
|
|
|
|
self.play(action.target)
|
|
|
|
case hanabi.ActionType.Discard:
|
|
|
|
self.discard(action.target)
|
|
|
|
case hanabi.ActionType.EndGame | hanabi.ActionType.VoteTerminate:
|
|
|
|
self.over = True
|
|
|
|
|
|
|
|
def _waste_clue(self) -> hanabi.Action:
|
|
|
|
for player in range(self.turn + 1, self.turn + self.num_players):
|
|
|
|
for card in self.hands[player % self.num_players]:
|
|
|
|
for rank in self.instance.variant.ranks:
|
|
|
|
if self.instance.variant.rank_touches(card, rank):
|
|
|
|
return hanabi.Action(
|
|
|
|
hanabi.ActionType.RankClue,
|
|
|
|
player % self.num_players,
|
|
|
|
rank
|
|
|
|
)
|
|
|
|
for color in range(self.instance.variant.num_colors):
|
|
|
|
if self.instance.variant.color_touches(card, color):
|
|
|
|
return hanabi.Action(
|
|
|
|
hanabi.ActionType.ColorClue,
|
|
|
|
player % self.num_players,
|
|
|
|
color
|
|
|
|
)
|
|
|
|
raise RuntimeError("Current game state did not permit any legal clue."
|
|
|
|
"This case is incredibly rare and currently not handled.")
|