Wednesday, February 22, 2006

TDDing a Card Game part 3

Last week I ended with the following questions
  • What happens when player makes an invalid move?
  • How does Player draw from its stock pile?
  • How does Player get notified of the end of game play?
  • Does player notify Game when its out of cards?
Today I'll pick start by picking a question and translating it into a test. I'll focus on the stock pile question.
#SpitPlayerRules
def test_player_draws_when_hand_is_short
  @stock = [2]
  player = Player.new(self)
  player.hand = [3, 4, 5, 6]
  @pile_one = [7]
  @pile_two = [7]
  player.play
  assert @stock.empty?, "player did not draw as expected"
end
Remember that SpitPlayerRules is using the self shunt pattern so it is a double (think stunt double) for the still theoretical Game object. I've primed the Player under test with a hand that is one card short. Also I've assumed that somehow that Player will be able to draw from the @stock that the test/Game double has. In our implementation we need to consider how the Game communicates to the Player that the stock pile is empty. I ran that and it failed predictably.
Here is the old Player#play
def play
  while(!@hand.empty?)
    @game.spit_on_one(@hand.pop)
  end
end
Should we draw before or after we spit? For now I'll say before, and implement the draw as quickly as I can
def play
  while(!@hand.empty?)
    if @hand.size < 5 and @game.draw?
      @hand << @game.draw 
    end
    @game.spit_on_one(@hand.pop)
  end
end
Before this can run we need to implement the two methods draw and draw? on our Game double.
#SpitPlayerRules
def draw
  @stock.pop
end
def draw?
  ! @stock.empty?
end
I'll just run the test and ... D'oh I need to make @stock an empty array in #test_run_without_draw to make it pass. Good that was fairly easy. Now on to the text test.
Wait! The TDD mantra is red (check), green (check), REFACTOR. Okay is there anything we can do to make the code simpler? What is Simple Code? There are several formulations of guidelines for simplicity in code here. The first guideline is "runs all tests", and we are good there. The next is "contains no duplication". While the Player class is acceptable. Its tests are not. The duplication is the set up the of the Player under test and the Game double's state. I'll factor that out into setup method.
#SpitPlayerRules 
def setup 
  @player = Player.new(self)
  #Game double's state
  @pile_one = [7]
  @pile_two = [7]
  @stock = []
end
def test_run_without_draw
  @player.hand = [ 2, 3, 4, 5, 6 ]
  @player.play
  assert_equal [7, 6, 5, 4, 3, 2] , @pile_one
end
def test_player_draws_when_hand_is_short
  @stock << 2
  @player.hand = [3, 4, 5, 6]
  @player.play
  assert @stock.empty?, "player did not draw as expected"
end
The third guideline is "expresses all the ideas you want to express". Looking at Player#play I think there are two ideas that are contained in that one method: draw and spit. I'll extract a method for each concept.
#Player
def play
  while(!@hand.empty?)
    draw
    spit
  end
end
def draw
  if @hand.size < 5 and @game.draw?
    @hand << @game.draw 
  end    
end
def spit
  @game.spit_on_one(@hand.pop)    
end
The final guideline "minimizes classes and methods". It makes me think twice about the decision to extract #spit. I'm a little uncertain if the #spit method is pulling its weight. On the one hand it is only one line long. On the other hand it makes explicit the action in the card game and provides a place for any more advanced decision making at that point in the game. I think I'll keep it for now.
I'm going to stop here because I'm trying to spend only an hour on this entry. I think we've made some progress here. The interaction between Player and Game is starting to show up in the SpitPlayerRules class. We covered some guidelines for simple code and applied them. And we answered one of the questions we asked last week. So until next time I'll leave you with a listing of the current state of things.
#--------------Spit.rb
class SpitPile
  def initialize( first_card )
    @pile = [ first_card ]
  end
  def play( card )
    if in_sequence?(card)
      @pile << card
      return true
    end
    false
  end
  def to_s 
    @pile.join(', ')
  end
  def top_card
    @pile.last
  end
  def in_sequence?( card )
    (card - top_card).abs == 1
  end
end

class Player
  attr_accessor :hand
  def initialize( game )
    @game = game
  end
  def play
    while(!@hand.empty?)
      draw
      spit
    end
  end
  def draw
    if @hand.size < 5 and @game.draw?
      @hand << @game.draw 
    end    
  end
  def spit
    @game.spit_on_one(@hand.pop)    
  end
end
#----------------SpitTest.rb
require 'test/unit'
require 'spit'

