Showing posts with label agile. Show all posts
Showing posts with label agile. Show all posts

Sunday, 18 May 2008

Garden Race Pt 2: Adding multiple players

This post is part of a series on the (very) basics of iterative development using the example of a simple Snakes and Ladders-like game. Links to each post in the series will be added to the index page.

Um, ok, so the customers (firstborn and I) weren't overly impressed with the demo from part 1. It did help illustrate the most pressing deficiencies though:

  • Doesn't support multiple players
  • Doesn't have anything even remotely resembling snakes or ladders, let alone fairies
  • No gui

Our story list currently looks like this:

  1. A player can roll the die, and then move that many spaces along the board.
  2. A player that ends his or her turn on a "feature square" (a square containing a creature or obstacle), will be moved to the square connected with that feature.
  3. There can be 1-4 players, and each player has their turn in sequence.
  4. A player that reaches the final square wins the game.

There are no GUI stories currently defined, but we'll need something resembling a GUI eventually. I am tempted to start working on some GUI stories, because I don't want to get too far through the code and then find that it won't play nice with a graphical interface. It is really the main point of this software after all. On the other hand, the Game class is still very basic, so maybe it would be a good idea to knock over one of the original stories on our list. Story 2 seems like it could tie in with the GUI pretty strongly -- the view will have to show the player's move, then perform some kind of animation in the event that the player lands on a feature square. How about Story 3? Pretty basic, and essential to the game. At least that way firstborn and I can race each other to the end of the command line demo :)

I'll just check with the customers... be right back.

Ok, customer is asleep, so I'll take that as "that's fine Dad" :)

Quick aside, normally we would estimate "points" or some other unit of how much effort each story would take, and how many units could be done in an iteration, then have the customer prioritise the stories for this iteration.

Our test list

What tests could we write for multiple players?

  1. Should be able to set the number of players for a new game
  2. For a new, 2 player game, both player's should be off-the-board (square 0)
  3. After first player's roll, current player should be player 2
  4. After first player's roll, current player's position should be off-the-board (square 0)
  5. First player rolls a 3, second player rolls a 2, then current player should be player 1 on square 3.

Frankly, I don't really like how these look. Implementation details keep coming to mind, and I want to ignore them and focus on what I want to achieve (not how I want to achieve it). Let's start with these anyway and we'll see how it goes.

Starting iteration 2

Running all our tests shows we are all green, and all good to go. The first test on the list looks easy -- set the number of players in the game. Let's do that one.

//From GameSpec.cs
[Fact]
public void Should_be_able_to_create_2_player_game() {
 var twoPlayerGame = new Game(10, 2);
 Assert.Equal(2, twoPlayerGame.NumberOfPlayers);
}
//From Game.cs
public int NumberOfPlayers { get { return numberOfPlayers; } }
public Game(int boardSize) {
 this.boardSize = boardSize;
}
public Game(int boardSize, int numberOfPlayers) {
 this.boardSize = boardSize;
 this.numberOfPlayers = numberOfPlayers;
}

What about the previous constructor that just takes the boardSize? Well that should probably just start a new one player game I guess. Let's write a test for how we think it should work.

[Fact]
public void New_game_should_have_1_player_by_default() {
 var onePlayerGame = new Game(10);
 Assert.Equal(1, onePlayerGame.NumberOfPlayers);
}

This fails because the number of players initialises to zero.

public Game(int boardSize) {
 this.boardSize = boardSize;
 this.numberOfPlayers = 1;
}

Fixed. Now let's look at test 2, checking the position of each player for a new game.

//From GameSpec.cs:
[Fact]
public void New_game_should_start_all_players_off_the_board() {
 var newThreePlayerGame = new Game(10, 3);
 var players = new[] {1, 2, 3};
 foreach (var player in players) {
  Assert.Equal(0, newThreePlayerGame.GetSquareFor(player));   
 }            
}

//From Game.cs:
public int GetSquareFor(int player) {
 return 0;
}

An obviously deficient implementation like this GetSquareFor() method suggests we need to writes some more tests to flesh out a better one.

[Fact]
public void Positions_should_be_correct_after_first_two_players_roll() {
 var threePlayerGame = new Game(10, 3);
 const int firstRoll = 3;
 const int secondRoll = 5;
 
 threePlayerGame.Roll(firstRoll);
 threePlayerGame.Roll(secondRoll);
 
 Assert.Equal(firstRoll, threePlayerGame.GetSquareFor(1));
 Assert.Equal(secondRoll, threePlayerGame.GetSquareFor(2));
 Assert.Equal(0, threePlayerGame.GetSquareFor(3));
}

