from enum import Enum, auto from random import shuffle class Suit(Enum): CLUBS = auto() HEARTS = auto() DIAMONDS = auto() SPADES = auto() class Game: def __init__(self): self.foundations = {suit: 1 for suit in Suit} self.deck = self.get_random_pile() self.carpet = set(self.deck[-20:]) self.free_cells = 0 self.moves = 0 self.deck = self.deck[:-20] self.offers = [self.deck.pop()] def get_random_pile(self): cards = {(suit, value) for suit in Suit for value in range(2, 13+1)} deck = list(cards) shuffle(deck) return deck def can_drop(self, card): suit, value = card return self.foundations[suit] == value - 1 # FIXME: use this function. def can_move(self): if len(self.deck) > 0: return True if len(self.offers) > 0 and self.can_drop(self.offers[-1]): return True if any(self.can_drop(card) for card in self.carpet): return True if self.free_cells > 0: return True return False def count_move(self): self.moves += 1 # Possible moves def drop_from_carpet(self, card): if card not in self.carpet: raise ValueError() suit, value = card if self.foundations[suit] != value - 1: raise ValueError() self.count_move() self.carpet.remove(card) self.free_cells += 1 self.foundations[suit] = value; def autodrop(self): # This is independent for each suit. for suit in Suit: while (suit, self.foundations[suit]+1) in self.carpet: self.drop_from_carpet((suit, self.foundations[suit]+1)) def drop_card_from_offer(self): # to the foundations if len(self.offers) == 0: raise ValueError() offered_card = self.offers[-1] suit, value = offered_card if self.foundations[suit] != value - 1: raise ValueError() self.count_move() self.offers.pop() self.foundations[suit] = value if len(self.offers) == 0 and len(self.deck) > 0: self.count_move() self.offers.append(self.deck.pop()) def flip_deck(self): if len(self.deck) == 0: raise ValueError() self.count_move() self.offers.append(self.deck.pop()) def get_card_from_deck(self): if len(self.offers) == 0: raise ValueError() if self.free_cells < 1: raise ValueError() self.count_move() self.carpet.add(self.offers.pop()) self.free_cells -= 1 if len(self.offers) == 0 and len(self.deck) > 0: self.count_move() self.offers.append(self.deck.pop()) def game_result(self): # None for not finished, True for win, False for fail if all(self.foundations[suit] == 13 for suit in Suit): return True if self.can_move(): return None return False def print(self): from pprint import pprint # Print game state print(f'Moves: {self.moves}\nFree cells: {self.free_cells}\nCards in deck: {len(self.deck)}\nAvailable card: {self.offers[-1] if len(self.offers) > 0 else "None"}') print('Foundations:') pprint(self.foundations) print('Carpet:') pprint(self.carpet) if __name__ == '__main__': ## A simple testing game: from textwrap import dedent game = Game() game.print() while game.game_result() is None: q = input('pafdg? ') try: if q.startswith('p'): game.print() if q.startswith('a'): game.autodrop() if q.startswith('f'): game.flip_deck() if q.startswith('d'): game.drop_card_from_offer() if q.startswith('g'): game.get_card_from_deck() if q.startswith('?'): print(dedent("""\ p: print state a: autodrop carpet f: flip deck d: drop from offers g: get from offers ?: this help. """)) except ValueError as e: print(e) print('Try again!') print(f'Game ended in {game.moves}.') if game.game_result(): print('You won!') else: print('You lost:') game.print()