diff --git a/README.md b/README.md new file mode 100644 index 0000000..b321cda --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# dominiongame \ No newline at end of file diff --git a/card.py b/card.py new file mode 100644 index 0000000..f204c4f --- /dev/null +++ b/card.py @@ -0,0 +1,44 @@ +from enum import Enum, auto + + +class Card: + class CardType(Enum): + Action = auto() + Attack = auto() + Treasure = auto() + Victory = auto() + Curse = auto() + Reaction = auto() + + def __init__(self, name, cost, cardtype, value, coin, action, reaction, buy, draw, effect): + self.__name = name + self.__cost = cost + self.__coin = coin + self.__type = cardtype + self.__action = action + self.__buy = buy + self.__draw = draw + self.__effect = effect + self.__value = value + self.__reaction = reaction + + def play(self, player): + player.add_actions(self.__action) + player.add_buys(self.__buy) + player.add_purchase_power(self.__coin) + player.add_reactions(self.__reaction) + player.draw_cards(self.__draw) + self.effect(player) + + def effect(self, player): + # This is here so that 'special' cards can override this function so that unique card effects can happen. + pass + + def get_name(self): + return self.__name + + def get_type(self): + return self.__type + + def identify(self): + return self.__name + ", " + str(self.__type) + ", " + str(self.__cost) \ No newline at end of file diff --git a/counter.py b/counter.py new file mode 100644 index 0000000..55ba6e2 --- /dev/null +++ b/counter.py @@ -0,0 +1,3 @@ +class Counter: + def __init__(self, n): + self.int = n \ No newline at end of file diff --git a/deck.py b/deck.py new file mode 100644 index 0000000..77e7faf --- /dev/null +++ b/deck.py @@ -0,0 +1,7 @@ +from supply import Supply +from random import shuffle + + +class Deck(Supply): + def shuffle(self): + shuffle(self._Supply__card) diff --git a/discard.py b/discard.py new file mode 100644 index 0000000..853493f --- /dev/null +++ b/discard.py @@ -0,0 +1,8 @@ +from supply import Supply + + +class Discard(Supply): + def cycle_card(self, deck): + while self.get_remaining() > 0: + self.transfer_top_card(deck) + deck.shuffle() diff --git a/game.py b/game.py new file mode 100644 index 0000000..95dea0a --- /dev/null +++ b/game.py @@ -0,0 +1,78 @@ +from table import Table +from player import Player +from card import Card + + +def main(): + game = list() + card_info = get_card_info() + setup_new_game(game, get_game_parameters(), card_info) + play_game(game[0]) + + +def setup_new_game(game_list, parameter, card_info): + t = Table() + humans = parameter[0] + bots = parameter[1] + + index = 0 + for p in parameter[2:]: + if p: + card = Card(card_info[index][0], card_info[index][1], card_info[index][2], card_info[index][3], + card_info[index][4], card_info[index][5], card_info[index][6], card_info[index][7], + card_info[index][8], card_info[index][9]) + t.add_pile(card, card_info[index][10]) + index += 1 + + for i in range(humans): + human = Player(True, t) + human.draw_deck(t, get_starting_deck()) + human.draw_hand() + t.add_player(human) + + # for i in range(bots): + # bot = Player(False, t) + # bot.draw_deck(t, get_starting_deck()) + # bot.draw_hand() + # t.add_player(bot) + + game_list.append(t) + + +def play_game(game_table): + game_table.play() + + +def get_game_parameters(): + return [1, 1, True, True, True, True, True, True, False, True, True, True, True, True, True, True, True, True, True] + + +def get_card_info(): + # [name, cost, cardtype, v, c, a, r, b, d, effect, count] - value to pass to Card() + return [["Copper", 0, Card.CardType.Treasure, 0, 1, 0, 0, 0, 0, None, 60], # 1 + ["Silver", 3, Card.CardType.Treasure, 0, 2, 0, 0, 0, 0, None, 40], # 2 + ["Gold", 6, Card.CardT]ype.Treasure, 0, 3, 0, 0, 0, 0, None, 30], # 3 + + ["Estate", 2, Card.CardType.Victory, 1, 0, 0, 0, 0, 0, None, 24], # 4 + ["Dutchy", 5, Card.CardType.Victory, 3, 0, 0, 0, 0, 0, None, 12], # 5 + ["Province", 8, Card.CardType.Victory, 6, 0, 0, 0, 0, 0, None, 12], # 6 + + ["Curse", 0, Card.CardType.Curse, -1, 0, 0, 0, 0, 0, None, 30], # 7 + + ["Cellar", 2, Card.CardType.Action, 0, 0, 1, 0, 0, 0, "Name", 10], # 8 + ["Market", 5, Card.CardType.Action, 0, 1, 1, 0, 1, 1, None, 10], # 9 + ["Merchant", 3, Card.CardType.Action, 0, 0, 1, 0, 0, 1, "Name", 10], # 10 + ["Militia", 4, Card.CardType.Attack, 0, 2, 0, 1, 0, 0, "Name", 10], # 11 + ["Mine", 5, Card.CardType.Action, 0, 0, 0, 0, 0, 0, "Name", 10], # 12 + ["Moat", 2, Card.CardType.Reaction, 0, 0, 0, 0, 0, 2, "Name", 10], # 13 + ["Remodel", 4, Card.CardType.Action, 0, 0, 0, 0, 0, 0, "Name", 10], # 14 + ["Smithy", 4, Card.CardType.Action, 0, 0, 0, 0, 0, 3, None, 10], # 15 + ["Village", 3, Card.CardType.Action, 0, 0, 2, 0, 0, 1, None, 10], # 16 + ["Workshop", 4, Card.CardType.Action, 0, 0, 0, 0, 0, 0, "Name", 10]] # 17 + + +def get_starting_deck(): + return [["Copper", 7], ["Estate", 3]] + # return [["Market", 2], ["Merchant", 2], ["Smithy", 2], ["Village", 2], ["Moat", 2]] + +main() \ No newline at end of file diff --git a/hand.py b/hand.py new file mode 100644 index 0000000..cc57b06 --- /dev/null +++ b/hand.py @@ -0,0 +1,28 @@ +from supply import Supply + + +class Hand(Supply): + def contains_one_of(self, acceptible_types): + result = False + unique_types = self.__get_unique_types() + + for at in acceptible_types: + result |= at in unique_types + return result + + def __get_unique_types(self): + unique_type = 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 + + def get_card_type_count(self, card_type): + result = 0 + + for c in self._Supply__card: + if c.get_type() == card_type: + result += 1 + return result \ No newline at end of file diff --git a/pile.py b/pile.py new file mode 100644 index 0000000..8e6892e --- /dev/null +++ b/pile.py @@ -0,0 +1,5 @@ +from supply import Supply + +class Pile(Supply): + def get_card_group(self): + return self._Supply__card[0] diff --git a/player.py b/player.py new file mode 100644 index 0000000..213facb --- /dev/null +++ b/player.py @@ -0,0 +1,169 @@ +from deck import Deck +from discard import Discard +from hand import Hand +from card import Card +from counter import Counter + + +class Player: + def __init__(self, human, table): + self.__deck = Deck() + self.__discard = Discard() + self.__hand = Hand() + self.__purchase_power = 0 + self.__actions = Counter(0) + self.__buys = 0 + self.__reactions = Counter(0) + self.__is_human = human + self.__table = table + + def add_actions(self, n): + self.__actions.int += n + + def add_purchase_power(self, n): + self.__purchase_power += n + + def add_buys(self, n): + self.__buys += n + + def add_reactions(self, n): + self.__reactions.int += n + + def get_score(self): + return 0 + + def draw_card(self): + self.__deck.transfer_top_card(self.__hand) + + def draw_cards(self, how_many): + spillover = how_many - self.__deck.get_remaining() + lacking_cards = spillover - self.__discard.get_remaining() + + if lacking_cards <= 0: + lacking_cards = 0 + elif lacking_cards == 1: + print("You are lacking " + str(lacking_cards) + " card. You cannot draw anymore.") + else: + print("You are lacking " + str(lacking_cards) + " cards. You cannot draw anymore.") + + if spillover > 0: + for i in range(how_many - spillover): + self.draw_card() + + self.__discard.cycle_card(self.__deck) + + for i in range(spillover - lacking_cards): + self.draw_card() + else: + for i in range(how_many): + self.draw_card() + + def draw_deck(self, table, deck_setup): + for ds in deck_setup: + index = table.get_pile_index_of_card(ds[0]) + for i in range(ds[1]): + table.get_pile(index).transfer_top_card(self.__deck) + self.__deck.shuffle() + + def draw_hand(self): + self.draw_cards(5) + + def discard_remaining_hand(self): + while self.__hand.get_remaining() > 0: + self.__hand.transfer_top_card(self.__discard) + + def __print_hand(self): + print("\nHand:") + self.__hand.print() + print("\n") + + def __print_discard(self): + print("\nDiscard:") + self.__discard.print() + print("\n") + + def __print_deck(self): + print("\nDeck") + self.__deck.print() + print("\n") + + def __print(self): + print("\nPlayer:") + print("Actions: " + str(self.__actions.int)) + print("Reactions: " + str(self.__reactions.int)) + print("Buys: " + str(self.__buys)) + print("Coin: " + str(self.__purchase_power)) + self.__print_hand() + self.__print_discard() + self.__print_deck() + + def __gain_turn_events(self): + self.__actions.int = 1 + self.__buys = 1 + self.__purchase_power = 0 + self.__reactions.int = 0 + + def play_card(self, acceptable_card_type, chances, counter): + if chances > 0 and self.__hand.contains_one_of(acceptable_card_type): + hand_index = int(input("Please identify a card from hand you would like to play by providing its index: ")) + + 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("Acceptible inputs range from 0 to " + str(self.__hand.get_remaining() - 1) + ". Try again.") + self.play_card(acceptable_card_type, chances - 1, counter) + elif self.__hand.get_card(hand_index).get_type() in acceptable_card_type: + self.__hand.get_card(hand_index).play(self) + self.__hand.transfer_card(hand_index, self.__discard) + if counter is not None: + counter.int -= 1 + self.__print() + else: + self.play_card(acceptable_card_type, chances - 1, counter) + elif chances <= 0: + print("You have used up all of your chances to enter a positive integer; forfeiting remaining plays.") + else: + print("There are no more acceptable cards in hand, moving to next phase.") + if counter is not None: + counter.int = 0 + + def take_action(self): + print("Please play an Action, Attack, or Reaction card until you have no remaining actions.") + while self.__actions.int > 0: + self.play_card([Card.CardType.Action, Card.CardType.Attack, Card.CardType.Reaction], 3, self.__actions) + + def give_reaction(self): + pass + + def take_reaction(self): + pass + + def take_buy(self): + if self.__hand.contains_one_of([Card.CardType.Treasure]): + print("Please play all Treasure cards that you want to play.") + + play_another = Counter(self.__hand.get_card_type_count(Card.CardType.Treasure)) + while play_another.int > 0: + self.play_card([Card.CardType.Treasure], 3, play_another) + self.buy_card(3) + + def buy_card(self, chances): + while self.__buys > 0 and not self.__table.are_there_any_empty_piles() and chances > 0: + pile_index = int(input("Please identify a pile from the table that you'd like to purchase: ")) + self.__table.get_pile(pile_index).transfer_top_card(self.__discard) + self.__buys -= 1 + + def take_turn(self): + print("Deck Size: " + str(self.__deck.get_remaining())) + self.__print_hand() + self.__gain_turn_events() + self.take_action() + # self.__print_discard() + # self.__print_deck() + # self.give_reaction() + self.take_buy() + self.discard_remaining_hand() + self.draw_hand() + self.__print_hand() \ No newline at end of file diff --git a/supply.py b/supply.py new file mode 100644 index 0000000..428e82e --- /dev/null +++ b/supply.py @@ -0,0 +1,30 @@ +class Supply: + def __init__(self): + self.__card = list() + + def add_card(self, card): + self.__card.append(card) + + def add_cards(self, card, n): + for i in range(n): + self.add_card(card) + + def get_supply(self): + return self.__card + + def transfer_top_card(self, recipient_supply): + self.transfer_card(len(self.__card) - 1, recipient_supply) + + def transfer_card(self, n, recipient_supply): + transfer_card = self.__card.pop(n) + recipient_supply.add_card(transfer_card) + + def get_card(self, n): + return self.__card[n] + + def get_remaining(self): + return len(self.__card) + + def print(self): + for c in self.__card: + print(c.identify()) diff --git a/table.py b/table.py new file mode 100644 index 0000000..b25b213 --- /dev/null +++ b/table.py @@ -0,0 +1,60 @@ +from trash import Trash +from pile import Pile + + +class Table: + def __init__(self): + self.__player = list() + self.__pile = list() + self.__trash = Trash() + self.__winner = None + self.__winning_score = 0 + + def add_player(self, p): + self.__player.append(p) + + def get_player(self, n): + return self.__player[n] + + def add_pile(self, card, n): + p = Pile() + p.add_cards(card, n) + self.__pile.append(p) + + def get_piles(self): + return self.__pile + + def get_pile(self, n): + return self.__pile[n] + + def get_pile_index_of_card(self, card_name): + result = 0 + for p in self.__pile: + if p.get_card_group().get_name() == card_name: + result = self.__pile.index(p) + return result + + def are_there_any_empty_piles(self): + result = False + for p in self.__pile: + result = result or p.get_remaining() == 0 + return result + + def play(self): + turn = 0 + # turn < 4 is for testing, otherwise endless as buying cards is not yet done + while not self.are_there_any_empty_piles() and turn < 4: + self.print() + self.__player[turn % len(self.__player)].take_turn() + turn += 1 + else: + self.print() + for p in self.__player: + if p.get_score() > self.__winning_score: + self.__winning_score = p.get_score + self.__winner = p + + def print(self): + print("Piles:") + for s in self.__pile: + print(s.get_card_group().identify() + ": " + str(s.get_remaining())) diff --git a/trash.py b/trash.py new file mode 100644 index 0000000..b2774c6 --- /dev/null +++ b/trash.py @@ -0,0 +1,5 @@ +from pile import Pile + + +class Trash(Pile): + pass \ No newline at end of file