import copy import random import logging from observable import Observable class Game: def __init__(self): self.players = [] class Mana: types = { 'W': 'White', 'U': 'Blue', 'B': 'Black', 'R': 'Red', 'G': 'Green', } def __init__(self, mana=None): self.mana = {} for type in Mana.types.keys(): self.mana[type] = 0 if mana: # Can't just set self to result... ? result = self + mana self.mana = result.mana def __add__(self, other): result = Mana() if isinstance(other, str): for m in [c for c in other.upper() if c in Mana.types.keys()]: result.mana[m] = result.mana[m] + 1 elif isinstance(other,Mana): for (key, value) in other.mana.iteritems(): result.mana[key] = self.mana[key] + value else: # TODO: raise exception raise Exception() pass return result def __sub__(self, other): result = Mana() if isinstance(other, Mana): for (key, value) in other.mana.iteritems(): result.mana[key] = self.mana[key] - value if result.mana[key] < 0: raise Exception('Insufficient {0} Mana'.format(Mana.types[key])) elif isinstance(other,ManaCost): for (key, value) in other.mana.mana.iteritems(): result.mana[key] = self.mana[key] - value if result.mana[key] < 0: raise Exception('Insufficient {0} Mana'.format(Mana.types[key])) remaining = other.any for (key, value) in result.mana.iteritems(): while result.mana[key] > 0 and remaining > 0: result.mana[key] = result.mana[key] - 1 remaining = remaining - 1 if remaining > 0: raise Exception('Insufficient Mana') return result def __repr__(self): return ''.join([type * count for (type, count) in self.mana.iteritems()]) if self.converted() > 0 else '0' def converted(self): return sum(self.mana.values()) class ManaCost: def __init__(self, cost=None): self.any = 0 self.mana = Mana() if cost: result = self + cost self.any = result.any self.mana = result.mana def __add__(self, other): result = ManaCost() if isinstance(other, str): result.mana = Mana(other) value = '' for c in other: if c not in '0123456789': break value = value + c value = int(value) if len(value) > 0 else 0 result.any = value elif isinstance(other, Mana): result.mana = other elif isinstance(other, ManaCost): result.any = self.any + other.any result.mana = self.mana + other.mana return result def __sub__(self, other): result = ManaCost() if isinstance(other, ManaCost): result.any = self.any - other.any result.mana = self.mana - other.mana return result def __repr__(self): return '{0}{1}'.format( self.any if self.any > 0 or self.mana.converted() == 0 else '', self.mana if self.mana.converted() > 0 else '' ) def converted(self): return self.mana.converted() + self.any class Player: def __init__(self, name, game, deck=None): self.name = name self.game = game self.life = 20 self.mana = Mana() self.deck = None self.library = CardList(self, 'library') self.hand = CardList(self, 'hand') self.graveyard = CardList(self, 'graveyard') self.battlefield = CardList(self, 'battlefield') self.setDeck(deck) self.lifeChanged = Observable() self.defeated = Observable() self.casts = Observable() logging.debug('Initialized %s', self) def __repr__(self): return 'Player: {0} [Life:{1},Mana:{2},Hand:{3},Library:{4}]'.format(self.name, self.life, self.mana, len(self.hand), len(self.library) if self.library else self.library) def setDeck(self, deck): if not deck: return self.deck = copy.deepcopy(deck) self.deck.owner = self self.library = copy.copy(self.deck) self.library.zone = 'library' for card in self.deck: # Re-initialize so the references are correct card.__init__() card.owner = self def setLife(self, life): self.life = life self.lifeChanged.emit(self.life) def affectLife(self, amount): self.life = self.life + amount if amount: self.lifeChanged.emit(self.life) def payMana(self, cost): try: self.mana -= cost except: logging.debug('%s could not pay mana cost %s', self, cost) return False logging.debug('%s paid mana cost %s', self, cost) return True def draw(self, count=1): for i in xrange(count): card = self.library[0] card.move(self.library, self.hand) logging.debug('%s drew %s', self, card) def cast(self, card): logging.debug('%s attempts to cast %s', self, card) if not self.payMana(card.cost): logging.debug('%s failed to cast %s: Not enough mana', self, card) return False card.move(self.hand, self.battlefield) logging.debug('%s successfully casts %s', self, card) self.casts.emit(card) return True class Card: def __init__(self, name, type, attributes, cost=0, power=0, toughness=0, owner=None): self.name = name self.type = type.lower() self.attributes = [a.lower() for a in attributes] self.cost = ManaCost(cost) self.power = power self.toughness = toughness self.is_tapped = False self.abilities = [] self.owner = owner self.zone = None # Events self.moved = Observable() self.tapped = Observable() self.attacked = Observable() self.store() def __repr__(self): return 'Card: [{3}] {0} -- {1}: {2} [{4}/{5}]{6}'.format(self.name, self.type.title(), ' '.join([a.capitalize() for a in self.attributes]), self.cost, self.power, self.toughness, ' [T]' if self.is_tapped else '') def __mul__(self, other): result = [] for i in xrange(other): result.append(copy.copy(self)) return result def store(self): self.__stored = copy.copy(self) def restore(self): self = copy.copy(self.__stored) def move(self, origin, destination): origin.remove(self) destination.append(self) logging.debug('%s moved from %s to %s', self, origin.zone, destination.zone) self.moved.emit(origin.zone, destination.zone) def tap(self): if self.is_tapped: return False self.is_tapped = True logging.debug('%s is tapped', self) self.tapped.emit() return True class Ability: def __init__(self, card, name='Unknown', cost=0, tap=False): self.card = card self.name = name self.cost = ManaCost(cost) self.tap = tap self.init() def __repr__(self): return 'Ability: {0} [{1}{2}]'.format(self.name, self.cost, ', T' if self.tap else '') def init(self): pass def run(self): pass def activate(self): logging.debug('%s attempting to activate %s on %s', self.card.owner, self, self.card) if self.tap and self.card.is_tapped: logging.debug('%s failed to activate %s on %s', self.card.owner, self, self.card) return False if self.card.owner.payMana(self.cost): if self.tap: self.card.tap() logging.debug('%s succeeded activating %s on %s', self.card.owner, self, self.card) self.run() return True logging.debug('%s failed to activate %s on %s', self.card.owner, self, self.card) return False class CardList(list): def __init__(self, owner, zone): list.__init__(self) self.owner = owner self.zone = zone def append(self, item): item.list = self list.append(self, item) class Deck(CardList): def __init__(self): CardList.__init__(self, None, 'deck') def shuffle(self): random.shuffle(self) def cards(self): return self.__cards