Now we are potentially looking at a bigger step. We need Roll() to affect only the position of the current player. We don't have the concept of a current player. We'll also probably need an array or similar structure to store each player's position. Roll() will then store update the position of the current player, and change the current player to the next player. The CurrentSquare implementation will probably need to change to refer to the current player too. And then we'll have to add code to change the position in the event the player lands on a feature square! Argh!

Stop worrying! Try baby steps...

Let's back up a bit. I'm fairly confident we can write up the code above, but it will only be covered by one test and we are touching a lot of the Game class without direct guidance from the tests. We still have this test on our test list: "After first player's roll, current player should be player 2". This deals with the concept of the current player without requiring addition position arrays. It should only affect the Roll() implementation. Let's skip our last test by updating the attribute to [Fact(Skip="Too big a step for now")] (I could delete the test and rewrite it if we need it, but it did illustrate our need to deal with the current player concept, so I'll leave it for now). I started off coding our new test with two separate assertions:

var newTwoPlayerGame = new Game(10, 2);
Assert.Equal(1, newTwoPlayerGame.CurrentPlayer);
newTwoPlayerGame.Roll(2);
Assert.Equal(2, newTwoPlayerGame.CurrentPlayer);

The split asserts are ugly, and we can split this into two more specific tests. Here's the passing code, which was written one step at a time (not shown is chaining the Game(int) constructor to Game(int, int), so everything gets initialised properly in either case. Check the download at the end for the finished code):

//From GameSpec.cs:
[Fact]
public void Current_player_for_new_game_should_be_player_1() {
 var newTwoPlayerGame = new Game(10, 2);
 Assert.Equal(1, newTwoPlayerGame.CurrentPlayer);
}
[Fact]
public void After_first_players_roll_it_should_be_the_second_players_turn() {
 var newTwoPlayerGame = new Game(10, 2);
 newTwoPlayerGame.Roll(2);
 Assert.Equal(2, newTwoPlayerGame.CurrentPlayer);
}
//From Game.cs:
public int CurrentPlayer { get; private set; }
public Game(int boardSize, int numberOfPlayers) {
 this.boardSize = boardSize;
 this.numberOfPlayers = numberOfPlayers;
 this.playerPositions = new int[numberOfPlayers];
 this.CurrentPlayer = 1;
}
public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 CurrentPlayer++;
}

Before we go back to the test we skipped, I'd like to flesh out more of the CurrentPlayer property. Fifth test on our list was "First player rolls a 3, second player rolls a 2, then current player should be player 1 on square 3". Let's do a simpler version and just verify that this scenario ends up with the correct CurrentPlayer.

//In GameSpec.cs:
[Fact]
public void After_all_players_have_had_a_turn_it_should_be_first_players_turn_again() {
 var newThreePlayerGame = new Game(10, 3);
 newThreePlayerGame.Roll(1);
 newThreePlayerGame.Roll(1);
 newThreePlayerGame.Roll(1);
 Assert.Equal(1, newThreePlayerGame.CurrentPlayer);
}
//In Game.cs:
public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 CurrentPlayer++;
 if (CurrentPlayer > NumberOfPlayers) CurrentPlayer = 1;
}

A quick refactor from the green bar

We now have a green bar (well, yellow if you count the skipped test I guess). Time to take a look for potential refactoring opportunities. I've been a bit slack about this up to now as I haven't noticed anything obvious while coding and haven't explicitly stopped to think about refactoring. I have a bad habit of doing this -- I do design work while writing the code to pass the test, rather than deferring it to the refactoring stage. This can lead me to generalising to early or changing the design without getting clear direction from the tests and passing implementation. Note to self: premature generalisation is one of the many roots of all evil :)

In this case we've done the Right ThingTM and written simple code to pass the test, then looked at refactoring based on what the current implementation needs, rather than what we think it will need. The code within Roll() contains the logic for selecting the next player as well as for updating the current square. Let's Extract Method to make this more obvious.

public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 nextPlayer();
}
private void nextPlayer() {
 CurrentPlayer++;
 if (CurrentPlayer > NumberOfPlayers) CurrentPlayer = 1;
}

This is purely a matter of taste. I find it reflects the intention more. If you don't, then leave it un-refactored. :-) Either way, key lesson here (for me, you probably know it already :)) is to defer design stuff until the production code shows a clear need, or until a test is too hard to write. Either way, design from the green bar whenever possible.

Time to face the music...

We probably shouldn't put it off any longer. Let's re-enable the test we skipped earlier:

//In GameSpec.cs:
[Fact]
public void Positions_should_be_correct_after_first_two_players_roll() {
 var threePlayerGame = new Game(10, 3);
 const int firstRoll = 3;
 const int secondRoll = 5;
 
 threePlayerGame.Roll(firstRoll);
 threePlayerGame.Roll(secondRoll);
 
 Assert.Equal(firstRoll, threePlayerGame.GetSquareFor(1));
 Assert.Equal(secondRoll, threePlayerGame.GetSquareFor(2));
 Assert.Equal(0, threePlayerGame.GetSquareFor(3));
}

