Mathwizurd.com is created by David Witten, a mathematics and computer science student at Stanford University. For more information, see the "About" page.

The Problem with War

The Problem with War

Unlike any other "random" game, like Rock-Paper Scissors, or Poker, where there is a psychological element to winning the game, War (card game) is purely a game of luck.

Here are some basic rules: There are two players, and they each have half a deck. They both put down a card, and if you have a higher card you take both cards. If the cards tie, you put down three more cards, and the third card decides who keeps ALL eight of the cards. You finish the game when one person loses all of their cards.

I decided to simulate War games and I noticed some interesting things. 

Code:

def make_deck():
    deck = list(range(2,15))*4
    random.shuffle(deck)
    a,b = deck[:26],deck[26:]
    return a,b
2 = 2 and 14 = A, so it shuffles them and splits them in half. 

def cards(a,b,put_in = 0):
    if a[put_in] != b[put_in]:
        return [put_in + 1, a[put_in] > b[put_in]]
    else:
        add = min([3, min(len(a), len(b)) - put_in - 1])
        if add == 0:
            return [put_in, len(a) > len(b)]
        return cards(a,b,put_in + add)

The function was called cards, which returned the put_in, and whether "a" gets the pot. Essentially, the pot is a[:put_in] + b[:put_in], so put_in is basically how many cards each person would put into the pot.  When the card is different, put_in increases by one, because both players have to put in a new card. If it's tied, put_in increases by up to three (could be two, or even one if one person doesn't have enough cards). If add happens to be zero, that means one person has one card left and it's a tie, so the person with one card loses. 

def give(a,b):
    giver = cards(a,b)
    q = [a[:giver[0]],b[:giver[0]]][::(0 + [-1,1][giver[1]])]
    queue = q[0] + q[1]
    a,b = a[giver[0]:], b[giver[0]:]
    a += (queue if giver[1] else [])
    b += (queue if not(giver[1]) else [])
    return a,b

First, giver is the number of cards each person puts in, in addition to who keeps it. The order matters, so b is on top if they win, and a is on top if they win. Now, the first cards are taken off each pile, and the queue is added to the winner of the previous hand.

def simulate_game():
    a_deck, b_deck = make_deck()
    moves = 0
    saved_a = a_deck[:]
    saved_b = b_deck[:]
    while (a_deck and b_deck):
        moves += 1
        a_deck,b_deck = give(a_deck,b_deck)
        if moves > 3000:
            break
    return moves, saved_a, saved_b

The while loop will run until one of the two decks are empty. If moves exceeded 3000, it would stop, and the reason behind this is an interesting occurrence that happens in War.  

Major Problem

While testing this simulation, an interesting phenomenon was present. Sometimes, games would go on forever, and the game would arrive at a deadlock at 26-26. Originally, I thought this was my mistake, but after looking deeper into it, it occurs whenever each deck alternates with a win and a loss, and it happens surprisingly often. 

Example:

Here's a simple example, [2,9], and [9,2]. Since they alternate wins and losses, the game will never finish, once the first person loses they'll have a 9 and the other person will have the 2, and it'll keep iterating. 

Example fail: 

A: [11, 10, 14, 5, 14, 9, 2, 11, 12, 10, 5, 4, 7, 5, 14, 8, 13, 13, 9, 4, 7, 6, 4, 9, 3, 3] 
B: [5, 7, 12, 7, 11, 2, 13, 3, 12, 6, 14, 8, 13, 2, 11, 8, 6, 12, 4, 10, 8, 2, 9, 3, 6, 10]
If played correctly (i.e. After a hand, put their card(s) on the bottom, not yours), this game will go on forever

Analysis

After simulating 200,000 games, interesting trends were shown. First of all, 11.565% of games never finished. It was assumed that exceeding 3000 moves meant that the game would last forever, however it could have been possible that the game could've ended later. The average of those that finish are 247.096 moves. If you assume that each move takes five seconds, the average game takes about 20 minutes and 35 seconds. The longest game (in that simulation) took 2554 moves, or 3:32:50.  The longest game ever seen in a simulation was 2836 moves, or 3:56:20. 

Frequency of Game Lengths

Number of moves, in hundreds, is a range of a hundred numbers, beginning in the number above (e.g. 3 equals 300-399)

Note: Numbers above that were not shown, because the numbers were very small, and they wouldn't be able to be seen on the graph. 

 

Conclusion

War is not a good game qua game; it requires no skill, and almost 12% of the time, it will never finish. If you do end up having a game that can finish, it'll take an average of twenty minutes to win, and you might have to play for four hours until you can finish. In short, this game is equivalent to flipping a coin over and over- it's pure chance, and it shouldn't be considered a game.

David Witten
Itertools Demo

Itertools Demo

Matrix Operations in Python