Add files via upload

Refactored card typing to no longer be a piece of data but a feature of the object structure.
This commit is contained in:
Brad Stein 2018-01-10 14:01:44 -05:00 committed by GitHub
parent 2b4b1f605c
commit 94d2e58214
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 348 additions and 66 deletions

View File

@ -0,0 +1,5 @@
from card.basic.card_kingdom import Kingdom
class Action(Kingdom):
pass

View File

@ -0,0 +1,5 @@
from card.basic.card_kingdom import Kingdom
class Attack(Kingdom):
pass

10
card/basic/card_curse.py Normal file
View File

@ -0,0 +1,10 @@
from card.card import Card
class Curse(Card):
@staticmethod
def pile_setup(player_count):
if player_count % Card.normal_full_table < Card.normal_full_table/2:
return Card.pile_player_rate
else:
return (player_count - 1) * Card.pile_player_rate

View File

@ -0,0 +1,10 @@
from card.card import Card
from math import floor
class Kingdom(Card):
pile_player_rate = 10
@staticmethod
def pile_setup(player_count):
return (floor(player_count/Card.normal_full_table) + 1) * Card.pile_player_rate

View File

@ -0,0 +1,5 @@
from card.basic.card_kingdom import Kingdom
class Reaction(Kingdom):
pass

View File

@ -0,0 +1,8 @@
from card.card import Card
from math import floor
class Treasure(Card):
@classmethod
def pile_setup(cls, player_count):
return (floor(player_count/Card.normal_full_table) + 1) * cls.pile_player_rate

View File

@ -0,0 +1,21 @@
from card.card import Card
from math import floor
class Victory(Card):
two_player_count = 8
four_player_count = 12
five_player_count = 15
six_player_count = 18
@classmethod
def pile_setup(cls, player_count):
if 0 < player_count % Card.normal_full_table < Card.normal_full_table/2:
supplement = cls.two_player_count
elif Card.normal_full_table/2 <= player_count % Card.normal_full_table < Card.normal_full_table - 1:
supplement = cls.four_player_count
elif player_count % Card.normal_full_table == Card.normal_full_table - 1:
supplement = cls.five_player_count
else:
supplement = cls.six_player_count
return (floor(player_count/Card.normal_full_table) * cls.six_player_count) + supplement

View File

@ -1,22 +1,12 @@
from enum import Enum, auto
class Card: class Card:
class CardType(Enum):
Treasure = auto()
Action = auto()
Reaction = auto()
Attack = auto()
Victory = auto()
Curse = auto()
prevent_attack = False prevent_attack = False
normal_full_table = 6
pile_player_rate = 10
def __init__(self, name, cost, cardtype, value, coin, action, buy, draw, owner): def __init__(self, name, cost, value, coin, action, buy, draw, owner):
self.__name = name self.__name = name
self.__cost = cost self.__cost = cost
self.__coin = coin self.__coin = coin
self.__type = cardtype
self.__action = action self.__action = action
self.__buy = buy self.__buy = buy
self.__draw = draw self.__draw = draw
@ -34,16 +24,19 @@ class Card:
# This is here so that 'special' card can override this function so that unique card effects can happen. # This is here so that 'special' card can override this function so that unique card effects can happen.
pass pass
def setup(self): @classmethod
def pile_setup(cls, player_count):
# This is here so that each card can override this function so that the right number of .
pass
@staticmethod
def setup():
# This is here so that 'special' card can override this function so that unique card setup effects can happen. # This is here so that 'special' card can override this function so that unique card setup effects can happen.
pass pass
def get_name(self): def get_name(self):
return self.__name return self.__name
def get_type(self):
return self.__type
def get_points(self): def get_points(self):
return self.__value return self.__value
@ -60,7 +53,15 @@ class Card:
return self.__owner return self.__owner
def identify(self): def identify(self):
return self.__name + ", " + str(self.__type) + ", " + str(self.__cost) return self.__name + ", " + self.__str__() + ", costing " + str(self.__cost)
def print_card_list(self, card, message):
print("\nPlayer " + str(self._Card__owner.get_player_index()) + " " + message)
counter = 0
for c in card:
print(str(counter) + ": " + c.identify())
counter += 1
def __get_index_not_self(self): def __get_index_not_self(self):
result = -1 result = -1
@ -69,13 +70,5 @@ class Card:
result = self._Card__owner.get_hand().get_player_index() result = self._Card__owner.get_hand().get_player_index()
return result return result
def __print_card_list(self, card, message):
print("\nPlayer " + str(self._Card__owner.get_player_index()) + " " + message)
counter = 0
for c in card:
print(str(counter) + ": " + c.identify())
counter += 1
def __str__(self): def __str__(self):
return "A " + self.__name + " card." return "a " + self.__name + " card"