This give the following assertion failure:

TestCase 'DaveSquared.GardenRace.Tests.GameSpec.Positions_should_be_correct_after_first_two_players_roll'
failed: Assert.Equal() Failure
Expected: 3
Actual:   0

This is failing because our implementation for GetSquareFor(...) stinks -- it's just returning 0. My fault, not yours. Let's get back to the green bar as soon as possible, then we'll worry about getting the design right. My original guess for passing this test was that we would need an array of player positions, and Roll() would just update the position for the current player. We have a current player concept in the code now, so let's chuck in an array and see how it goes:

//Bits and pieces from Game.cs:
public class Game {
    //...
 private readonly int[] playerPositions;
 //...
 public Game(int boardSize, int numberOfPlayers) {
  this.boardSize = boardSize;
  this.numberOfPlayers = numberOfPlayers;
  this.playerPositions = new int[numberOfPlayers];
  this.CurrentPlayer = 1;
 }
 public void Roll(int dieValue) {
  CurrentSquare += dieValue;
  playerPositions[CurrentPlayer - 1] = CurrentSquare;
  nextPlayer();
 }
 //...
 public int GetSquareFor(int player) {
  return playerPositions[player - 1];
 }
}

Running this fails with a new message:

TestCase 'DaveSquared.GardenRace.Tests.GameSpec.Positions_should_be_correct_after_first_two_players_roll'
failed: Assert.Equal() Failure
Expected: 5
Actual:   8

It is failing on our second assertion, Assert.Equal(secondRoll, threePlayerGame.GetSquareFor(2));. This gives us a good hint as to what's happening -- our first and second rolls of 3 and 5 are both being added to the same array index. Hold on, that's only half the story. Let's have a closer look at this:

public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 playerPositions[CurrentPlayer - 1] = CurrentSquare;
 nextPlayer();
}

Brilliant Dave. What a fantastic coder I am :) I'm still adding all rolls to CurrentSquare, then assigning that to the current player position. In this case, CurrentSquare is 3+5=8 which fails our test. Why didn't you point this out? Luckily I had my tests to do it in your absence :) We'll fix it right now:

public void Roll(int dieValue) {
 CurrentSquare += dieValue;
 playerPositions[CurrentPlayer - 1] += dieValue;
 nextPlayer();
}

Tests pass. My own ineptitude aside, this test that was initially giving us troubles has been trivial to solve. We now have a green bar, so let's refactor. First obvious bit of duplication is the two square increments in the Roll() method. Let's update CurrentSquare to use our GetSquareFor(int player) method:

public int CurrentSquare { 
 get { return GetSquareFor(CurrentPlayer); } 
}
//...
public void Roll(int dieValue) {
 playerPositions[CurrentPlayer - 1] += dieValue;
 nextPlayer();
}

CurrentSquare is no longer a getter/setter, but is a convenient shorthand for GetSquareFor(int player). Our Roll() method is fairly clear. We could make it a little bit clearer by extracting a moveCurrentPlayer(int squares) method, but would also mean more indirection. I'll make you a deal, if we end up with a few playerPositions[CurrentPlayer - 1] style array indexes (the -1 is pretty ugly), then we do something about it. For now it is probably ok.

Looking at Game from the perspective of the S.O.L.I.D. principles, I think the main one we have to be wary of is violating the Single Responsibility Principle (SRP). Current Game is responsible for:

  • Number of squares on the board
  • Number of players
  • Keeping track of whose turn it is
  • Keeping track of each player's position
  • Knowing when the game has finished

Are these cohesive enough to count under the banner of one responsibility? I'm not sure, but I can't currently think of a better abstraction (feel free to leave comments :-)). For now let's stay conscious of this and we'll revisit it if it starts becoming a problem.

We're done? No we're not!

So are we done for our multiple players story? After running a larger test it appears so. However, after updating the demo we see some strange behaviour. An extract of from the demo code and the output are shown below:

//From main() in Program.cs:
const int numberOfSquares = 20;
const int numberOfPlayers = 2;
//...
Console.WriteLine("Creating a new game with " + numberOfSquares + " squares and " + numberOfPlayers + " players.");
var game = new Game(numberOfSquares, numberOfPlayers);
Console.WriteLine("Press any key to roll the die.");
while (!game.IsFinished) {
 Console.ReadKey(interceptKey);
 var dieRoll = GetDieValue();
 game.Roll(dieRoll);
 var currentState = (game.IsFinished) ? "You won the game!" : "Now on square " + game.CurrentSquare;
 Console.WriteLine("Player " + game.CurrentPlayer + " rolled a " + dieRoll + ". " + currentState);
}
//...
Creating a new game with 20 squares and 2 players.
Press any key to roll the die.
Player 2 rolled a 1. Now on square 0
Player 1 rolled a 3. Now on square 1
Player 2 rolled a 2. Now on square 3
...snip...
Player 2 rolled a 2. Now on square 18
Player 1 rolled a 2. Now on square 14
Player 2 rolled a 3. You won the game!

Why does player 2 have the first go? And why do they roll a 1 and yet are on square 0? The reason is highlighted in the code snippet above -- the Roll() method updates the Game so that it is the next player's turn. When the demo gets to writing out the current player's position and state, it is actually writing the details for the next player who is about to have their turn.

I believe this is a form of temporal coupling, or coupling in time. The CurrentSquare and CurrentPlayer methods depend on whether they are called before or after Roll. There is almost certainly going to be a problem with IsFinished as well, as that depends on the CurrentPlayer too.

I've been worried about this for a little while actually, especially when thinking about how the GUI will probably want to animate the initial move, then an additional move up or down the snake/ladder/critter type thing. It looks like we'll either have to trigger events from Roll, or break it up and have some kind of coordinator object.

I am quietly confident that we can change our design to support this when we get some tests around the "feature square" or GUI stories, and that these tests will drive us toward a decent design and implementation. Again, let's stay conscious of this coupling and work around it in our demo code. Remember that our demo code is not production code, so we are being a little less precious with it. Next iteration we'll make sure we write tests to cover this and drive a nicer design.

Here's the new main loop in the demo code:

while (!game.IsFinished) {
 var currentPlayer = game.CurrentPlayer;
 Console.ReadKey(interceptKey);
 var dieRoll = GetDieValue();
 game.Roll(dieRoll);
 var currentState = (game.IsFinished) ? "You won the game!" : "Now on square " + game.GetSquareFor(currentPlayer);
 Console.WriteLine("Player " + currentPlayer + " rolled a " + dieRoll + ". " + currentState);
}
Creating a new game with 20 squares and 2 players.
Press any key to roll the die.
Player 1 rolled a 5. Now on square 5
Player 2 rolled a 3. Now on square 3
(...snip...)
Player 2 rolled a 1. Now on square 10
Player 1 rolled a 5. Now on square 23
Player 2 rolled a 5. You won the game!

As predicted, our IsFinished property is having problems because of the coupling issue. We'll fix this, then we're done.

Test-first debugging

Let's write a test to expose the bug with the IsFinished implementation:

[Fact]
public void Game_should_finish_as_soon_as_any_player_reaches_the_end() {
 var threePlayerGame = new Game(5, 3);
 threePlayerGame.Roll(1);
 threePlayerGame.Roll(6);
 Assert.True(threePlayerGame.IsFinished);
}

This fails. At the time the assertion is evaluated player 2 has finished, but the current player is player 3, who is yet to finish. This should be straight-forward to pass from here:

public bool IsFinished {
 get {
   return playerPositions.Any(square => square >= boardSize);
 }
}

All the tests now pass. We've now removed the temporal coupling between Roll and IsFinished, and our demo now works as expected:

Creating a new game with 20 squares and 2 players.
Press any key to roll the die.
Player 1 rolled a 1. Now on square 1
Player 2 rolled a 1. Now on square 1
(...snip...)
Player 1 rolled a 2. Now on square 17
Player 2 rolled a 2. Now on square 15
Player 1 rolled a 3. You won the game!

Press a key to exit.

That's a wrap!

Not sure if this looks like a lot of work to you, but it actually represents only a couple of minutes of coding time, and a couple of minutes of thinking time (plus lots of time to write it all down in excruciating detail :-)). Point is that each iteration so far has been really quick despite (or because of?) involving lots of small steps.

You can browse or checkout the final code for this iteration from here. Thanks for making it this far! :)

Friday, 16 May 2008

Garden Race Pt 1: Snakes, ladders and iterations

This post is part of a series on the (very) basics of iterative development using the example of a simple Snakes and Ladders-like game. Links to each post in the series will be added to the index page.

For a while now I've wanted to write a Snakes and Ladders-style game for my daughter, both to be a nice nerdy Dad, and to practice some dev stuff. I was going to create "Warps and Wormholes", but firstborn was adamant she wanted fairies involved. And so I've ended up with "Garden Race", which is going to be a race for players to make it to one end of the garden aided by fairies, butterflies and other girlie stuff, while hindered by lizards, hoses, and other less-girlie stuff.

