From 803c0fb4d7a2d4edb00b0124187841da4f464919 Mon Sep 17 00:00:00 2001 From: MTW-PT <40525343+MTW-PT@users.noreply.github.com> Date: Mon, 6 Aug 2018 21:26:54 -0700 Subject: [PATCH] Add files via upload Last attempt at Pull Request Failed, fixed the critical error --- src/mtginator/cards.py | 123 ++++++++++++++++++++++++++++++++++++++--- src/mtginator/decks.py | 6 +- src/mtginator/game.py | 67 +++++++++++++++------- src/mtginator/sim.py | 34 +++++++----- 4 files changed, 188 insertions(+), 42 deletions(-) diff --git a/src/mtginator/cards.py b/src/mtginator/cards.py index 84b4162..aef8eb2 100644 --- a/src/mtginator/cards.py +++ b/src/mtginator/cards.py @@ -68,7 +68,8 @@ class Cost(object): """ - def __init__(self, fromString="", B=0, G=0, R=0, U=0, W=0, c=0, X=False): + def __init__(self, fromString="", B=0, G=0, R=0, U=0, W=0, C=0, gen=0, X=False): + # C = colorless gen = generic self.mana = {} if fromString: bracks = re.compile(r'[\{\}]') @@ -88,11 +89,11 @@ def __init__(self, fromString="", B=0, G=0, R=0, U=0, W=0, c=0, X=False): else: print("Unknown mana symbol: %s" % (symbol)) raise - else: # warning does not handle hybrid/phyrexian use fromString!!! # does it handle wingding? - self.mana['generic'] = int(c) + self.mana['generic'] = int(gen) + self.mana['C'] = int(C) self.mana['B'] = int(B) self.mana['G'] = int(G) self.mana['R'] = int(R) @@ -100,6 +101,105 @@ def __init__(self, fromString="", B=0, G=0, R=0, U=0, W=0, c=0, X=False): self.mana['W'] = int(W) self.mana['X'] = X + for value in land_mana.values(): + if value not in self.mana.keys(): + self.mana[value] = 0 + + def add_costs(self,additional_costs): + for cost in additional_costs: + self.mana['generic'] += cost.mana['generic'] + self.mana['C'] += cost.mana['C'] + self.mana['B'] += cost.mana['B'] + self.mana['G'] += cost.mana['G'] + self.mana['R'] += cost.mana['R'] + self.mana['U'] += cost.mana['U'] + self.mana['W'] += cost.mana['W'] + self.mana['X'] = self.mana['X'] or cost.mana['X'] + return self + +class Context(object): + def __init__(self, battlefield, mana_pool): + self.available_mana = {} + self.battlefield = battlefield + #mana_pool is a list of characters that represent mana already in the mana pool + #assume for now that player is only playing basic lands and no other mana sources + if len(mana_pool) == 0: + for permanent in battlefield: + if permanent.is_land() and not permanent.tapped: + if land_mana[permanent.name] in self.available_mana: + self.available_mana[land_mana[permanent.name]] += 1 + else: + self.available_mana[land_mana[permanent.name]] = 1 + else: + print "floating mana is currently not implemented" + # need to initialize all basic colors + for value in land_mana.values(): + if value not in self.available_mana.keys(): + self.available_mana[value] = 0 + + def can_pay(self, cost): + if 'generic' in cost.mana.keys(): + total_mana_needed = cost.mana['generic'] + else: + total_mana_needed = 0 + if 'B' in cost.mana.keys(): + if cost.mana['B'] > self.available_mana['B']: + return False + else: + total_mana_needed += cost.mana['B'] + if 'U' in cost.mana.keys(): + if cost.mana['U'] > self.available_mana['U']: + return False + else: + total_mana_needed += cost.mana['U'] + if 'C' in cost.mana.keys(): + if cost.mana['C'] > self.available_mana['C']: + return False + else: + total_mana_needed += cost.mana['C'] + if 'G' in cost.mana.keys(): + if cost.mana['G'] > self.available_mana['G']: + return False + else: + total_mana_needed += cost.mana['G'] + if 'R' in cost.mana.keys(): + if cost.mana['R'] > self.available_mana['R']: + return False + else: + total_mana_needed += cost.mana['R'] + if 'W' in cost.mana.keys(): + if cost.mana['W'] > self.available_mana['W']: + return False + else: + total_mana_needed += cost.mana['W'] + if (self.available_mana['B'] + self.available_mana['U'] + self.available_mana['C'] + self.available_mana['G'] + self.available_mana['R'] + self.available_mana['W']) < total_mana_needed: + return False + else: + return True + + def pay(self, cost): + track_mana = {} + #first pass get tap all the colored mana + for permanent in self.battlefield: + if permanent.is_land() and not permanent.tapped: + if land_mana[permanent.name] in cost.mana: + if land_mana[permanent.name] in track_mana: + if track_mana[land_mana[permanent.name]] < cost.mana[land_mana[permanent.name]]: + track_mana[land_mana[permanent.name]] += 1 + permanent.tapped = True + else: + track_mana[land_mana[permanent.name]] = 1 + permanent.tapped = True + if 'generic' not in cost.mana: + return True + #second pass tap random mana equal to generic mana + random_taps = 0 + for permanent in self.battlefield: + if permanent.is_land() and not permanent.tapped: + permanent.tapped = True + random_taps += 1 + if random_taps == cost.mana['generic']: + return True class Card(object): @@ -230,11 +330,18 @@ def pay_cost(self, context): def play(self, context): - self.pay_cost(context) # need some sort of game context object - self.zone = 'battlefield' - self.tapped = False - if self.cipt: - self.tapped = True + if context is None: + self.zone = 'battlefield' + self.tapped = False + if self.cipt: + self.tapped = True + else: + self.pay_cost(context) # need some sort of game context object + self.zone = 'battlefield' + self.tapped = False + if self.cipt: + self.tapped = True + def __repr__(self): return "[ %s (%s) ]" % (self.name, self.card_data.get('manaCost', '0')) diff --git a/src/mtginator/decks.py b/src/mtginator/decks.py index e977115..a8e09b2 100644 --- a/src/mtginator/decks.py +++ b/src/mtginator/decks.py @@ -1,6 +1,8 @@ +import sys +sys.path.insert(0,'/src/mtginator/') import random import json -import mtginator.cards as cards +import cards import re @@ -64,7 +66,7 @@ def load_deck(self, input_file='', file_type="txt"): class CardDB(object): ''' this reads all MTG cards from JSON. It should be memoized ''' - def __init__(self, input_file='data/mtgjson/AllSets-x.json'): + def __init__(self, input_file='./data/AllSets-x.json'): self.all = json.load(open(input_file)) hashed = {} diff --git a/src/mtginator/game.py b/src/mtginator/game.py index 5f789d9..c27ee8e 100644 --- a/src/mtginator/game.py +++ b/src/mtginator/game.py @@ -1,5 +1,7 @@ +import sys +sys.path.insert(0,'/src/mtginator/') import random - +import cards class Game(object): ''' Object that represents the game/board state of a given game ''' @@ -24,7 +26,7 @@ def is_over(self, turn=None, draw=(None, None)): if turn and turn > self.maxturns: return self.players for player in self.players: - if (player.life <= 0) or (player.poison <= 0) or (len(player.deck) == 0 and player in draw): + if (player.life <= 0) or (player.poison <= 0) or (len(player.deck.main) == 0 and player in draw): losers.update(player) return list(losers) @@ -38,7 +40,19 @@ def __str__(self): class Mana(object): ''' Object that represents mana-producing board state of a player ''' def __init__(self, board, extras=[]): - for permanent in board: + self.available_mana = [] + #extras is a list of characters that represent mana already in the mana pool + #assume for now that player is only playing basic lands and no other mana sources + if len(extras) == 0: + self.available_mana[:] = [] + for permanent in board: + if permanent.is_land() and not permanent.tapped: + self.available_mana.append(cards.land_mana[permanent.name]) + print self.available_mana + + + + class Player(object): ''' Object that represents a player of the game''' @@ -61,7 +75,7 @@ def _satisfied(self, rules, n): if len(self.hand) and len(self.hand) < 5: # keep all 4s return True - elif len([c for c in self.hand if c.isLand()]) and len([c for c in self.hand if not c.isLand()]): + elif len([c for c in self.hand if c.is_land()]) and len([c for c in self.hand if not c.is_land()]): # literally "lands and spells" return True else: @@ -92,11 +106,14 @@ def main(self): self.make_play(plays) def land_drop(self): - lands = [c for c in self.hand if c.is_land] - pick = random.choice(lands) - # Note: should implment some color optimization - pick.play() - print("Played {} as Land for turn [ hand size: {} ]".format(pick, len(self.hand))) + lands = [c for c in self.hand if c.is_land()] + if len(lands) > 0: + pick = random.choice(lands) + # Note: should implment some color optimization + pick.play(None) #lands don't need context to play + self.battlefield.append(pick) + self.hand.remove(pick) + print("Played {} as Land for turn [ hand size: {} ]".format(pick, len(self.hand))) def cleanup(self): while(len(self.hand) > self.max_handsize): @@ -104,16 +121,24 @@ def cleanup(self): def choose_discard(self): discard = self.hand.pop(random.randrange(len(self.hand))) + self.graveyard.append(discard) + print("Dicarding {}".format(discard)) def make_play(self, possible_plays): - card_to_play = random.choice(possible_plays) - card_to_play.play() - print("Playing {}".format(card_to_play)) + if possible_plays: + card_to_play = random.choice(possible_plays) + card_to_play.play(cards.Context(self.battlefield, [])) + self.hand.remove(card_to_play) + if card_to_play.is_permanent(): + self.battlefield.append(card_to_play) + else: + self.graveyard.append(card_to_play) + print("Playing {}".format(card_to_play)) def available_mana(self): + return Mana(self.battlefield) ''' return mana object based on board state ''' - pass def reset(self): self.deck.main = self.deck.main + self.hand + self.graveyard + self.exile + self.battlefield @@ -134,7 +159,7 @@ def mulligan(self, rules=None, n=7, verbose=True): else: print("Mulling to {}...".format(n)) for i in range(0, n): - self.hand.append(self.deck.drawCard()) + self.hand.append(self.deck.draw_card()) while(not self._satisfied(rules, n)): self.mulligan(rules, n-1, verbose=verbose) @@ -144,16 +169,20 @@ def mulligan(self, rules=None, n=7, verbose=True): def enumerate_plays(self): ''' For a given hand (or metahand) and available mana, what plays are available to Player - Returns: Set of Card objects + Returns: List of Card objects ''' + list_of_plays = [] if not self.hand: - return set() + return list_of_plays + + context = cards.Context(self.battlefield,[]) for card in self.hand: - pass - # something like if card.can_play... add to list + if not card.is_land(): + if context.can_pay(card.mana_cost): + list_of_plays.append(card) - return {self.hand} + return list_of_plays def __repr__(self): return("Name: {} Deck {} Life {} Poison {} #Cards-in-Hand {} WP% {}".format(self.name, diff --git a/src/mtginator/sim.py b/src/mtginator/sim.py index 859dc6b..41dd560 100755 --- a/src/mtginator/sim.py +++ b/src/mtginator/sim.py @@ -1,11 +1,13 @@ #!/usr/bin/env python import sys +sys.path.insert(0,'/src/mtginator/') import os -import mtginator.decks as decks -import mtginator.game as game +import decks +import game -DECKS_DIR = 'data/decks/' + +DECKS_DIR = './data/decks/' def run(turns, rounds, decks, goldfish=True): @@ -32,16 +34,22 @@ def run(turns, rounds, decks, goldfish=True): print("After mulligans player %s has %s cards." % (player.name, len(player.hand))) print("%s" % ([str(c) for c in player.hand])) game_state = game.Game(ourplayers, maxturns=turns) - current_turn = 0 - (first_player, second_player) = game_state.play_or_draw() - print("{} goes first".format(first_player.name)) - print("{} goes second".format(second_player.name)) - - while(not game_state.is_over(turn=current_turn)): - current_turn += 1 - print(game_state) - first_player.take_turn() - second_player.take_turn() + game_state.turn = 0 + if not goldfish: + (first_player, second_player) = game_state.play_or_draw() + print("{} goes first".format(first_player.name)) + print("{} goes second".format(second_player.name)) + while(not game_state.is_over(turn=game_state.turn)): + game_state.turn += 1 + print(game_state) + first_player.take_turn() + second_player.take_turn() + else: #single player only one player is taking turns + while(not game_state.is_over(turn=game_state.turn)): + game_state.turn += 1 + print(game_state) + ourplayers[0].take_turn() + def main():