class SpitPileRules < Test::Unit::TestCase
  def setup
    @pile = SpitPile.new( 2 )
  end
  def test_accepts_card_of_next_higher_rank
    assert @pile.play(3)
    assert_equal "2, 3", @pile.to_s
  end
  def test_accepts_card_of_next_lower_rank
    assert @pile.play(1)
    assert_equal "2, 1", @pile.to_s
  end
  def test_rejects_card_of_same_rank
    assert !@pile.play(2)
    assert_equal "2", @pile.to_s
  end
  def test_rejects_cards_of_non_consecutive_rank
    assert @pile.play(3)
    assert !@pile.play(1)
    assert_equal "2, 3", @pile.to_s
  end
end

class SpitPlayerRules < Test::Unit::TestCase
  def setup 
    @player = Player.new(self)
    #Game double's state
    @pile_one = [7]
    @pile_two = [7]
    @stock = []
  end
  def test_run_without_draw
    @player.hand = [ 2, 3, 4, 5, 6 ]
    @player.play
    assert_equal [7, 6, 5, 4, 3, 2] , @pile_one
  end
  def test_player_draws_when_hand_is_short
    @stock << 2
    @player.hand = [3, 4, 5, 6]
    @player.play
    assert @stock.empty?, "player did not draw as expected"
  end

  # game methods
  def spit_on_one( card )
    @pile_one << card
  end
  def draw
    @stock.pop
  end
  def draw?
    ! @stock.empty?
  end 
end

Wednesday, February 15, 2006

TDDing a Card Game part 2

In the last entry I wrote a pretty simple class, SpitPile. It is responsible for enforcing the rules for playing a card in the card game Spit.
I received some feedback from Jim Hughes via the TDD yahoogroup. Jim kindly pointed out a corollary to the TDD rule "Only write code to pass a failing test". His corollary is "Always delete production code when it won't break a test". Then he suggested a place in the SpitPile where I could apply this corollary. In SpitPile#in_sequence? I have this
def in_sequence?(card) 
  (card != top_card) and ((card - top_card).abs == 1)
end
What I noticed, with Jim's guidance, is the logical duplication in that snippet. So I removed it and the tests still passed. Mmm, feel that green bar goodness.
Here is the code for SpitPile in its entirety
class SpitPile
  def initialize( first_card )
    @pile = [ first_card ]
  end
  def play( card )
    if in_sequence?(card)
      @pile << card
      return true
    end
    false
  end
  def to_s
    @pile.join(', ')
  end
  def top_card
    @pile.last
  end
  def in_sequence?( card )
    (card - top_card).abs == 1
  end
end
and the tests
class SpitPileRules < Test::Unit::TestCase
  def setup
    @pile = SpitPile.new( 2 )
  end
  def test_accepts_card_of_next_higher_rank
    assert @pile.play(3)
    assert_equal "2, 3", @pile.to_s
  end
  def test_accepts_card_of_next_lower_rank
    assert @pile.play(1)
    assert_equal "2, 1", @pile.to_s
  end
  def test_rejects_card_of_same_rank Okay I'm stuck. I can't c
    assert !@pile.play(2)
    assert_equal "2", @pile.to_s
  end
  def test_rejects_cards_of_non_consecutive_rank
    assert @pile.play(3)
    assert !@pile.play(1)
    assert_equal "2, 3", @pile.to_s
  end
end
Onward. I think I'm going to work on a Game object to manage the flow of pay. Spit is not turn based so I'm thinking I might have to do some threading magic. That is notoriously difficult to test, but I'm not sure I'll need it yet.
Start with a test. Okay, I'm stuck. I can't think of a simple test for Game without going immediately to a Player, but I did come up with this test for Player
class SpitPlayerRules < Test::Unit::TestCase
  def test_run_without_draw
    player = Player.new(self)
    player.hand = [ 2, 3, 4, 5, 6 ]
    @pile_one = [7]
    @pile_two = [7]
    player.play
    assert_equal [7, 6, 5, 4, 3, 2] , @pile_one
  end
end
This test assumes that player will modify SpitPlayerRules@pile_one which begs the question, how? This makes me think about the interaction between Player and Game. I don't have a Game object yet, but Ruby's 'duck' typing allows me to pass the test class to the Player and have it respond to the messages that Game should respond to. Incidentally this is known as the Self-Shunt pattern. I often start with this and later refactor to use a real or mock version of the object.
What should it respond to? It seems to me that Player only needs to be able to 'spit' on a pile to pass this test. First I'll, wait, always run the test first to watch it fail. Okay it threw a NameError which means I haven't defined a Player class yet. Next it'll need a constructor with one parameter, an accessor for @hand, and a method 'play'. With that in place
class Player
  attr_accessor :hand
  def initialize( game )
  end
  def play
  end
end
the test is now failing as expected.
I'm thinking the simplest thing that could possibly work is
def initialize( game )
  @game = game
end
def play
  while(!@hand.empty?)
    @game.spit_on_one(@hand.pop)
  end