I intend to use iterative development for this project -- I'll start off with a thin slice of functionality and keep adding to it to build up a semi-usable game. This should accomplish two things: first, showcase my own ineptitude, and second, to help improve my TDD and development skills. While I'm not exactly sure how far I'll get with this project (free time is short at present), I can commit to being fairly honest during this process. If and when I stuff up, I'll write it down (I might excuse myself a quick spike here and there so the posts don't get bogged down to much, but the stuff ups will stay).

Note: MS has a free game design version of Visual Studio that might be useful if your aim is to make a good game for your kids (instead of just playing with dev stuff). There are also a few open source frameworks out there (like Childsplay).

For my starting point, I began with a rough solution structure (main Game project and a test project), chucked it in a local SVN repo, and setup some of the tools I'd need. I'm trying using a /src based configuration just for kicks, and might try nant just for something different. I am also trying XUnit.Net, instead of my usual NUnit test framework.

Requirements

Our customers, i.e. my firstborn (after some prompting) and I, want a Snakes and Ladders-type game involving fairies and other garden-dwelling folk. Here is the basic statement of what we want:

"Garden Race is a computer-based board game. Players take it in turns to roll a die, and then move the corresponding number of squares on the board. If the player lands on a square featuring a garden creature of obstacle at the end of their turn, then that creature or obstacle will move the player to a connected square on the board. The first player to reach the bottom of the garden (the end of the board) wins. We want fancy 3D graphics, surround sound, and it has to be ready yesterday."

Ever noticed how hard it is to succinctly describe a simple concept, even something as simple as this? Let's try and remove some ambiguity by boiling things down into user stories.

  1. A player can roll the die, and then move that many spaces along the board.
  2. A player that ends his or her turn on a "feature square" (a square containing a creature or obstacle), will be moved to the square connected with that feature.
  3. There can be 1-4 players, and each player has their turn in sequence.
  4. A player that reaches the final square wins the game.

That should probably be enough to get us going.

Planning and design for this iteration

In the spirit of iterative design we are going to first try to deliver a slice of functionality. As we are currently unsure of exactly how to present the customer with fancy 3D graphics and sound (at this stage the dev team (me) would like to try WPF or QT4, but we don't know all the requirements yet), let's focus on the core game functionality, which is pretty much captured by the user stories above.

For our first iteration we've agreed with the customer to deliver stories 1 and 4, which is basically a single player rolling a die and moving to the end of the board. Exciting game huh? But it should reveal some basics of how the game mechanics work, so it seems a safe place to start.

If we are picking nouns for potential classes in our game we might come up with Player, Die, Square, and Board, but we're definitely not going to use nouns as a basis for our design. A nice place to start might just be a Game or GameEngine class. What tests could we come up with for our first story?

  • A player should not be on the board until the roll the die and move.
  • A player that rolls a 4 should then be on the 4th square from the start.
  • A player that rolls a 2, and then a 6, should be on the 8th square from the start.
  • The game should finish once the player reaches the end.

We currently don't have enough information to implement all of this. How many squares are there on the board? What is the maximum value of the die used? What happens if the player is one square from the end, and rolls a 3? The first couple of points we will check with the customer, but it doesn't really affect our design. Let's test for the last case though, and we'll check with the customer if our assumption is correct:

  • A player that is one square from the end, and rolls a 3, should win the game.

Before we jump into this iteration, I'd be really interested to hear any up-front design ideas you have for this. If you were going to draw a UML class diagram or write up some CRC cards, what classes, data and relationships would you have? I've got my own ideas, but I don't want to influence my iterative design process too much at this point, nor do I want to deliberately come up with a shoddy upfront design and then marvel at how well my iterative design (hopefully) turns out. Please feel free to leave your design ideas in the comments or email me.

Right, let's start.

First tests

Let's look at the second test on our test list. It looks like an easy thing to do and should help us learn a little about the problem domain.

public class GameSpec {
    [Fact]
    public void Player_should_be_on_fourth_square_after_rolling_a_four() {
        var game = new Game();
        game.Roll(4);
        Assert.Equal(4, game.CurrentSquare);
    }
}

Not going to keep much of this, I'm pretty sure of that. But we need to start somewhere. By the way, if you aren't familiar with XUnit.Net, [Fact] == [Test]. Now let's pass the test:

public class Game {
    public int CurrentSquare;

    public void Roll(int dieValue) {
        CurrentSquare += dieValue;
    }
}

Now let's test two rolls, which is the third test on our list.

[Fact]
public void Player_should_be_on_eight_square_after_rolling_a_two_then_a_six() {
 var game = new Game();
 game.Roll(2);
 game.Roll(6);
 Assert.Equal(8, game.CurrentSquare);
}

This works without change, as we jumped straight to an obvious implementation for Roll(). We could have taken smaller steps and initially used CurrentSquare = dieValue; to pass the first test, the updated it to pass the second test, but we don't need to do that unless we aren't really sure how to proceed.

Let's add a third test to make sure the player starts off the board, or square 0.

var game = new Game();
Assert.Equal(0, game.CurrentSquare);

Again, this passes without modification. We also have a test on our list about finishing the game. We don't have too much information about how this works, so let's look at what we can test.

[Fact]
public void Game_should_finish_when_player_reaches_end_of_board() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 game.Roll(boardSize);
 Assert.Equal(10, game.CurrentSquare);
 Assert.True(game.IsFinished);
}

This introduces two new concepts: board size, and the game state as finished or not finished. To get this to compile we need to add non-default constructor to Game, which means we also need to explicitly add a default constructor if we want to stop our other tests from breaking. We also need to add an IsFinished property to Game. The following implementation compiles and passes all the tests.

public class Game {        
 public int CurrentSquare;
 public Game(int boardSize) {}
 public Game() {}
 public bool IsFinished {
  get { return true; }
 }
 public void Roll(int dieValue) {
  CurrentSquare += dieValue;
 }
}

The IsFinished implementation obviously stinks, so let's also add a test around unfinished games.

[Fact]
public void New_game_should_be_unfinished() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 Assert.False(game.IsFinished);
}

