My teacher wants us to create a program that runs n games of war and calculates the average number of battles, wars and double wars that occurred. He has very specific rules (not necessarily the actual rules of war). I know there's some redundant code in the deck building and splitting for each game, but that's how he wants it.
His rules are as follows:
- The start of a list should be considered the top of a deck.
- Whenever cards are 'won', they should be shuffled together and placed on the bottom of the winner's deck.
- Wars occur when the initial cards are identical. Each player deals three cards face down and picks one of those three at random to decide the war. If it's another tie, the process is repeated. If a player runs out of cards while putting three down, they use the last card they drew.
- Lastly, each battle OR war counts as a battle, each war is counted separately, and the 'double war' stat is only incremented when a war lasted exactly two rounds.
While it's not perfect or efficient, I'm fairly certain my code works as per the specifications, but his sample results differ from mine. His battles, wars, and double wars are ~365, ~23, and ~1.3 respectively (over 1000 games). Mine are ~265, ~20, ~2.6.
import random
def buildDeck():
'''Builds and returns a standard, shuffled deck of 52 playing cards.'''
ranks = [str(x) for x in range(2, 11)] + ['J', 'Q', 'K', 'A']
suits = ['D', 'H', 'S', 'C']
deck = [rank + ' of ' + suit for rank in ranks for suit in suits]
random.shuffle(deck)
return deck
def cardValue(card):
'''Receives a single card and returns the integer value of the card (2-14)'''
faceCards = ['J', 'Q', 'K', 'A']
if card[0] in faceCards:
return faceCards.index(card[0]) + 11
else:
return int(card[0])
def cleanUp(pileOne, pileTwo):
'''Receives both players piles and checks for and removes a placeholder in the last list index (empty item).'''
if not pileOne[-1]:
pileOne.pop()
if not pileTwo[-1]:
pileTwo.pop()
def cardChoice(pile):
'''During a war, checks if placeholder is present in last index (meaning the player ran out of cards before putting three down). If present, return last actual card added. If not present, pick from the last three at random.'''
if pile[-1]:
return random.choice(pile[-3:])
else:
return pile[-2]
def replaceCards(deck, pileOne, pileTwo):
'''Shuffles together both players piles and appends them to the bottom of the winners deck.'''
pileOne.extend(pileTwo)
random.shuffle(pileOne)
deck.extend(pileOne)
def playWar():
deck = buildDeck()
battleStat, warStat, doubleWarStat = 0, 0, 0
pileOne, pileTwo, deckOne, deckTwo = [], [], [], []
for i in xrange(26):
deckOne.append(deck.pop())
deckTwo.append(deck.pop())
while deckOne and deckTwo:
del pileOne[:], pileTwo[:]
pileOne.append(deckOne.pop(0))
pileTwo.append(deckTwo.pop(0))
if cardValue(pileOne[0]) > cardValue(pileTwo[0]):
battleStat += 1
replaceCards(deckOne, pileOne, pileTwo)
elif cardValue(pileTwo[0]) > cardValue(pileOne[0]):
battleStat += 1
replaceCards(deckTwo, pileOne, pileTwo)
else:
warCount = 1
while True:
battleStat += 1
warStat += 1
for i in xrange(3):
if deckOne:
pileOne.append(deckOne.pop(0))
elif pileOne[-1]:
pileOne.append('')
if deckTwo:
pileTwo.append(deckTwo.pop(0))
elif pileTwo[-1]:
pileTwo.append('')
if cardValue(cardChoice(pileOne)) > cardValue(cardChoice(pileTwo)):
cleanUp(pileOne, pileTwo)
replaceCards(deckOne, pileOne, pileTwo)
if warCount == 2:
doubleWarStat += 1
break
elif cardValue(cardChoice(pileTwo)) > cardValue(cardChoice(pileOne)):
cleanUp(pileOne, pileTwo)
replaceCards(deckTwo, pileOne, pileTwo)
if warCount == 2:
doubleWarStat += 1
break
warCount += 1
return battleStat, warStat, doubleWarStat
def warStats(n):
'''Records statistics from n number of War games.
Prints out calculated averages for battles, wars, and double wars.'''
battleStat, warStat, doubleWarStat = 0.0, 0.0, 0.0
for i in range(n):
result = playWar()
battleStat += result[0]
warStat += result[1]
doubleWarStat += result[2]
print '\nAvg # of battles: ' + str(battleStat/n)
print 'Avg # of wars: ' + str(warStat/n)
print 'Avg # of double wars: ' + str(doubleWarStat/n)
while True:
selection = raw_input('\nHow many games of war would you like to play? (\'q\' to exit): ')
try:
if selection.lower()[0] == 'q':
break
elif 0 < int(selection):
warStats(int(selection))
else:
print '\nPlease enter an integer between 1 and 1000'
except:
print '\nPlease enter an integer between 1 and 1000'