end
I'm assuming that game has a method 'spit_on_one'that takes a card. I'll make the SpitPlayerRules class do that for now.
def spit_on_one( card )
  @pile_one << card
end
I'll run the tests. Green bar.
Now I can think of a few more tests that would help flesh out the interface between Game and Player.
  • What happens when player makes an invalid move?
  • How does Player draw from its stock pile?
  • How does Player get notified of the end of game play?
  • Does player notify Game when its out of cards?
All of these questions to answer and I'm out of time to write this. I'll ponder them. Maybe somebody like Jim will virtual pair with me again, and help me figure out where to go next. Until next time here is the code listing.
#--------------------------Spit.rb
class SpitPile
  def initialize( first_card )
    @pile = [ first_card ]
  end
  def play( card )
    if in_sequence?(card)
      @pile << card
      return true
    end
    false
  end
  def to_s
    @pile.join(', ')
  end
  def top_card
    @pile.last
  end
  def in_sequence?( card )
    (card - top_card).abs == 1
  end
end

class Player
  attr_accessor :hand
  def initialize( game )
    @game = game
  end
  def play
    while(!@hand.empty?)
      @game.spit_on_one(@hand.pop)
    end
  end
end
#-----------------SpitTest.rb
require 'test/unit'
require 'spit'

class SpitPileRules < Test::Unit::TestCase
  def setup
    @pile = SpitPile.new( 2 )
  end
  def test_accepts_card_of_next_higher_rank
    assert @pile.play(3)
    assert_equal "2, 3", @pile.to_s
  end
  def test_accepts_card_of_next_lower_rank
    assert @pile.play(1)
    assert_equal "2, 1", @pile.to_s
  end
  def test_rejects_card_of_same_rank
    assert !@pile.play(2)
    assert_equal "2", @pile.to_s
  end
  def test_rejects_cards_of_non_consecutive_rank
    assert @pile.play(3)
    assert !@pile.play(1)
    assert_equal "2, 3", @pile.to_s
  end
end

class SpitPlayerRules < Test::Unit::TestCase
  def test_run_without_draw
    player = Player.new(self)
    player.hand = [ 2, 3, 4, 5, 6 ]
    @pile_one = [7]
    @pile_two = [7]
    player.play
    assert_equal [7, 6, 5, 4, 3, 2] , @pile_one
  end

  # game methods
  def spit_on_one( card )
    @pile_one << card
  end
end

Wednesday, February 08, 2006

TDDing a Card Game part 1

In my previous post I committed to writing an entry a week in this blog. Coming up with content is hard! Of course since I've called this my Software Engineering blog I've narrowed the topic down somewhat.
One important technique in any programmer's toolkit is Test Driven Development. Contrary to the name, TDD is a Design technique. We write a test to specify and constrain the problem at hand. Then after watching it fail, we write the simplest code that will cause the test to pass. Finally, and most importantly we refactor to eliminate any duplication that we created in the previous step. We do this loop several times and soon we've designed and coded a piece of software. For the next few entries I'm going to use TDD to create a card game.
Spit, also called Speed, is a card game I used to play when I was younger, though I always played the five card hand variant. I'm going to focus the five card hand variant because it's what I know. Others have programmed versions of the game before. I never have though, so this ought to be fun.
The layout is as follows:
 stockpile ------> 15 cards

10 cards    1 card          1 card    10 cards <---- spit piles and spit reserves

 stockpile ------> 15 cards
The object of the game is to place all of your cards from your stockpile into the spit piles before the other player. This is not a turn based game. You may only have five cards in your hand at time. Cards of the next higher or lower rank may be played from your hand to either spit pile. If both players are stuck you simultaneously flip the top card from the spit reserves on to the spit pile.
As this post is already pretty long we'll just tackle the rules for playing a card on the spit pile. My language of choice at the moment is ruby. Ruby's Test::Unit is the ruby member of the xUnit family.
We start with a test.
require 'test/unit'

class SpitPileRules < Test::Unit::TestCase
  def test_accepts_card_of_next_higher_rank
    @pile = SpitPile.new( 2 )
    assert @pile.play(3)
    assert_equal "2, 3", @pile.to_s
  end
end
I've named this test case SpitPileRules because it is specifying the rules of play for the spit pile. The first test shows we can play a card of higher rank. At the moment cards are represented as numbers because in Spit we don't care about the suite. We're checking the contents by looking at the string representation of the pile. Also, we expect the SpitPile#play to return a boolean showing success. The next step is to pass this test as quickly as possible, wait first we run it to watch it fail. Okay, now we can pass it.
class SpitPile
  def initialize( first_card )
  end
  def play ( card )
    true
  end
  def to_s 
   "2, 3"
  end
