1
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.6 KiB
Python

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()