27
card/named/cellar.py Normal file
View File

@ -0,0 +1,27 @@
from card.basic.card_action import Action
class Cellar(Action):
def effect(self):
hand_index = 0
cards_discarded = 0
have_not_run_yet = True
while self._Card__owner.get_hand().get_remaining() >= 0 and \
0 <= hand_index < self._Card__owner.get_hand().get_remaining() and \
(hand_index != self._Card__owner.get_hand().get_supply().index(self) or have_not_run_yet):
hand_index = self.__get_index("Player " + str(self._Card__owner.get_player_index()) + ", input the index "
"from your hand to discard that card and gain an action, or input an "
"impossible index to end discard selection: ")
if 0 <= hand_index < self._Card__owner.get_hand().get_remaining() and \
hand_index != self._Card__owner.get_hand().get_supply().index(self):
self._Card__owner.discard_from_hand(hand_index)
self._Card__owner.print_hand()
cards_discarded += 1
hand_index = self.__get_index_not_self()
have_not_run_yet = False
self._Card__owner.draw_cards(cards_discarded)
def __get_index(self, message):
return self.__Card_owner.get_general_input(message, int)

5
card/named/copper.py Normal file
View File

@ -0,0 +1,5 @@
from card.basic.card_treasure import Treasure
class Copper(Treasure):
pile_player_rate = 60

8
card/named/estate.py Normal file
View File

@ -0,0 +1,8 @@
from card.basic.card_victory import Victory
class Estate(Victory):
two_player_count = 14
four_player_count = 18
five_player_count = 21
six_player_count = 24

5
card/named/gold.py Normal file
View File

@ -0,0 +1,5 @@
from card.basic.card_treasure import Treasure
class Gold(Treasure):
pile_player_rate = 30

17
card/named/merchant.py Normal file
View File

@ -0,0 +1,17 @@
from card.basic.card_action import Action
class Merchant(Action):
def effect(self):
silver_card_index = self._Card__owner.get_hand().get_index_of_card_by_name("Silver")
if silver_card_index >= 0:
yes_no = self.__get_Merchant_input("'Y' if you'd like to play a silver card and gain an extra coin: ")
if yes_no:
self._Card__owner.get_hand().transfer_card_by_card(
self._Card__owner.get_hand().get_card(silver_card_index), self._Card__owner.get_discard())
self._Card__owner.add_purchase_power(3)
def __get_Merchant_input(self, message):
return self.__Card_owner.get_general_input("Player " + str(self._Card__owner.get_player_index()) + ", " +
message, str)

32
card/named/militia.py Normal file
View File

@ -0,0 +1,32 @@
from card.basic.card_attack import Attack
from card.basic.card_action import Action
from random import randint
class Militia(Action, Attack):
def effect(self):
for player in self._Card__owner.get_table().get_players():
if self._Card__owner != player and not player.get_hand().blocks_attack(self.get_name()):
player.print_hand()
print("Player " + str(player.get_player_index()) + ", you MUST discard down to 3 card.")
self.__force_discard(self._Card__owner.get_std_chances(), player)
def __force_discard(self, chances, player):
if player.get_hand().get_remaining() > 3 and chances > 0:
hand_index = player.militia_input("\nPlease provide an index to identify a card from hand you would like to"
" discard (0 to " + str(player.get_hand().get_remaining() - 1) + "): "
, int)
self.__check_discard(hand_index, player, chances)
elif self._Card__owner.get_hand().get_remaining() > 3 and chances <= 0:
print("You're out of chances to select a valid card to discard, randomly selecting for you.")
player.discard_from_hand(randint(0, self.__hand.get_remaining() - 1))
def __check_discard(self, index, player, chances):
if 0 > index or index >= self._Card__owner.get_hand().get_remaining():
print("Valid inputs range from 0 to " + str(player.get_hand().get_remaining() - 1) + ". 1 chance lost.")
self.__force_discard(chances - 1, player)
else:
print("Discarding " + player.get_hand().get_card(index).get_name() + ".")
player.discard_from_hand(index)
player.print_hand()
self.__force_discard(self._Card__owner.get_std_chances(), player)

9
card/named/mine.py Normal file
View File

