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 CardType(Enum):
Treasure = auto()
Action = auto()
Reaction = auto()
Attack = auto()
Victory = auto()
Curse = auto()
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.__cost = cost
self.__coin = coin
self.__type = cardtype
self.__action = action
self.__buy = buy
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.
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.
pass
def get_name(self):
return self.__name
def get_type(self):
return self.__type
def get_points(self):
return self.__value
@ -60,7 +53,15 @@ class Card:
return self.__owner
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):
result = -1
@ -69,13 +70,5 @@ class Card:
result = self._Card__owner.get_hand().get_player_index()
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):
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 card.card import Card
#name943meats23jet
from card.basic.card_treasure import Treasure
class Pure_Big_Money(Player):
def take_action(self):
@ -12,7 +12,7 @@ class Pure_Big_Money(Player):
hand = self.get_hand().get_supply()
for c in hand:
if c.get_type() == Card.CardType.Treasure:
if isinstance(c, Treasure):
choice = hand.index(c)
print(message + str(choice))
@ -39,7 +39,10 @@ class Pure_Big_Money(Player):
min_coin = self.get_hand().get_supply()[choice].get_purchase_power()
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()
choice = self.get_hand().get_supply().index(c)
@ -48,7 +51,7 @@ class Pure_Big_Money(Player):
def __get_first_non_Treasure(self):
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 0

View File

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

View File

@ -2,7 +2,8 @@ from player.deck import Deck
from player.discard import Discard
from player.hand import Hand
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
@ -99,10 +100,10 @@ class Player:
def discard_from_hand(self, n):
self.__hand.transfer_card_by_index(n, self.__discard)
def play_card(self, acceptable_card_type, chances, counter):
if chances > 0 and self.__hand.contains_one_of(acceptable_card_type):
def play_card(self, acceptable_card_class, chances, counter):
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)
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:
print("You have used up all of your chances to enter a valid integer; forfeiting remaining plays.")
if counter is not None:
@ -113,18 +114,17 @@ class Player:
counter.int = 0
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:
self.play_card([Card.CardType.Action, Card.CardType.Attack, Card.CardType.Reaction],
self.__std_chances, self.__actions)
self.play_card(Action, self.__std_chances, self.__actions)
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.")
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:
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)
def buy_card(self, chances):
@ -169,15 +169,15 @@ class Player:
print("\nPlayer " + str(self.__table.get_players().index(self)) + " Hand:")
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:
print("You have elected to forfeit any remaining plays.")
if counter is not None:
counter.int = 0
elif hand_index >= self.__hand.get_remaining():
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)
elif self.__hand.get_card(hand_index).get_type() in acceptable_card_type:
self.play_card(acceptable_card_class, chances - 1, counter)
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())
play_card = self.__hand.get_card(hand_index)
play_card.play()
@ -187,7 +187,7 @@ class Player:
self.__print()
else:
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
def get_play_input(self, message, target_type):

View File

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