diff --git a/abilities.py b/abilities.py index d813c18..3ce59a4 100644 --- a/abilities.py +++ b/abilities.py @@ -1,4 +1,13 @@ +import logging import mtg +class TapMana(mtg.Ability): + def __init__(self, card, mana): + mtg.Ability.__init__(self, card, name='Tap Land', tap=True) + self.mana = mana + def run(self): + self.card.owner.mana += self.mana + logging.debug('%s: Added %s to mana pool of %s', self, self.mana, self.card.owner) + class Lifelink(mtg.Ability): pass diff --git a/cards.py b/cards.py index 59fab5f..c8e131a 100644 --- a/cards.py +++ b/cards.py @@ -1,4 +1,17 @@ from mtg import * +import abilities + +""" +Forest +Basic Land -- Forest +A-L, B-L, U-L, RV-L, 4E-L, 5E-L, 6E-L, 7E-L, 8ED-L, 9ED-L, 10E-L, M10-L, P1-L, P2-L, P3K-L, S99-L, S00-L, IA-L, MI-L, TE-L, US-L, MM-L, IN-L, OD-L, ONS-L, MRD-L, +CHK-L, RAV-L, TSP-L, LRW-L, SHM-L, ALA-L, ZEN-L, ROE-L, HOP-L +""" +class Forest(Card): + def __init__(self): + Card.__init__(self, 'Forest', 'basic land', ['forest']) + self.abilities.append(abilities.TapMana(self, 'G')) + """ Elvish Archdruid 1GG @@ -8,7 +21,6 @@ Other Elf creatures you control get +1/+1. {T}: Add {G} to your mana pool for each Elf you control. M10-R """ - class Elvish_Archdruid(Card): def __init__(self): - Card.__init__(self, 'Elvish Archdruid', ['elf', 'druid'], '1GG', 2, 2) + Card.__init__(self, 'Elvish Archdruid', 'creature', ['elf', 'druid'], '1GG', 2, 2) diff --git a/main.py b/main.py new file mode 100644 index 0000000..aa45d1e --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +import logging +from mtg import * +import cards + +logging.basicConfig(level=logging.DEBUG) + +game = Game() +test_deck = Deck() +test_deck.extend(cards.Forest() * 20) +test_deck.extend(cards.Elvish_Archdruid() * 40) +opponent = Player('Opponent', game, test_deck) +you = Player('You', game, test_deck) + +you.draw(7) +# Place a land +you.cast(you.hand[0]) +#Tap the land +you.battlefield[0].abilities[0].activate() \ No newline at end of file diff --git a/mtg.py b/mtg.py index 8c9a667..bda6ce6 100644 --- a/mtg.py +++ b/mtg.py @@ -1,5 +1,6 @@ import copy import random +import logging from observable import Observable class Game: @@ -99,39 +100,81 @@ class ManaCost: return self.mana.converted() + self.any class Player: - def __init__(self, name, deck=None): + 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.hand = CardList() - self.graveyard = CardList() - self.battlefield = CardList() + + self.lifeChanged = Observable() + self.defeated = Observable() + self.casts = Observable() + + logging.debug('Initialized %s', self) def __repr__(self): - return 'Player: {0} [Deck:{1}]'.format(self.name, len(self.deck)) + 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.copy(deck) - for card in deck: + 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 draw(self): - card = self.deck.pop() - self.hand.append(card) - card.moved.emit('deck', 'hand') + 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): - hand.remove(card) - self.battlefield.append(card) - card.moved.emit('hand', 'battlefield') + 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, attributes, cost, power, toughness, owner=None): - self.name = name + 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 = cost + 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() @@ -140,7 +183,7 @@ class Card: self.store() def __repr__(self): - return 'Card: [{2}] {0}: {1} [{3}/{4}]'.format(self.name, ' '.join([a.capitalize() for a in self.attributes]), self.cost, self.power, self.toughness) + 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): @@ -150,27 +193,58 @@ class Card: 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, target): - self.target = target + 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, game): + def __init__(self, owner, zone): list.__init__(self) - self.game = game + self.owner = owner + self.zone = zone def append(self, item): item.list = self list.append(self, item) class Deck(CardList): def __init__(self): - pass + CardList.__init__(self, None, 'deck') def shuffle(self): random.shuffle(self) def cards(self): diff --git a/observable.py b/observable.py index 3b32317..bb1aa36 100644 --- a/observable.py +++ b/observable.py @@ -3,6 +3,8 @@ class Observable(object): self.subscribers = [] def subscribe(self, subscriber): self.subscribers.append(subscriber) + def unsubscribe(self, subscriber): + self.subscribers.remove(subscriber) def emit(self, *args): for fn in self.subscribers: fn(*args)