I first did a trivial implementation:

public bool IsFinished {
 get { return CurrentSquare == 0; }
}

Which failed both my IsFinished tests. Oops, that should be CurrentSquare != 0. Tests now pass, but the implementation still stinks. Let's try this one:

[Fact]
public void In_progress_game_should_be_unfinished() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 game.Roll(5);
 Assert.False(game.IsFinished);
}

Which we can pass with this:

public class Game {
 private readonly int boardSize;  
 public Game(int boardSize) {
  this.boardSize = boardSize;
 }
 public bool IsFinished {
  get { return CurrentSquare >= boardSize; }
 }
 //...[snip]...

The last test on our list so far is to see what happens when we overrun the last square on the board.

[Fact]
public void Game_should_still_finish_when_player_overruns_last_square() {
 const int boardSize = 10;
 var game = new Game(boardSize);
 game.Roll(boardSize + 2);
 Assert.True(game.IsFinished);
}

This passes because we used >= for the CurrentSquare/boardSize comparison. One thing I haven't been doing is the refactor step of the TDD red-green-refactor process. Let's look at Game:

public class Game {
 private readonly int boardSize;
 public int CurrentSquare;
 public Game(int boardSize) {
  this.boardSize = boardSize;
 }
 public Game() {}
 public bool IsFinished {
  get { return CurrentSquare >= boardSize; }
 }
 public void Roll(int dieValue) {
  CurrentSquare += dieValue;
 }
}

Not much to refactor there, right? The tests have some duplication in the Game setup though. And the default constructor of Game looks fairly useless. Normally I would just whack that into a [SetUp] method, but XUnit.Net discourages this as it can make for non-obvious test contexts. My GameSpec tests currently want to execute in the one test context -- a new Game with 10 squares. It seems reasonable that my GameSpec HAS-A context, so let's add a game as a field with a descriptive name. I can then replace the game initialisation in each test with a simple reference to newTenSquareGame, and get rid of Game's default constructor.

public class GameSpec {
 private readonly Game newTenSquareGame = new Game(10);
 //...[snip]...
 [Fact]
 public void Game_should_finish_when_player_reaches_end_of_board() {
  newTenSquareGame.Roll(10);
  Assert.Equal(10, newTenSquareGame.CurrentSquare);
  Assert.True(newTenSquareGame.IsFinished);
 }
 //...[snip]...

If we start having lots of different contexts or complicated contexts we may want to revisit this, but the whole test fixture reads fairly well for now. Full disclosure: before this approach I mucked around with some crazy test structure ideas I have regarding tests and test contexts. I'm omitting that from this post as it was mainly for personal interest rather that something I would normally do.

So what's next? Our test list is empty, and we seem to have completed most of our first iteration, stories 1 and 4. If you remember, these basically covered moving a single player around the board, and being able to finish the game. This isn't really something we can show our customer though. You don't go showing a youngin' a bunch of unit tests when they are expecting a fairyised version of Snakes and Ladders. And we haven't even dealt with the concept of "rolling a die", we have just assumed a value. But we seem to have the basic functionality we promised for this iteration.

Customer demo

To finish this iteration, let's write a console app that will allow our customer to run through the current game logic. Here's what I'm thinking of:

  Creating new game with 20 squares.
  Press a key to roll the dice.
  You rolled a 4. Now on square 4.
  You rolled a 6. Now on square 10.
  ...
  You rolled a 2. You won the game!

At first I was thinking about creating a GameController class and writing some tests around that, then calling that from a simple console app. But we don't need that yet. So let's just do the simplest thing that will work for our demo. And because it is slapped together and not designed to specific requirements, let's just promise not to use any of this code in the actual product.

namespace DaveSquared.GardenRace.ConsoleFrontEnd {
    class Program {
        private static readonly Random random = new Random();
                
        static void Main(string[] args) {
            Console.WriteLine("Welcome to Garden Race! It's like Snakes and Ladders, only without the copyright violation!");
            Console.WriteLine();
            
            const int numberOfSquares = 20;
            const bool interceptKey = true;
            
            Console.WriteLine("Creating a new game with " + numberOfSquares + " squares.");
            var game = new Game(numberOfSquares);
            Console.WriteLine("Press any key to roll the die.");
            while (!game.IsFinished) {                
                Console.ReadKey(interceptKey);
                var dieRoll = GetDieValue();
                game.Roll(dieRoll);
                var currentState = (game.IsFinished) ? "You won the game!" : "Now on square " + game.CurrentSquare;
                Console.WriteLine("You rolled a " + dieRoll + ". " + currentState);
            }
            Console.WriteLine();
            Console.WriteLine("Press a key to exit.");
            Console.ReadKey(interceptKey);
        }

        static int GetDieValue() {
            return random.Next(1, 6);
        }
    }
}

A now for the moment of truth:

Console app showing our demo

Fantastic! Assert.That(this_game_rocks).SaidWith(sarcasm)! On the other hand, it took about 12 minutes to code this iteration up as well as write out tests (plus lots of time for me to type out this narrative), and our story list made it clear when we could stop. So let's go show our customers. I'm sure they'll be impressed...

To be continued...

Download

You can browse through or download this tremendously exiting code from davesquared.googlecode.com.

Thursday, 15 May 2008

Garden Race Series: Basics of iterative development and TDD

This page is an index page for the Garden Race series. This intention behind this series of posts is to teach myself a bit about the basics TDD and iterative development by working through an example of building a Snakes and Ladders-style game. Hopefully this example strikes a balance between being simple enough to write up as blog posts and being involved enough to for me to learn some things about iterative development that can translate to real work.

Note: This is post has been back-dated to appear before the other posts in the series. It was written after Part 2.

Saturday, 12 January 2008

The contentious myth of Waterfall development?

Frans has finally had enough of the bickering on the ALT.NET lists. The last straw was when he mentioned the "W" word -- "Waterfall development", at which point he was allegedly set upon by a band of Kent Beck-loving, test-infected, over-zealous Agile thugs :-). Frans' main complaint (and probably a fair one) was the lack of evidence or science behind any of the arguments. Chad Myers had a good reply, where he used an industry example to try and show Waterfall development is fairly useless compared to iterative development.

All this reminded me of something I heard ages ago: Waterfall never really existed. I decided to have a quick look into it. First stop, Wikipedia, which states that the first reference to Waterfall-style development by Winston W. Royce in 1970 was in showing a model that was good in theory but flawed in practice. The purpose of his paper was to describe how the model could be fixed by adopting a more iterative approach. (He didn't use the term "waterfall", but he does use the familiar waterfall phase diagram.)

In the original paper, Managing the Development of Large Software Systems [PDF], Royce asserts that "the [Waterfall] implementation described above is risky and invites failure", his main complaint being that it takes too long to get feedback from testing and that any changes to design required become too disruptive late in the process. He then goes on to describe the steps to "transform a risky development process into one that will provide the desired product". Royce even concludes that the "simpler method [Waterfall] has never worked on large software development efforts."

This 2003 piece from Conrad Weisert has a bit more on the topic. His conclusion seems in rough agreement with Royce's: that phased software development is fine in theory and can work "when practiced with sensible judgment and flexibility" (or even agility ;-) ). But he is quite adamant that strict "Waterfall development", in which one never revists previous phases nor adapts to feedback from later in the process, does not and has never existed. This lends a bit of weight to Chad's view that strictly phased development processes "eventually devolve into necessary sub-standard Agile".

So according to my 5 minute, quasi-research, all this debate over Waterfall development is really just discussing a theoretical model presented over 37 years ago for the purpose of illustrating the advantages of iterative development. :-) Now can we all stop arguing about waterfall and get back to attacking people that misuse code comments please? :-)

Monday, 9 July 2007

Agile chopping or mindless hacking?