@ -0,0 +1,9 @@
from card.basic.card_action import Action
from card.basic.card_treasure import Treasure
from card.special.card_gain_trash import CardGainTrash
class Mine(Action, CardGainTrash):
coin_gain = 3
trashable_type_restriction = Treasure
gainable_type_restriction = Treasure

6
card/named/moat.py Normal file
View File

@ -0,0 +1,6 @@
from card.basic.card_action import Action
from card.basic.card_reaction import Reaction
class Moat(Action, Reaction):
prevent_attack = True

6
card/named/remodel.py Normal file
View File

@ -0,0 +1,6 @@
from card.basic.card_action import Action
from card.special.card_gain_trash import CardGainTrash
class Remodel(Action, CardGainTrash):
coin_gain = 2

6
card/named/silver.py Normal file
View File

@ -0,0 +1,6 @@
from card.basic.card_treasure import Treasure
class Silver(Treasure):
pile_player_rate = 40

9
card/named/workshop.py Normal file
View File

@ -0,0 +1,9 @@
from card.basic.card_action import Action
from card.special.card_gain import CardGain
class Workshop(Action, CardGain):
coin_gain = 4
def effect(self):
self.gain_card(self.coin_gain)

41
card/special/card_gain.py Normal file
View File

@ -0,0 +1,41 @@
from card.card import Card
class CardGain(Card):
gainable_type_restriction = None
def gain_card(self, spending_limit):
gainable_cards = self.__get_gainable_cards(spending_limit)
self.print_card_list(gainable_cards, "Gainable Cards: ")
index = 0
chances = self.get_owner().get_std_chances()
while len(gainable_cards) > 0 and 0 <= index < len(gainable_cards) - 1 and chances > 0:
index = self.__get_gain_card()
if 0 > index >= len(gainable_cards):
print("Acceptable inputs range from 0 to " + str(len(gainable_cards) - 1) + ". 1 chance lost.")
index = 0
chances -= 1
else:
pile_index = self.get_owner().get_table().get_pile_index_of_card(gainable_cards[index].get_name())
print("Player " + str(self.get_owner().get_player_index()) + " drawing "
+ self.get_owner().get_table().get_pile(pile_index).get_card_group().get_name() + " to hand.")
self.get_owner().get_table().get_pile(pile_index).transfer_top_card(self.get_owner().get_hand())
self.get_owner().claim_top_card(self.get_owner().get_hand())
chances = 0
def __get_gain_card(self):
return self.get_owner().get_general_input("\nPlease identify the index of which card you would like to "
"obtain: ", int)
def __get_gainable_cards(self, spending_limit):
result = list()
for p in self.get_owner().get_table().get_piles():
if p.get_card_group().get_cost() <= spending_limit:
if self.gainable_type_restriction is None:
result.append(p.get_card_group())
elif isinstance(p.get_card_group(), self.gainable_type_restriction):
result.append(p.get_card_group())
return result

View File

@ -0,0 +1,9 @@
from card.special.card_trash import CardTrash
from card.special.card_gain import CardGain
class CardGainTrash(CardTrash, CardGain):
coin_gain = 0
def effect(self):
self.gain_card(self.trash_card_get_cost() + self.coin_gain)

View File

@ -0,0 +1,43 @@
from card.card import Card
class CardTrash(Card):
trashable_type_restriction = None
def trash_card_get_cost(self):
tc = self.__get_trashable_cards()
self.print_card_list(tc, " Trashable Cards: ")
index = 0
bonus = 0
chances = self.get_owner().get_std_chances()
while 0 < len(tc) and 0 <= index < len(tc) - 1 and chances > 0:
index = self.__get_card_to_trash()
if index < 0 or index >= len(tc):
print("Acceptable inputs range from 0 to " + str(len(tc) - 1) + ". 1 chance lost.")
index = 0
chances -= 1
else:
print("Player " + str(self.get_owner().get_player_index()) + " trashing " + tc[index].get_name() + ".")
bonus = tc[index].get_cost()
self.get_owner().get_hand().transfer_card_by_card(tc[index], self.get_owner().get_table().get_trash())
chances = 0
return bonus
def trash_card(self):
self.trash_card_get_cost()
def __get_card_to_trash(self):
return self.get_owner().get_general_input("\nPlease identify the index of the desired card to trash: ", int)
def __get_trashable_cards(self):
result = list()
for c in self.get_owner().get_hand().get_supply():
if c != self:
if self.trashable_type_restriction is None:
result.append(c)
elif isinstance(c, self.trashable_type_restriction):
result.append(c)
return result

View File

