What’s in a game?
Written on May 20, 2008
Ever heard of the card game, ‘War’?
A deck of cards is evenly split between two players. The players compare the top cards from their respective piles. Comparison is performed by rank alone; suit is irrelevant. The player with the higher ranking card collects both cards and places them at the bottom of their pile. In the event that both cards have equal rank (a “War” situation), both players set aside three cards from their piles, and compare the new top card of their piles. The winner of this second comparison collects the other card, as well as all the cards laid aside, and the cards from the orignal comparison. Should the cards in the second comparison be equal, an additional three cards from each player’s pile are set aside, and another comparison is made, etc. etc.
In the event that a comparison causes one player’s pile to run out of cards, they lose the game.
According to some house rules, if a player has insufficient cards to lay aside for a “War” and its ensuing secondary comparison, they take the required cards from the other player’s pile.
It’s kind of a stupid game. Arguably it’s not a game at all. The winner and loser are determined entirely by the deal of the cards. No decision-making takes place; players cannot alter the outcome of the game in any fashion. Play amounts to a revelation of who was dealt the winning pile; nothing more. My (admittedly verbose) implementation comes in at a little under 100 lines of code and there’s one special case it doesn’t handle.
War has always fascinated me. Since players’ actions don’t affect the outcome, it’s not really a game as we understand games. But the social experience of it is very much a game; ask any kid and he’ll tell you.
Version 1
class WarGame
attr_accessor :log_file
def initialize(deck_size = 52, suit_count = 4)
@deck_size = deck_size
@suit_count = suit_count
@rank = @deck_size / @suit_count
@deck = nil
@p1_deck = nil
@p2_deck = nil
@compare_count = 0
@log_file = File.new("logfile.txt","w")
end
#expects an even deck size
def shuffle_up_and_deal
@deck = (1..@deck_size).to_a.sort_by {rand}
@p1_deck = @deck[0,@deck_size/2]
@p2_deck = @deck[@deck_size/2,@deck_size/2]
@compare_count = 0
end
def play_the_game
game_over = false
while(!game_over)
#cards currently at stake are broken off into stacks
#usually this is just the single card being matched
#but this could conceivably be a large stack in the case of many 'wars'
p1_stack = Array.new
p2_stack = Array.new
p1_stack << @p1_deck.slice!(0)
p2_stack << @p2_deck.slice!(0)
#start the (possibly) recursive comparison for this turn
compare_stacks(p1_stack,p2_stack)
@compare_count += 1
if(@p1_deck.length == 0)
puts "Player 2 wins"
puts @compare_count
game_over = true
end
if(@p2_deck.length == 0)
puts "Player 1 wins"
game_over = true
puts @compare_count
end
end
end
#not truly functional, depends on the (class) globals @p1_deck,@p2_deck
def compare_stacks(p1_stack,p2_stack)
@log_file.puts "Stack1 length: " + p1_stack.length.to_s
@log_file.puts "Stack2 length: " + p2_stack.length.to_s
p1_rank = p1_stack.last % @rank
p2_rank = p2_stack.last % @rank
if(p1_rank > p2_rank)
@p1_deck.concat(p2_stack)
@p1_deck.concat(p1_stack)
elsif(p2_rank > p1_rank)
@p2_deck.concat(p1_stack)
@p2_deck.concat(p2_stack)
else
#WAR!
@log_file.puts "WAR"
if(@p1_deck.length < 4)
@log_file.puts "P1 stealing from P2"
to_steal = 4 - @p1_deck.length
@p1_deck.concat(@p2_deck.slice!(0,to_steal))
end
if(@p2_deck.length < 4)
@log_file.puts "P2 stealing from P1"
to_steal = 4 - @p2_deck.length
@p2_deck.concat(@p1_deck.slice!(0,to_steal))
end
p1_stack.concat(@p1_deck.slice!(0,4))
p2_stack.concat(@p2_deck.slice!(0,4))
compare_stacks(p1_stack,p2_stack)
end
end
end
wg = WarGame.new
wg.shuffle_up_and_deal
wg.play_the_game
wg.log_file.close
Version 2
result = rand(2)
puts "Player 1 wins" if(result == 0)
puts "Player 2 wins" if(result == 1)
To a computer, the only difference between the two is that the first one takes longer