Hacknot has an interesting article about planning vs. hacking. The article tells the story of two would-be carpenter's apprentices, George and Abe, who are given the task of bring the carpenter back a tree without flaws or knots before sunset. Not having much time, George rushes to the shed, finds a small, rusty axe, and charges deep into the forest. Abe sets a brisk but steady pace, takes time to find and sharpen a decent axe, and plans his path through the forest to make sure he gets a good tree. Do I need a spoiler warning here? ;-) Unsurprisingly, since the article is about the dangers of unplanned hacking, Abe's approach results in him finding the best tree and he becomes the carpenter's apprentice.

As the article presents this as an analogy for software development, the first thing I thought was "Wow, Abe has a good Agile approach". I also recalled the many experiences of working on George-like projects. Reading on, I discovered the article was actually describing George's approach as Agile. Well, more specifically, it is suggesting some people use Agilist propaganda as an excuse for George's approach.

Since the advent of Agile Methods, [George's] approach seems to be frequently euphemized as "being agile". Fearing the bogeyman of BDUF (Big Design Up Front) as espoused by the AM proponents, the George decides to opt for no design at all. They just start hacking...

So why the disparity between my view and the Hacknot view? Well, Hacknot actually has an audience, unlike me, so the obvious answer is that I'm wrong. However, the benefit of not having an audience is I can write whatever I like without fear of being pulled up on it! So here is my view.

Let's look at some of the ways Abe is being agile. He sets a sustainable pace. He breaks the task down into manageable and essential steps or iterations, which he ensures are completed with high quality. If the carpenter changed his requirements (ok, now I need three trees), Abe would be well placed to adapt as he has a good foundation (sharp axe) to build from. Abe also invests in tests -- he tests the axe he sharpened to make sure it was sharp enough. Yeh, it's not test first, but this is similar to investing in a good test suite, or even creating a spike or prototype.

George's approach is to blindly follow his plan: find axe, find tree, chop like crazy, drag it home. He does not have time for testing. He doesn't even have time to make sure he is doing a decent job as he is so busy trying to do everything in the plan. Its not really Big Design Up Front either, but it is definitely hacky. This is a pet peeve of mine (et al.) -- when people say "we don't have time to do it right because we are too busy doing it wrong".

That said, I agree with the article that the term "Agile" is probably used as an excuse to hack in some places. The worst aspect of the Agile movement is the associated zealotry that is occasionally used as a supplement for rational thought. One of the fundamental parts of Agile methodologies, at least to my reading, is doing what works for you and your team. People over process. Pragmatism. An Agile team should not simply be hacking without planning and design, but rather concentrate on doing the "right amount", and doing all work at high quality so the plan and design can be adapted as work progresses.

Of course, as this was only a story it could have ended very differently to make a completely different point. George could have found a semi-decent tree while Abe was still busy sharpening his axe, effectively demonstrating how evil a utopian approach to planning and design is. Or Abe could have been stuck gathering satellite data of the forest, getting management approval to purchase a GPS for the tree finding activity and filling out a tree requirements specification document. That's one of the problems with analogies I guess. :-)

Wednesday, 21 February 2007

Free online book: Essential Skills for Agile Development

www.AgileSkills.org has a free online book available called Essential Skills for Agile Development that covers OO design, TDD and a number of other important development skills.

Thursday, 15 February 2007

XP resources at xp123.com

The XPlorations section of XP123.com has a number of XP-related resources, including the SimpleSpreadsheet example that first got me into TDD.

Wednesday, 27 December 2006

The Case Against XP

Did you know that Extreme Programming (XP) is based on a methodology used to fairly spectacularly fail to deliver a project? The project team consisted of most of the people have since started selling books on the subject. (Ok, I'm being overly cynical here, there have been some great books written from members of the team).

Matt Stephens also decided to sell some books, but instead by pointing out flaws in XP as co-author of Extreme Programming Refactored. He has an article making The Case Against XP, as well as other information critical of XP on his site. Interestingly enough, he also points out that he supports agile practices in general, and his main aim is cut through the hype of XP and address its limitations, which seems a pretty worthwhile goal.

While I feel Agile methods (of which XP is one example) do have a lot to offer (things like TDD, refactoring, avoiding over-engineering, and interesting approaches to gathering requirements), XP does seem to have been over-hyped (the name itself screams hype!).

There are no silver bullets for software development (but there are werewolves), and I think one of the best parts of Agile methodologies in general is that they emphasise doing what works, rather than blindly following a fixed process. By becoming overly zealous with things like XP we lose that advantage of using common sense to do what works.

For the record, here is how the original, failed XP project (C3) project wound up.

Tuesday, 12 December 2006

Implementing Continuous Integration: With and Without Team System

A presentation on Continuous Integration given by Roy Osherove at TechEd Europe is available on MSDN.

The presentation covers MSBuild, Nant, CruiseControl.NET and others.