import random from operator import attrgetter def chunks(lst, n): """Yield successive n-sized chunks from lst.""" for i in range(0, len(lst), n): yield lst[i:i + n] class Card: def __init__(self, color, value, idx=None): self.color = color self.value = value self.idx = idx def __str__(self): sc = self.color if self.color else ' ' sv = str(self.value) return sc+' '+sv def __repr__(self): return str(self) def json_serialise(self): if self.color and self.color != '-': s = "{}{:02d}".format(self.color, self.value) else: s = "{}{}".format(self.value.lower(), self.idx) return s def get_higher_card(card1: Card, card2: Card, trump_color: str): """ compares two cards, assuming card1 was played first and card2 played second. returns: True if card2 beats card1, otherwise False """ if card2.value == 'N': # no way to win if 'N' comes in second return False elif card1.value == 'Z': # no way to win if 'Z' was already played return False elif card1.value == 'N': # any other than 'N' will beat us here, and the special case # where card2 also is 'N' was already handled above return True elif card2.value == 'Z': # we only lose if card1 is also 'Z', and that was handled above, # so this is an easy case also return True else: if card1.color == card2.color: if card1.value > card2.value: return False else: return True elif card2.color == trump_color: return True else: return False class Player: def __init__(self, name, player_id): self.name = name self.id = player_id self.cards = [] self.tricks_taken = 0 def set_cards(self, cards): self.cards = sorted(cards, key=attrgetter('color', 'value')) self.tricks_taken = 0 def play_card(self, card_idx): c = self.cards[card_idx] self.cards.remove(c) return c def take_trick(self): self.tricks_taken += 1 def add_card(self, card): self.cards.append(card) self.cards = sorted(self.cards, key=attrgetter('color', 'value')) class WizGame: def __init__(self, player_names: list): self.players = [Player(pn, idx) for idx, pn in enumerate(player_names)] if len(self.players) > 6: raise Exception # TODO: figure out more specific type self.card_deck = [] self.trump_card = None self.start_player = 0 self.active_player = 0 self.current_round = 1 self.num_tricks_played = 0 self.played_cards = list() self.players_ordered = self.players self.last_trick = None self.websockets = [] self.ws_tasks = [] def start_game(self): self.create_deck() self.current_round = 1 self.active_player = 0 self.start_player = 0 self.deal_cards(1) self.played_cards = list() self.players_ordered = self.players self.last_trick = [('-', '-')]*len(self.players) self.websockets = [] def create_deck(self): self.card_deck = [] for color in ["b", "r", "g", "y"]: for val in range(1, 14): self.card_deck.append(Card(color, val)) for idx in range(1, 5): self.card_deck.append(Card('-', 'Z', idx)) for idx in range(1, 5): self.card_deck.append(Card('-', 'N', idx)) def get_trump_color(self): card = self.trump_card if card: if card.color: return card.color elif card.value == 'Z': return 'choose' else: return None else: return None def deal_cards(self, cards_per_player): random.shuffle(self.card_deck) cs = list(chunks(self.card_deck, cards_per_player)) for idx, player in enumerate(self.players): player.set_cards(cs[idx]) if len(cs) > len(self.players): cc = cs[len(self.players)] c = cc[0] self.trump_card = c else: self.trump_card = None def get_trick_winner(self): cards = self.played_cards high_player, high_card = cards[0] for player, card in cards[1:]: if get_higher_card(high_card, card, self.trump_card.color if self.trump_card else '-'): high_card = card high_player = player return high_player, high_card def is_trick_finished(self): if len(self.played_cards) < len(self.players): return False else: return True def is_round_finished(self): h = self.is_trick_finished() if h and self.num_tricks_played == self.current_round: return True else: return False def next_round(self): max_rounds = len(self.card_deck)/len(self.players) if self.current_round <= max_rounds: self.current_round += 1 self.start_player += 1 if self.start_player >= len(self.players): self.start_player = 0 self.active_player = 0 self.players_ordered = self.players[self.start_player:] + self.players[:self.start_player] self.played_cards.clear() self.deal_cards(self.current_round) self.num_tricks_played = 0 return True else: return False def next_trick(self): self.last_trick = self.played_cards.copy() win = self.get_trick_winner() winner = win[0] starter = winner.id self.players_ordered = self.players[starter:] + self.players[:starter] self.active_player = 0 self.played_cards.clear() def play_card(self, player, card): p = self.players[player] c = p.play_card(card) t = (p, c) self.played_cards.append(t) self.active_player += 1 if self.is_trick_finished(): self.num_tricks_played += 1 return c # return card to calling function def set_trump_color(self, trump_color): if self.trump_card.value != 'Z': return False else: if trump_color not in ["b", "r", "g", "y"]: return False else: self.trump_card = Card(trump_color, 'Z') return True def get_prev_player(self): if self.start_player == 0: return len(self.players)-1 else: return self.start_player-1 async def send_page_reload(self): for ws in self.websockets: try: msg = {"type": "message", "fct": "reload"}; await ws.send_json(msg) except Exception as e: pass p = ["spam", "egg", "ham", "tomato"] the_game = WizGame(p)