from enum import Enum
from random import shuffle, randint
from copy import deepcopy
STARTING_HAND_SIZE = 5
TRIALS = 10000
class Card(Enum):
PIKACHU_EX = 1
BASIC = 2
POKEBALL = 3
PROFESSORS_RESEARCH = 4
X_SPEED = 5
OTHER = 6
class Simulation:
hand = []
# Track whether pikachu was started, or swapped in later with X speed
is_pikachu_active = False
# Track whether pikachu was swapped in with X speed
x_speed_used = False
# Track whether pikachu is available
pikachu_available_at_one_energy = False
def __init__(self, basicCount, isFirstPlayer):
self.startingDeck = [Card.PIKACHU_EX,
Card.POKEBALL,
Card.PROFESSORS_RESEARCH,
Card.X_SPEED] * 2 + \
[Card.BASIC] * basicCount + \
[Card.OTHER] * (12 - basicCount)
self.deck = deepcopy(self.startingDeck)
self.isFirstPlayer = isFirstPlayer
shuffle(self.deck)
# Add the top card to your hand and remove it from your deck
def drawCard(self, times=1):
for card in range(times):
self.hand.append(self.deck.pop())
# Attempt to draw a starting hand until a hand is drawn that contains a basic
def drawStartingHand(self):
while not (Card.PIKACHU_EX in self.hand or Card.BASIC in self.hand):
self.hand = []
self.deck = deepcopy(self.startingDeck)
shuffle(self.deck)
self.drawCard(5)
# Pot of Greed allows me to draw two new cards from my deck!
def playProfessorsResearchIfAble(self):
if Card.PROFESSORS_RESEARCH in self.hand:
self.hand.remove(Card.PROFESSORS_RESEARCH)
self.drawCard(2)
# If XSpeed is in hand and pikachu isn't active, swap pikachu to active.
def playXSpeed(self):
if not self.is_pikachu_active and Card.X_SPEED in self.hand:
self.is_pikachu_active = True
self.x_speed_used = True
# Play all pokeballs in hand, each fetches a random basic from your deck.
def playPokeballs(self):
while Card.POKEBALL in self.hand:
self.hand.remove(Card.POKEBALL)
pikachu_count = self.deck.count(Card.PIKACHU_EX)
other_basic_count = self.deck.count(Card.BASIC)
total_basic_count = pikachu_count + other_basic_count
if total_basic_count > 0:
randomChoice = randint(1,total_basic_count)
if randomChoice <= pikachu_count:
self.hand.append(self.deck.pop(self.deck.index(Card.PIKACHU_EX)))
else:
self.hand.append(self.deck.pop(self.deck.index(Card.BASIC)))
shuffle(self.deck)
def hasFullBenchWithPikachu(self):
countPikachuInHand = self.hand.count(Card.PIKACHU_EX)
countBasicsInHand = self.hand.count(Card.BASIC) + countPikachuInHand
return countPikachuInHand >= 1 and countBasicsInHand >= 4
def hasAtLeastTwoBenchWithPikachu(self):
countPikachuInHand = self.hand.count(Card.PIKACHU_EX)
countBasicsInHand = self.hand.count(Card.BASIC) + countPikachuInHand
return countPikachuInHand >= 1 and countBasicsInHand >= 3
def doTurn(self, isFirstEnergyTurn = False):
self.drawCard()
# If we have enough basics, we want to deck thin before research
if (self.hasFullBenchWithPikachu()):
self.playPokeballs()
self.playProfessorsResearchIfAble()
if isFirstEnergyTurn:
if not self.hasPikachu():
# play all pokeballs in a last attempt to get a pikachu to play for first energy
self.playPokeballs()
if self.hasPikachu():
self.pikachu_available_at_one_energy = True
def runSimulation(self):
#turn 0
self.drawStartingHand()
self.is_pikachu_active = Card.PIKACHU_EX in self.hand
#turn 1
self.doTurn(isFirstEnergyTurn = not self.isFirstPlayer)
#turn 2
self.doTurn(isFirstEnergyTurn = self.isFirstPlayer)
if self.isFirstPlayer:
self.doTurn()
# We play our pokeballs at the last possible moment to maximize our odds of drawing basics from other draw sources
self.playPokeballs()
self.playXSpeed()
def hasPikachu(self):
return Card.PIKACHU_EX in self.hand
print("| extra basics| pika90 | pika60 | pika | x speed |")
print("| ------ | ------ | ----- | ---- | ----- |")
for isFirstPlayer in [True, False]:
if isFirstPlayer:
print("First Player")
else:
print("Second Player")
for basicCount in range(0, 11):
successCount = 0
x_speed_used = 0
pika60 = 0
hasPika = 0
for trial in range(TRIALS):
simulation = Simulation(basicCount, isFirstPlayer)
simulation.runSimulation()
successCount += simulation.hasFullBenchWithPikachu() and simulation.is_pikachu_active and simulation.pikachu_available_at_one_energy
x_speed_used += simulation.x_speed_used
pika60 += simulation.hasAtLeastTwoBenchWithPikachu() and simulation.is_pikachu_active and simulation.pikachu_available_at_one_energy
hasPika += simulation.hasPikachu()
print(" | ", basicCount, " | ", round(successCount/TRIALS,2), " | ", round(pika60/TRIALS,2), " | ", round(hasPika/TRIALS,2), " | ", round(x_speed_used/TRIALS,2), " | ")