How many Aces do you need to win at War?

Python is a fantastic language for answering life's questions. It's amazing how such a powerful and popular syntax could be so great for prototyping and trying things out. So one day,  I was teaching my three year old, the card game War

Rules (the version I learned as a kid):
1. Give each player half of a deck of cards face down.
2. Each player turns over a card.
      - The player with the larger card adds both cards to the bottom of their pile
3. If the cards are the same, then it is War!
      - Each player adds three face down cards.
4. Repeat steps 2-3 as needed.
  
It's a great way to practice numeracy and comparing values while being simple enough to learn in a minute. He loved getting Aces and was sad when he started to lose (I have no control over this son!). It got me thinking, what is the minimum numbers of Aces needed to have a statistical advantage?

The trick for this code, was to make it optimized so it could run as many times as I wanted without using up too much memory or taking too long. Additionally, with a couple of data type changes, it became all the more important for me to brainstorm before what my program would look like. It is very tempting to jump right in but with a dynamically typed language like Python you will save a lot of headaches if you plan it out (for more resources on designing programs with your students I direct you to the Bootstrap Math Curriculum/Program).

Change num_games to see how many Aces are needed to have a statistical advantage and increase game_limit if you want to enable a game to go on longer (memory warning: some go on for a long time!). I'm really interested to see what fun discoveries you can make from the data in wins_num_hands.

So what question will you use technology to solve?

from random import shuffle

wins_num_aces = [0,0,0,0,0] #Each win, keep track of original number of aces
wins_num_hands = []
card_values = {'2':2, '3':3, '4':4, '5':5, '6':6, ' 7':7, '8':8, '9':9, '10':10, 
     'J':11, 'Q':12, 'K':13, 'A':14} #Ace is highest value

def createDeck():
    deck = []
    the_card_values = card_values    #local variable for faster processing
    for suit in range(0, 4):    #iterate through the dictionary 4 times
        for card in the_card_values:
            deck.append(card)
    shuffle(deck)    #From the random module
    return deck

class player:
    def __init__(self, halfDeck):
        self.hand = halfDeck
        self.num_aces = self.hand.count('A')    #Count aces in hand
        self.game_over = False
    def nextCard(self):
        try:
            return self.hand.pop()  #Returns next card if any left
        except IndexError:
            if self.game_over == False:
                winning_num_aces = 4 - self.num_aces
                wins_num_aces[winning_num_aces] += 1
                self.game_over = True
                return None
    def addCards(self, current_hand):    #Add the cards to the bottom of your hand
        self.hand = current_hand + self.hand


def warGame():
    deck = createDeck()
    game_limit = 10000
    p1 = player(deck[:26])    #split deck in half
    p2 = player(deck[-26:])
    current_hand = []
    the_card_values = card_values
    times = 0
    while True:
        times += 1
        if times < game_limit:    #restart game if going on too long
            try:    #Fails if there are no more cards left
                current_hand.extend([p1.nextCard(), p2.nextCard()])    
                p1_card = the_card_values[current_hand[-2]]    #2nd to last
                p2_card = the_card_values[current_hand[-1]]    #Last card
                if p1_card == p2_card:
                    for i in range(0,3):
                                current_hand.extend([p1.nextCard(),p2.nextCard()])
                elif p1_card > p2_card:
                    p1.addCards(current_hand)    
                    current_hand = []
                elif p1_card < p2_card:
                    p2.addCards(current_hand)
                    current_hand = []
            except KeyError:    #If a player runs out of cards (None)
                    wins_num_hands.append(times)    #Keeping track of data
                    break
                    return False    #Game is over
        else:
            break
            return True    #Too long a game, restart
            
 
numGames = 100    #Number of games to play
while numGames > 0:
    current_game = True
    while (current_game == True):
        current_game = warGame()
    numGames -= 1

print 'Wins with 0 Aces:',wins_num_aces[0]
print 'Wins with 1 Ace: ',wins_num_aces[1]
print 'Wins with 2 Aces:',wins_num_aces[2]
print 'Wins with 3 Aces:',wins_num_aces[3]
print 'Wins with 4 Aces:',wins_num_aces[4]
print 'Number of hands:', wins_num_hands