@ -1,6 +1,6 @@
from player.player import Player from player.player import Player
from card.card import Card from card.basic.card_treasure import Treasure
#name943meats23jet
class Pure_Big_Money(Player): class Pure_Big_Money(Player):
def take_action(self): def take_action(self):
@ -12,7 +12,7 @@ class Pure_Big_Money(Player):
hand = self.get_hand().get_supply() hand = self.get_hand().get_supply()
for c in hand: for c in hand:
if c.get_type() == Card.CardType.Treasure: if isinstance(c, Treasure):
choice = hand.index(c) choice = hand.index(c)
print(message + str(choice)) print(message + str(choice))
@ -39,7 +39,10 @@ class Pure_Big_Money(Player):
min_coin = self.get_hand().get_supply()[choice].get_purchase_power() min_coin = self.get_hand().get_supply()[choice].get_purchase_power()
for c in self.get_hand().get_supply(): for c in self.get_hand().get_supply():
if c.get_purchase_power() < min_coin and c.get_type() != Card.CardType.Treasure: # We want to do isinstance rather than not isinstance because we only want to evaluate this loop when we are
# evaluating an all treasure card hand as at that point the choice will be a treasure card, otherwise the
# choice will already be non-treasure and we don't need to check anything since this bot doesn't do action
if c.get_purchase_power() < min_coin and isinstance(c, Treasure):
min_coin = c.get_purchase_power() min_coin = c.get_purchase_power()
choice = self.get_hand().get_supply().index(c) choice = self.get_hand().get_supply().index(c)
@ -48,7 +51,7 @@ class Pure_Big_Money(Player):
def __get_first_non_Treasure(self): def __get_first_non_Treasure(self):
for c in self.get_hand().get_supply(): for c in self.get_hand().get_supply():
if c.get_type() != Card.CardType.Treasure: if not isinstance(c, Treasure):
return self.get_hand().get_supply().index(c) return self.get_hand().get_supply().index(c)
return 0 return 0

View File

@ -2,19 +2,19 @@ from table.supply import Supply
class Hand(Supply): class Hand(Supply):
def contains_one_of(self, acceptible_types): def contains_one_of(self, acceptable_class):
result = False result = False
unique_types = self.__get_unique_types() unique_class_instances = self.__get_unique_class_instances()
for at in acceptible_types: for uci in unique_class_instances:
result |= at in unique_types result |= isinstance(uci, acceptable_class)
return result return result
def get_card_type_count(self, card_type): def get_card_type_count(self, card_class):
result = 0 result = 0
for c in self._Supply__card: for c in self.get_supply():
if c.get_type() == card_type: if isinstance(c, card_class):
result += 1 result += 1
return result return result
@ -22,23 +22,21 @@ class Hand(Supply):
yes_no = False yes_no = False
found_at = -1 found_at = -1
for c in self._Supply__card: for c in self.get_supply():
if c.prevent_attack: if c.prevent_attack:
found_at = self._Supply__card.index(c) found_at = self.get_supply().index(c)
if found_at >= 0: if found_at >= 0:
owner = self._Supply__card[found_at].get_owner() owner = self.get_supply()[found_at].get_owner()
yes_no = "Y" == owner.get_general_input("Player " + str(owner.get_player_index()) + ", enter 'Y' if you'd " yes_no = "Y" == owner.get_general_input("Player " + str(owner.get_player_index()) + ", enter 'Y' if you'd "
"like to reveal " "like to reveal " + self.get_supply()[found_at].get_name() +
+ self._Supply__card[found_at].get_name() + " to block the " " to block the " + what_attack + " attack: ", str)
+ what_attack + " attack: ", str)
return yes_no return yes_no
def __get_unique_types(self): def __get_unique_class_instances(self):
unique_type = list() unique_class_instances = list()
for c in self._Supply__card: for c in self.get_supply():
current_type = c.get_type() if c not in unique_class_instances:
if not current_type in unique_type: unique_class_instances.append(c)
unique_type.append(current_type) return unique_class_instances
return unique_type

View File

