Last active
April 16, 2025 12:11
-
-
Save Winslett/a0167f1eee56c186a1b17c954de701d8 to your computer and use it in GitHub Desktop.
test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import random | |
import numpy as np | |
import time | |
from tabulate import tabulate | |
import csv | |
from itertools import chain | |
class Card: | |
def __init__(self, name, cost_value, space_value, available): | |
self.name = name | |
self.cost_value = cost_value | |
self.space_value = space_value | |
self.initial_available = available | |
self.available = available | |
def reset_availability(self): | |
"""Resets the card's availability to its initial count.""" | |
self.available = self.initial_available | |
def __update_availability__(self, num_bought): | |
self.available += (-1 * num_bought) | |
def __str__(self): | |
return f'{self.name} (Cost:{self.cost_value})' | |
class ActionCard(Card): | |
def __init__(self, name, money_value, cost_value, victory_points, space_value, available, actions=0, extra_cards=0, buys=0, is_reaction=False): | |
super().__init__(name, cost_value, space_value, available) | |
self.money_value = money_value # Number of coins it provides per use | |
self.victory_points = victory_points # Number of victory points it is worth at the end of the game | |
self.actions = actions # Number of extra actions it grants | |
self.extra_cards = extra_cards # Number of extra cards it grants when played | |
self.buys = buys | |
self.is_reaction = is_reaction | |
def play_action(self,game, player): | |
""" Method to apply the effect of the action card to a player. For example, it can increase the player's actions and draw extra cards. """ | |
if player.actions > 0: | |
player.actions -= 1 | |
player.actions += self.actions | |
player.draw_cards(self.extra_cards) | |
player.buys += self.buys | |
player.coins += self.money_value | |
def __str__(self): | |
return f"{self.name} (Cost: {self.cost_value}, Actions: {self.actions}, Cards: {self.extra_cards})" | |
class Cellar(ActionCard): | |
def __init__(self, name, money_value, cost_value, victory_points, space_value, available, actions=0, extra_cards=0, buys=0): | |
super().__init__(name, money_value, cost_value, victory_points, space_value, available, actions, extra_cards, buys) | |
def play_action(self,game, player): | |
""" Override to apply the unique effect of the Cellar card to a player, including discarding based on card utility and future benefits. """ | |
if player.actions > 0: | |
player.actions -= 1 | |
# Calculate the average coin value of the player's deck | |
average_coin_value = self.calculate_average_coin_value(player) | |
# Calculate the potential value of extra cards drawn from the action card | |
potential_extra_cards_value = self.calculate_potential_extra_cards_value(player) | |
# Filter the cards in hand to find those with lower coin value than the average, but consider extra card benefits | |
cards_to_discard = [] | |
for card in player.hand: | |
# If the card has a lower value than the deck average and won't complement future actions, discard it | |
if card.money_value < average_coin_value and (card.money_value < potential_extra_cards_value): | |
cards_to_discard.append(card) | |
# Discard the least valuable cards (up to a number determined by the player) | |
for card in cards_to_discard: | |
player.discard_card(card) | |
self.extra_cards+=1 | |
# Draw the extra cards as usual | |
player.draw_cards(self.extra_cards) | |
player.buys += self.buys | |
player.coins += self.money_value | |
def calculate_average_coin_value(self, player): | |
""" Calculate the average coin value of the player's deck, considering all cards in hand, deck, and discard pile. """ | |
total_coin_value = 0 | |
total_cards = 0 | |
# Include the player's deck, discard pile, and hand for the calculation | |
all_cards = player.hand + player.deck + player.discard_pile | |
for card in all_cards: | |
total_coin_value += card.money_value | |
total_cards += 1 | |
return total_coin_value / total_cards if total_cards > 0 else 0 # Avoid division by zero | |
def calculate_potential_extra_cards_value(self, player): | |
""" Estimate the potential value of extra cards that may be drawn after playing the action card. """ | |
total_coin_value = 0 | |
total_cards = 0 | |
# Simulate the value of the extra cards based on their type | |
for _ in range(self.extra_cards): | |
total_coin_value += self.estimate_card_value(player) | |
total_cards += 1 | |
return total_coin_value / total_cards if total_cards > 0 else 0 # Avoid division by zero | |
def estimate_card_value(self, player): | |
""" Estimate the value of a card drawn from the deck (can be modified to simulate better). """ | |
return self.calculate_average_coin_value(player) | |
def __str__(self): | |
return f"{self.name} (Cost: {self.cost_value}, Actions: {self.actions}, Cards: {self.extra_cards})" | |
class Trasher(ActionCard): | |
def __init__(self, name, money_value, cost_value, victory_points, space_value, available, actions=0, extra_cards=0, buys=0, required_trashes = 0, optional_trashes = 0, trash_first = False, trash_location="hand" , trashing_effect= False): | |
super().__init__( name, money_value, cost_value, victory_points, space_value, available, actions=0, extra_cards=0, buys=0) | |
self.required_trashes = required_trashes | |
self.optional_trashes = optional_trashes | |
self.trash_first = trash_first | |
self.trash_location = trash_location | |
self.trashing_effect = trashing_effect | |
def play_action(self,game, player): | |
if self.trash_first: | |
print(f"{player.name} is trashing cards before gaining benefits.") | |
trashed = player.trash_cards(self.required_trashes, self.optional_trashes, self.trash_location ) | |
if trashed and self.trashing_effect: | |
super().play_action(game,player) | |
print(f"{player.name} trashed {[card.name for card in trashed]}.") | |
if not(self.trashing_effect): | |
super().play_action(game,player) | |
if not(self.trash_first): | |
print(f"{player.name} is trashing cards after gaining benefits.") | |
trashed = player.trash_cards(self.required_trashes, self.optional_trashes, self.trash_location) | |
print(f"{player.name} trashed {[card.name for card in trashed]}.") | |
class AttackCard(ActionCard): | |
def __init__(self, name, money_value, cost_value, victory_points, space_value, available, actions=0, extra_cards=0, buys=0, discard_down_to=None, gives_curse=False, trash_range=None, topdeck_victory=False): | |
super().__init__( name, money_value, cost_value, victory_points, space_value, available, actions=0, extra_cards=0, buys=0) | |
self.discard_down_to = discard_down_to # If set, forces discard down to X cards | |
self.gives_curse = gives_curse # If True, the attack gives a Curse | |
self.trash_range = trash_range # If set, trashes cards in a cost range | |
self.topdeck_victory = topdeck_victory # If True, forces topdecking a Victory card | |
def play_action(self,game, player): | |
super().play_action(game,player) | |
self.attack(game, player) | |
def attack(self, game, attacker): | |
"""Apply attack effects to all opponents.""" | |
for player in game.players: | |
if player != attacker and not player.has_reaction(): | |
self.apply_attack(player, game) | |
def apply_attack(self, player, game): | |
"""Handles discard, curse-giving, trashing, and bureaucrat effects dynamically.""" | |
if self.discard_down_to is not None: | |
while len(player.hand) > self.discard_down_to: | |
discarded_card = player.choose_discard() | |
player.discard_pile.append(discarded_card) | |
player.hand.remove(discarded_card) | |
print(f"{player.name} discards {discarded_card.name}") | |
curse_card = None #initialize curse_card | |
if self.gives_curse: | |
curse_cards = [card for card in game.supply if card.name == "Curse" and card.available > 0] | |
if curse_cards: | |
curse_card = next((card for card in game.supply if card.name == "Curse" and card.available > 0), None) | |
if curse_card: | |
player.discard_pile.append(curse_card) | |
curse_card.available -= 1 #reduce available amount | |
print(f"{player.name} gains a Curse!") | |
if self.trash_range is not None: | |
top_card = player.reveal_top() | |
if top_card and self.trash_range[0] <= top_card.cost <= self.trash_range[1]: | |
player.trash(top_card, game.trash_pile) | |
print(f"{player.name} trashes {top_card.name}") | |
else: | |
print(f"{player.name} reveals {top_card.name}, but it is safe.") | |
if self.topdeck_victory: | |
victory_cards = [card for card in player.hand if card.is_victory()] | |
if victory_cards: | |
chosen_card = player.choose_victory_to_topdeck(victory_cards) | |
player.hand.remove(chosen_card) | |
player.deck.insert(0, chosen_card) | |
print(f"{player.name} places {chosen_card.name} on top of their deck.") | |
else: | |
print(f"{player.name} reveals they have no Victory cards in hand.") | |
class VictoryCard(Card): | |
def __init__(self, name, cost_value, victory_points, space_value, available): | |
super().__init__(name, cost_value, space_value, available) | |
self.victory_points = victory_points | |
self.money_value = 0 | |
def __str__(self): | |
return f'{self.name} (Cost: {self.cost_value}, Victory_Points: {self.victory_points})' | |
class TreasureCard(Card): | |
def __init__(self, name, money_value, cost_value, space_value, available): | |
super().__init__(name, cost_value, space_value, available) | |
self.money_value = money_value | |
def __str__(self): | |
return f"{self.name} (Cost: {self.cost_value}, Money: {self.money_value})" | |
class Player: | |
def __init__(self, name, card_names, action_card_names, buy_priority_system=None, play_action_priority_system=None): | |
self.name = name | |
self.initial_deck = [TreasureCard('Copper', 1, 0, 1, 50)] * 7 + [VictoryCard('Estate', 2, 1, 1, 8)] * 3 | |
self.buy_priority_system = buy_priority_system if buy_priority_system else generate_random_priority(card_names) | |
self.wins = 0 | |
self.losses = 0 # Initialize losses | |
self.ties = 0 # Initialize tiebreaker_score | |
self.total_VP=0 | |
self.totalRevenue = 0 | |
self.unusedcoins = 0 | |
self.totalCards = [] | |
def reset_deck(self): | |
"""Resets the player's deck, discard pile, and hand to the initial state.""" | |
self.deck = self.initial_deck.copy() | |
random.shuffle(self.deck) | |
self.discard_pile = [] | |
self.play_area = [] | |
self.hand = [] | |
self.actions = 1 # Default number of actions per turn | |
self.buys = 1 | |
self.coins = 0 | |
self.draw_cards(5) | |
def draw_cards(self, num): | |
"""Draw 'num' cards from the deck (simulation for this example).""" # Check if the deck has enough cards | |
if len(self.deck) < num: | |
# Shuffle the discard pile back into the deck and shuffle | |
self.shuffle_deck() | |
# Draw the cards from the deck | |
drawn_cards = self.deck[:num] | |
self.hand.extend(drawn_cards) | |
self.deck = self.deck[num:] | |
# Remove the drawn cards from the deck | |
print(f"{self.name} drew {num} cards.") | |
def shuffle_deck(self): | |
"""SHuffle the discard pile into the deck and shuffle the deck.""" | |
random.shuffle(self.discard_pile) | |
self.deck.extend(self.discard_pile) | |
self.discard_pile.clear() | |
def play_card(self, card, game): | |
"""Simulate playing a card.""" | |
print(f"{self.name} plays {card.name}") | |
if isinstance(card, ActionCard): | |
card.play_action(game, self) | |
def trash_cards(self, required_trashes, optional_trashes, trash_location): | |
"""Trashes the specified number of required and optional cards based on the player's buy priority.""" | |
trashed_cards = [] | |
# Determine the source of trashing | |
if trash_location == "hand": | |
source = self.hand | |
elif trash_location == "deck": | |
source = self.deck | |
elif trash_location == "discard": | |
source = self.discard_pile | |
else: | |
print(f"Invalid trash location: {trash_location}") | |
return [] | |
# Ensure Estates and Coppers are in the buy priority list | |
adjusted_priority = self.buy_priority_system.copy() | |
if "Estate" not in adjusted_priority: | |
adjusted_priority.append("Estate") | |
if "Copper" not in adjusted_priority: | |
adjusted_priority.append("Copper") | |
if "Curse" not in adjusted_priority: | |
adjusted_priority.append("Curse") | |
# Sort the cards in source by their position in the buy priority list (lower index = more desirable to keep) | |
source.sort(key=lambda card: adjusted_priority.index(card.name) if card.name in adjusted_priority else float('inf')) | |
# Trash the required number of cards, starting from the least desirable | |
for _ in range(required_trashes): | |
if source: | |
trashed_card = source.pop(-1) # Remove from the end (least desirable) | |
trashed_cards.append(trashed_card) | |
# Trash optional cards if possible | |
for _ in range(optional_trashes): | |
if source: | |
trashed_card = source.pop(-1) | |
trashed_cards.append(trashed_card) | |
else: | |
break # Stop if there are no more cards to trash | |
print(f"{self.name} trashed {[card.name for card in trashed_cards]} from {trash_location}.") | |
return trashed_cards | |
def discard_card(self, card): | |
""" Discards a card by adding it to the discard pile. """ | |
if card in self.hand: | |
self.hand.remove(card) # Remove the card from the hand | |
self.discard_pile.append(card) # Add the card to the discard pile | |
else: | |
print(f"Card {card.name} is not in hand to discard.") | |
def choose_discard(self): | |
# Rank cards based on their position in the player's buy priority list | |
card_priorities = {card: self.get_card_buy_priority(card) for card in self.hand} | |
# Sort cards by priority (based on their position in the buy priority list) | |
sorted_cards = sorted(self.hand, key=lambda card: card_priorities[card], reverse=True) | |
# Discard cards until we reach the target count | |
cards_to_discard = sorted_cards[self.discard_down_to:] | |
discarded_cards = [] | |
for card in cards_to_discard: | |
discarded_cards.append(card) | |
self.hand.remove(card) | |
self.discard_pile.append(card) | |
print(f"{self.name} discards {card.name}") | |
return discarded_cards | |
def get_card_buy_priority(self, card): | |
""" | |
Determines the priority of a card based on its position in the player's buy priority list. | |
The position in the list corresponds to the priority. | |
""" | |
try: | |
# If the card is in the buy priority list, return its index (position in the list) | |
return self.buy_priority.index(card.name) | |
except ValueError: | |
# If the card is not in the buy priority list, give it a low priority (e.g., last) | |
return len(self.buy_priority) | |
def has_reaction(self): | |
"""Checks if the player has any reaction cards in their hand.""" | |
for card in self.hand: | |
if isinstance(card, ActionCard) and card.is_reaction: | |
return True | |
return False | |
# Initializing my cards | |
cards = [ | |
VictoryCard('Province', 8, 6, 1, 8), | |
TreasureCard('Gold', 3, 6, 1, 20), | |
VictoryCard('Duchy', 5, 3, 1, 8), | |
AttackCard(name="Witch", money_value=0, cost_value=5, victory_points=0, space_value=0, available=True, actions=0, extra_cards=2, buys=0, discard_down_to=None, gives_curse=True, trash_range=None, topdeck_victory=False), | |
ActionCard('Laboratory', 0, 5, 0, 1, 10, actions = 1, extra_cards = 2, buys = 0,is_reaction = False), | |
ActionCard('Festival', 2, 5, 0, 1, 10, actions = 2, extra_cards = 0, buys = 1,is_reaction = False), | |
ActionCard('Market', 1, 5, 0, 1, 10, actions = 1, extra_cards = 1, buys = 1,is_reaction = False), | |
Trasher("Money Lender", 3, 4, 0, 1, 10, required_trashes=1, optional_trashes=0, trash_first=True, trash_location="hand", trashing_effect=True ), | |
TreasureCard('Silver', 2, 3, 1, 30), | |
ActionCard("Moat", 0, 2, 0, 1, 10, actions = 0, extra_cards = 0, buys = 0, is_reaction = True), | |
Cellar(name="Cellar", money_value=0, cost_value=2, victory_points=0, space_value=1, available=10, actions=1, extra_cards=0, buys=0), | |
Trasher("Chapel", 0, 2, 0, 1, 10, required_trashes=0, optional_trashes=4, trash_first=True, trash_location="hand", trashing_effect=False ), | |
VictoryCard('Estate', 2, 1, 1, 8), | |
TreasureCard('Copper', 1, 0, 1, 50), | |
VictoryCard('Curse', 0, -1, 1, 10) | |
] | |
# Extract card names from the card objects | |
card_names = [card.name for card in cards] | |
action_card_names = [card.name for card in cards if isinstance(card, ActionCard)] | |
victory_card_names = [card.name for card in cards if isinstance(card, VictoryCard)] | |
treasure_card_names = [card.name for card in cards if isinstance(card, TreasureCard)] | |
def generate_random_priority(card_names): | |
"""Generate a random card purchasing priority list.""" | |
priority_list = [card for card in card_names if card != "Curse"] # Exclude "Curse" | |
priority_list = [card for card in priority_list if random.randint(1, 4) != 1] | |
return priority_list | |
num_players = int(input('How many players are in this tournament (4-128)? : ')) | |
players = [Player(f'Player {i+1}', card_names, action_card_names) for i in range(num_players)] | |
for player in players: | |
print(f"{player.name}'s buying priority: {player.buy_priority_system}") | |
cards.append(VictoryCard('Curse', 0, -1, 1, 10)) | |
def action_phase(player,game): | |
"""Simulates the action phase of a player's turn, prioritizing action cards based on play_action_priority_system.""" | |
print(f"\n{player.name}'s Action Phase begins.") | |
# Iterate over a COPY of player.hand to prevent modification issues | |
for card in player.hand[:]: | |
if isinstance(card, ActionCard): | |
player.play_card(card, game) # Play the action card | |
if card in player.hand: # Only remove if it wasn't trashed | |
player.play_area.append(card) # Move it to play area | |
player.hand.remove(card) | |
player.discard_pile.extend(player.play_area) # Move played cards to discard pile | |
player.play_area.clear() # Clear play area | |
print(f"{player.name}'s Action Phase ends.\n") | |
def treasure_phase(player): | |
"""Simulates the Treasure Phase where a player plays all Treasure Cards to generate coins.""" | |
print(f"\n{player.name}'s Treasure Phase begins.") | |
# Find all Treasure Cards in the player's hand | |
treasure_cards = [card for card in player.hand if isinstance(card, TreasureCard)] | |
if not treasure_cards: | |
print(f"{player.name} has no Treasure Cards to play.") | |
else: | |
for card in treasure_cards: | |
player.coins += card.money_value | |
# Move all played Treasure Cards to the discard pile | |
player.hand = [card for card in player.hand if not isinstance(card, TreasureCard)] | |
player.discard_pile.extend(treasure_cards) | |
print(f"{player.name} has {player.coins} coins available for the Buy Phase.") | |
print(f"{player.name}'s Treasure Phase ends.\n") | |
def buy_phase(player, cards): | |
"""Simulates the Buy Phase where a player purchases cards based on their buy_priority_system.""" | |
print(f"\n{player.name}'s Buy Phase begins.") | |
#storing hand info | |
player.totalRevenue+=player.coins | |
while player.buys > 0 and player.coins >= 0: | |
# Look through the player's buy priority and find an affordable, available card | |
for card_name in player.buy_priority_system: | |
# Find the card object in the main cards list | |
chosen_card = next((card for card in cards if card.name == card_name), None) | |
if chosen_card and chosen_card.cost_value <= player.coins and chosen_card.available > 0: | |
# Buy the card | |
player.coins -= chosen_card.cost_value | |
player.buys -= 1 | |
player.discard_pile.append(chosen_card) | |
chosen_card.__update_availability__(1) # Reduce availability by 1 | |
print(f"{player.name} buys {chosen_card.name} for {chosen_card.cost_value} coins.") | |
break # Move to next buy (if any buys remain) | |
else: | |
# No affordable cards found | |
break | |
print(f"{player.name} has {player.coins} coins left and {player.buys} buys remaining.") | |
player.unusedcoins += player.coins | |
print(f"{player.name}'s Buy Phase ends.\n") | |
def cleanup_phase(player): | |
"""Simulate the Clean-Up Phase where the player discards their hand and draws new cards.""" | |
print(f"{player.name}'s Clean-Up Phase:") | |
# Discard all cards in hand and played cards | |
player.discard_pile.extend(player.hand) | |
player.hand.clear() | |
# Draw 5 new cards | |
player.draw_cards(5) | |
# Unused Actions, Buys, and Coins do not carry over | |
player.actions = 1 | |
player.buys = 1 | |
player.coins = 0 | |
print(f"{player.name} ends their turn.") | |
def play_turn(player, cards, game): | |
"""Simulates a full turn for a given player, including Action, Treasure, and Buy phases.""" | |
print(f"\n===== {player.name}'s Turn =====") | |
action_phase(player,game) # Call the Action Phase | |
treasure_phase(player) # Call the Treasure Phase | |
buy_phase(player, cards) # Call the Buy Phase | |
cleanup_phase(player) | |
print(f"===== {player.name}'s Turn Ends =====\n") | |
def game_over(cards): | |
"""Checks if the game should end based on depletion conditions.""" | |
province_card = next((card for card in cards if card.name == "Province"), None) | |
# End game if Provinces are gone | |
if province_card and province_card.available <= 0: | |
print("All Provinces are gone! Game over.") | |
return True | |
# End game if 3 supply piles are empty | |
empty_piles = sum(1 for card in cards if card.available <= 0) | |
if empty_piles >= 3: | |
print("Three supply piles are empty! Game over.") | |
return True | |
return False | |
def determine_winner(player1, player2): | |
"""Determines the winner by counting Victory Points.""" | |
def count_victory_points(player): | |
# Combine all zones: deck, hand, discard_pile, and play_area | |
all_cards = player.deck + player.hand + player.discard_pile + player.play_area | |
# Count total victory points from all VictoryCard instances | |
total_points = sum(card.victory_points for card in all_cards if isinstance(card, VictoryCard)) | |
return total_points | |
p1_score = count_victory_points(player1) | |
p2_score = count_victory_points(player2) | |
player1.total_VP += p1_score | |
player2.total_VP += p2_score | |
print(f"{player1.name} Victory Points: {p1_score}") | |
print(f"{player2.name} Victory Points: {p2_score}") | |
if p1_score > p2_score: | |
return player1 | |
elif p2_score > p1_score: | |
return player2 | |
else: | |
return int(0) | |
def play_game(player1, player2, cards): | |
"""Simulates a full game between two players.""" | |
print("\n===== New Game Begins =====") | |
turn_count = 1 | |
max_turns = 24 # Set a reasonable limit to prevent infinite games | |
#create a game object | |
class Game: | |
def __init__(self, players, supply): | |
self.players = players | |
self.supply = supply | |
self.trash_pile = [] | |
game = Game([player1, player2], cards) | |
while turn_count <= max_turns: | |
print(f"\n===== Turn {turn_count} =====") | |
# Player 1's turn | |
play_turn(player1, cards, game) #pass game | |
# Check if the game should end | |
if game_over(cards): | |
break | |
# Player 2's turn | |
play_turn(player2, cards, game) #pass game | |
# Check if the game should end | |
if game_over(cards): | |
break | |
turn_count += 1 | |
print("\n===== Game Over! =====") | |
# Determine the winner | |
winner = determine_winner(player1, player2) | |
return winner | |
# Round-Robin Tournament function | |
def play_round_robin(players,cards): | |
"""Schedules a round-robin tournament where each player plays against every other player.""" | |
print("\nRound-Robin Tournament Starting...\n") | |
# Each player plays against every other player | |
for i in range(len(players)): | |
for j in range(i + 1, len(players)): | |
print(f"\n{players[i].name} vs {players[j].name}") | |
if 1 == random.randint(1,2): | |
player1 = players[i] | |
player2 = players[j] | |
else: | |
player1 = players[j] | |
player2 = players[i] | |
# Reset players' decks | |
player1.reset_deck() | |
player2.reset_deck() | |
# Reset card availabilities | |
for card in cards: | |
card.reset_availability() | |
winner = play_game(player1, player2, cards) | |
if winner != 0: | |
if winner == players[i]: | |
players[j].losses +=1 | |
if winner == players[j]: | |
players[i].losses +=1 | |
winner.wins += 1 | |
print(f"{winner.name} wins this match!\n") | |
else: | |
players[i].ties+=1 | |
players[j].ties+=1 | |
print('Tie') | |
for player in [players[i], players[j]]: | |
player.totalCards.extend((player.discard_pile)) | |
player.totalCards.extend((player.deck)) | |
def display_tournament_results(players): | |
headers = ["Player", "Wins", "Losses", "Ties", "Win Rate", "Total Victory Points", "Total Revenue", "Total Unused Coins", "Buy Priority System"] | |
headers.extend([f"{card.name} Presence" for card in cards]) # Add a column for each card in 'cards' | |
table_data = [] | |
for player in players: | |
total_games = player.wins + getattr(player, 'losses', 0) + getattr(player, 'ties', 0) | |
win_rate = player.wins / total_games if total_games > 0 else 0 # Avoid division by zero | |
card_count = {} | |
total_cards = len(player.totalCards) # Total number of cards in player's deck | |
# Prepare percentage makeup for each card in card_count | |
# Count occurrences of each card in player.totalCards | |
for card in player.totalCards: | |
if hasattr(card, "name"): | |
card_name = card.name | |
card_count[card_name] = card_count.get(card_name, 0) + 1 | |
percentage_makeup = {} | |
for card_name, count in card_count.items(): | |
percentage_makeup[card_name] = (count / total_cards) * 100 # Percent presence for each card | |
# Debugging percentage makeup | |
print(f"{player.name}'s percentage makeup: {percentage_makeup}") # Debugging line | |
# Add row data for the current player | |
row = [ | |
player.name, | |
player.wins, | |
getattr(player, 'losses', 'N/A'), | |
getattr(player, 'ties', 'N/A'), | |
f"{win_rate:.2%}", # Format win rate as percentage | |
getattr(player, 'total_VP', 'N/A'), | |
getattr(player, 'totalRevenue', 'N/A'), | |
getattr(player, 'unusedcoins', 'N/A'), | |
player.buy_priority_system, | |
] | |
# Add the percentage for each card to the row | |
for card in cards: | |
row.append(f"{percentage_makeup.get(card.name, 0):.2f}%") # Add percentage for each card | |
table_data.append(row) | |
# Sort players by win rate (descending) | |
table_data.sort(key=lambda row: float(row[4].strip('%')) / 100, reverse=True) | |
# Print table using tabulate | |
print(tabulate(table_data, headers=headers, tablefmt="grid")) | |
# Save as a CSV file | |
with open("tournament_results.csv", "w", newline="") as f: | |
writer = csv.writer(f) | |
writer.writerow(headers) | |
writer.writerows(table_data) | |
print("\nResults saved to 'tournament_results.csv'") | |
# Determine the overall winner | |
winner = max(players, key=lambda p: (p.wins / (p.wins + getattr(p, 'losses', 1) + getattr(p, 'ties', 0)), p.wins)) | |
print(f"\n{winner.name} wins the Round-Robin Tournament with {winner.wins} wins!") | |
play_round_robin(players,cards) | |
display_tournament_results(players) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment