Permalink
Browse files

Implement Mulligan

Careful! This means a Mulligan phase is now forced on both players.
The players will *not* be able to end the turn, unless their mulligan
has been completed.

To "skip" a mulligan, one can simply do an empty mulligan with
`player.choice.choose()`. To remove the Mulligan phase altogether,
use a BaseGame instead of a Game object.
  • Loading branch information...
jleclanche committed Aug 25, 2015
1 parent e4a3b43 commit fe3edd0926f8d9245a190e5d113283e2270460ce
Showing with 63 additions and 5 deletions.
  1. +26 −1 fireplace/actions.py
  2. +2 −0 fireplace/card.py
  3. +14 −0 fireplace/enums.py
  4. +7 −2 fireplace/game.py
  5. +1 −0 fireplace/player.py
  6. +6 −0 tests/full_game.py
  7. +7 −2 tests/utils.py
@@ -1,7 +1,7 @@
import logging
from enum import IntEnum
from .dsl import LazyNum, Picker, Selector
from .enums import CardType, PowSubType, Zone
from .enums import CardType, Mulligan, PowSubType, Zone
from .entity import Entity


@@ -181,10 +181,35 @@ class Args(Action.Args):
type = None

def do(self, source, player):
assert not player.choice, "Attempted to end a turn with a choice open"
self.broadcast(source, EventListener.ON, player)
source.game._end_turn()


class MulliganChoice(GameAction):
class Args(Action.Args):
PLAYER = 0

type = None

def do(self, source, player):
player.mulligan_state = Mulligan.INPUT
player.choice = self
# NOTE: Ideally, we give The Coin when the Mulligan is over.
# Unfortunately, that's not compatible with Blizzard's way.
self.cards = player.hand.exclude(id="GAME_005")
self.player = player

def choose(self, *cards):
self.player.draw(len(cards))
for card in cards:
assert card in self.cards
card.zone = Zone.DECK
self.player.choice = None
self.player.shuffle_deck()
self.player.mulligan_state = Mulligan.DONE


class Play(GameAction):
"""
Make the source player play \a card, on \a target or None.
@@ -273,6 +273,8 @@ def hit(self, target, amount):
return self.game.queue_actions(self, [Damage(target, amount)])

def is_playable(self):
if self.controller.choice:
return False
if not self.controller.current_player:
return False
if self.controller.mana < self.cost:
@@ -338,6 +338,20 @@ def test(self, entity, *args):
##
# Game enums

class ChoiceType(IntEnum):
INVALID = 0
MULLIGAN = 1
GENERAL = 2


class Mulligan(IntEnum):
INVALID = 0
INPUT = 1
DEALING = 2
WAITING = 3
DONE = 4


class OptionType(IntEnum):
PASS = 1
END_TURN = 2
@@ -239,6 +239,7 @@ def prepare(self):
def start(self):
logging.info("Starting game %r", self)
self.state = State.RUNNING
self.step = Step.MAIN_BEGIN
self.zone = Zone.PLAY
self.prepare()
self.manager.start_game()
@@ -318,15 +319,19 @@ class MulliganRules:
Performs a Mulligan phase when the Game starts.
Currently just a dummy phase.
"""

def start(self):
from .actions import MulliganChoice

self.next_step = Step.BEGIN_MULLIGAN
super().start()
self.begin_mulligan()

def begin_mulligan(self):
logging.info("Entering mulligan phase")
self.step, self.next_step = self.next_step, Step.MAIN_READY

for player in self.players:
self.queue_actions(self, MulliganChoice(player))


class Game(MulliganRules, CoinRules, BaseGame):
pass
@@ -31,6 +31,7 @@ def __init__(self, name):
self.graveyard = CardList()
self.secrets = CardList()
self.buffs = []
self.choice = None
self.start_hand_size = 4
self.max_hand_size = 10
self.max_resources = 10
@@ -22,6 +22,12 @@ def main():
game = Game(players=(player1, player2))
game.start()

for player in game.players:
print("Can mulligan %r" % (player.choice.cards))
mull_count = random.randint(0, len(player.choice.cards))
cards_to_mulligan = random.sample(player.choice.cards, mull_count)
player.choice.choose(*cards_to_mulligan)

while True:
heropower = game.current_player.hero.power
# always play the hero power, just for kicks
@@ -4,7 +4,7 @@
import fireplace.cards
from fireplace.cards.heroes import *
from fireplace.enums import *
from fireplace.game import Game
from fireplace.game import BaseGame, CoinRules, Game
from fireplace.player import Player
from fireplace.utils import random_draft

@@ -53,7 +53,7 @@ def _draft(hero, exclude):
_heroes = fireplace.cards.filter(collectible=True, type=CardType.HERO)


class BaseTestGame(Game):
class BaseTestGame(CoinRules, BaseGame):
def start(self):
super().start()
self.player1.max_mana = 10
@@ -75,6 +75,11 @@ def prepare_game(hero1=None, hero2=None, exclude=(), game_class=BaseTestGame):
game = game_class(players=(player1, player2))
game.start()

# Do empty mulligans
for player in game.players:
if player.choice:
player.choice.choose()

return game


0 comments on commit fe3edd0

Please sign in to comment.