end
That passes. Now we refactor to remove duplication. What duplication, you might ask. Well, the string in SpitPile#to_s duplicates the expected value in SpitPileRules#test_accepts_card_of_next_higher_rank. So I'll replace it with a member variable, and make sure the variable is populated correctly.
class SpitPile
  def initialize( first_card )
    @pile = [ first_card ]
  end
  def play ( card )
    @pile << card
    true
  end
  def to_s 
    @pile.join(', ')
  end
end
Run the tests, and it still passes. What happens when we add a card of lower rank? Let's write the test.
def test_accepts_card_of_next_lower_rank
  @pile = SpitPile.new( 2 )
  assert @pile.play(1)
  assert_equal "2, 1", @pile.to_s
end
Okay, let's make that pass. Wait, first I'll run the test, and it passes. That's fine, that happens very rarely to me, but as SpitPile stands it passes this new test. On to the next test. We could either check if it rejects cards of non consecutive rank or if it rejects cards that are of the same rank. I think I'll do the same rank first.
def test_rejects_card_of_same_rank
  @pile = SpitPile.new( 2 )
  assert !@pile.play(2)
  assert_equal "2", @pile.to_s
end
Run that, and it fails. Good. Now we make it pass.
def play ( card )
  if card != @pile.first
    @pile << card
    return true
  end
  false
end
Passed. Okay, now we have an opportunity to refactor. This time we see duplication in the test specifically all three tests have:
@pile = SpitPile.new( 2 )
Let's factor that out into a setup method.
class SpitPileRules < Test::Unit::TestCase
  def setup
    @pile = SpitPile.new( 2 )
  end
  def test_accepts_card_of_next_higher_rank
    assert @pile.play(3)
    assert_equal "2, 3", @pile.to_s
  end
  def test_accepts_card_of_next_lower_rank
    assert @pile.play(1)
    assert_equal "2, 1", @pile.to_s
  end
  def test_rejects_card_of_same_rank
    assert !@pile.play(2)
    assert_equal "2", @pile.to_s
  end
end
After running tests I'm sure it's still passing so I will move on to non consecutive cards.
def test_rejects_cards_of_non_consecutive_rank
  assert @pile.play(3)
  assert !@pile.play(5)
  assert_equal "2, 3", @pile.to_s
end
It fails. Now we can make it pass.
def play ( card )
  if (card != @pile.first) and ((card - @pile.first).abs == 1)
    @pile << card
    return true
  end
  false
end
That passes, but I just noticed that it might not pass if I change that case like this:
def test_rejects_cards_of_non_consecutive_rank
  assert @pile.play(3)
  assert !@pile.play(1)
  assert_equal "2, 3", @pile.to_s
end
It doesn't. The reason is I'm adding card to the end of the pile with <<, but I'm checking against
@pile.first
so I'll change that to
 @pile.last
and we're green. Now it's time to refactor. The obvious duplication of @pile.last can be factored out to something more meaningful.
def play( card )
  if (card != top_card) and ((card - top_card).abs == 1)
    @pile << card
    return true
  end
  false
end
def top_card
  @pile.last
end
Tests are still green. Next we'll clarify what those conditions mean.
def play( card )
  if in_sequence?(card)
    @pile << card
    return true
  end
  false
end
def in_sequence?(card)
  (card != top_card) and ((card - top_card).abs == 1)
end
Okay. I've spent about two hours on writing this article including about 15 minutes of coding. I see a few directions I can go from here. I think SpitPile#in_sequence? really doesn't belong to SpitPile. It seems like it should be on Card, or Deck or something like that. Also, I'm not entirely comfortable with to_s. I think it may be tying the rules of a SpitPile to closely too a UI, but it will do for now. Now that we have SpitPile should we create a Player class that can interact with SpitPile, or a Game class that controls the flow of the game around SpitPile? Do we make some of the other piles as independent classes? I'll decide soon and work on it in the next entry.
Thanks for following along.

Wednesday, February 01, 2006

Why I'm Starting to Blog

The other day I purchased My Job Went To India (And All I Got Was This Lousy Book) 52 Ways To Save Your Job by Chad Fowler. It's a great book on how to architect your career for software engineers. Each chapter ends with an Act On It section that encourages you to apply the advice provided. My shiny new blog is a result of an Act On It.
Chapter 12 is about finding a mentor, and the Act On It section encourages you to Mentor Yourself by choosing someone you admire as a role model, list and rank their most important attributes, and rate yourself as they would rate you on those attributes. Then you can determine what you should work on.
I did this activity, and determined the attribute of my chosen mentor that I most need to work on is a prolific writing habit. This includes blogging, web articles, contributions to mailing lists, and books. I've resolved to do the following:
  • Get a blog <Check />
  • Post at least once a week to said blog
  • Respond relevantly to at least one mailing list post per day
  • Take the next opportunity to write a technical article
So there you have it, why I'm starting to blog in a nutshell. Many thanks to Chad for the book that will change my career.