@ -2,7 +2,8 @@ from player.deck import Deck
from player.discard import Discard from player.discard import Discard
from player.hand import Hand from player.hand import Hand
from player.counter import Counter from player.counter import Counter
from card.card import Card from card.basic.card_treasure import Treasure
from card.basic.card_action import Action
from random import randint from random import randint
@ -99,10 +100,10 @@ class Player:
def discard_from_hand(self, n): def discard_from_hand(self, n):
self.__hand.transfer_card_by_index(n, self.__discard) self.__hand.transfer_card_by_index(n, self.__discard)
def play_card(self, acceptable_card_type, chances, counter): def play_card(self, acceptable_card_class, chances, counter):
if chances > 0 and self.__hand.contains_one_of(acceptable_card_type): if chances > 0 and self.__hand.contains_one_of(acceptable_card_class):
hand_index = self.get_play_input("\nPlease identify a card from hand to play by providing its index: ", int) hand_index = self.get_play_input("\nPlease identify a card from hand to play by providing its index: ", int)
self.__check_play_card(hand_index, counter, acceptable_card_type, chances) self.__check_play_card(hand_index, counter, acceptable_card_class, chances)
elif chances <= 0: elif chances <= 0:
print("You have used up all of your chances to enter a valid integer; forfeiting remaining plays.") print("You have used up all of your chances to enter a valid integer; forfeiting remaining plays.")
if counter is not None: if counter is not None:
@ -113,18 +114,17 @@ class Player:
counter.int = 0 counter.int = 0
def take_action(self): def take_action(self):
print("\nPlease play an Action, Attack, or Reaction card until you have no remaining actions.") print("\nPlease play an Action card until you have no remaining actions.")
while self.__actions.int > 0: while self.__actions.int > 0:
self.play_card([Card.CardType.Action, Card.CardType.Attack, Card.CardType.Reaction], self.play_card(Action, self.__std_chances, self.__actions)
self.__std_chances, self.__actions)
def take_buy(self): def take_buy(self):
if self.__hand.contains_one_of([Card.CardType.Treasure]): if self.__hand.contains_one_of(Treasure):
print("\nPlease play all Treasure card that you want to play.") print("\nPlease play all Treasure card that you want to play.")
play_another = Counter(self.__hand.get_card_type_count(Card.CardType.Treasure)) play_another = Counter(self.__hand.get_card_type_count(Treasure))
while play_another.int > 0: while play_another.int > 0:
self.play_card([Card.CardType.Treasure], self.__std_chances, play_another) self.play_card(Treasure, self.__std_chances, play_another)
self.buy_card(self.__std_chances) self.buy_card(self.__std_chances)
def buy_card(self, chances): def buy_card(self, chances):
@ -169,15 +169,15 @@ class Player:
print("\nPlayer " + str(self.__table.get_players().index(self)) + " Hand:") print("\nPlayer " + str(self.__table.get_players().index(self)) + " Hand:")
self.__hand.print() self.__hand.print()
def __check_play_card(self, hand_index, counter, acceptable_card_type, chances): def __check_play_card(self, hand_index, counter, acceptable_card_class, chances):
if hand_index < 0: if hand_index < 0:
print("You have elected to forfeit any remaining plays.") print("You have elected to forfeit any remaining plays.")
if counter is not None: if counter is not None:
counter.int = 0 counter.int = 0
elif hand_index >= self.__hand.get_remaining(): elif hand_index >= self.__hand.get_remaining():
print("Acceptable inputs range from 0 to " + str(self.__hand.get_remaining() - 1) + ". 1 chance lost.") print("Acceptable inputs range from 0 to " + str(self.__hand.get_remaining() - 1) + ". 1 chance lost.")
self.play_card(acceptable_card_type, chances - 1, counter) self.play_card(acceptable_card_class, chances - 1, counter)
elif self.__hand.get_card(hand_index).get_type() in acceptable_card_type: elif isinstance(self.__hand.get_card(hand_index), acceptable_card_class):
print("Player " + str(self.get_player_index()) + " playing: " + self.__hand.get_card(hand_index).get_name()) print("Player " + str(self.get_player_index()) + " playing: " + self.__hand.get_card(hand_index).get_name())
play_card = self.__hand.get_card(hand_index) play_card = self.__hand.get_card(hand_index)
play_card.play() play_card.play()
@ -187,7 +187,7 @@ class Player:
self.__print() self.__print()
else: else:
print("Index in bounds but not an acceptable card type. Chance to get it right reduced.") print("Index in bounds but not an acceptable card type. Chance to get it right reduced.")
self.play_card(acceptable_card_type, chances - 1, counter) self.play_card(acceptable_card_class, chances - 1, counter)
# The following two methods are identical under different names so they can be overridden by bot classes later # The following two methods are identical under different names so they can be overridden by bot classes later
def get_play_input(self, message, target_type): def get_play_input(self, message, target_type):

View File

@ -1,5 +1,6 @@
from table.supply import Supply from table.supply import Supply
class Pile(Supply): class Pile(Supply):
def __init__(self, card): def __init__(self, card):
self.__card_group = card self.__card_group = card