This is the log of a sample project from a small band of software engineers eager to learn better ways of working and new technologies. We've decided to use .NET 3.0 to create a Battleships game.

The Quest For The Perfect Project

Wednesday 9 May 2007

Mocking helps test in isolation

As you are probably already aware, I've been spending most of my time expanding the domain classes that we've come up with. It's been a roller coaster ride going backwards and forwards over several ideas of how it's all implemented. I've actually made forward progress this week in that I'm stepping on territory that hasn't already been visited.

I now have what I see as the fundamentals behind the domain classes ironed out and I decided to write a few tests that exercised game play. Our Game class is somewhat simple at the moment [see figure 1] and at this moment in time we're not 100% sure on how the server will interact with the clients, though I suspect this will become more of an issue sooner rather than later.

public sealed class Game
{
    static Game theGame = null;

    readonly static object locker = new object();

    IList<Board> boards = new List<Board>(); 

    private int currentTurn;

    private IGameController gameController;

    private Game()
    {
    }

    public static Game TheGame
    {
        get
        {
            if (theGame == null)
            {
                lock (locker)
                {
                    if (theGame == null)
                    {
                        theGame = new Game();
                    }
                }
            }
            return theGame;
        }
    }

    public IGameController GameController
    {
        get { return gameController; }
        set { gameController = value; }
    }

    public Board this[Player player]
    {
        get
        {
            foreach (Board board in boards)
            {
                if (board.Player == player)
                {
                    return board;
                }
            }
            return null;
        }
    }

    public void AddBoard(Board board)
    {
        boards.Add(board);
    }

    public int NumberOfPlayers()
    {
        return boards.Count;
    }

    public void NextTurn()
    {
        if (currentTurn >= boards.Count)
        {
            // Reset to player 1 again.
            currentTurn = 0;
        }
        Board playersBoard = boards[currentTurn];
        Attack attack = gameController.TakeTurn(playersBoard.Player);

        Board targetsBoard = this[attack.Target];
        bool result = targetsBoard.ProcessAttack(attack);
        currentTurn++;
    }

    public static void Reset()
    {
        lock (locker)
        {
            theGame = null;
        }
    }
}

Figure 1.

Whilst I'm in the domain area all that I am interested in at the moment is my domain objects. I'm also trying to be truly Agile with our design in that, yes I'm aware that we'll need some form of communication layer and interface into that layer, but at the moment it's not essential that I know the nitty gritty of how it will be implemented. I just assumed that I'd have some interface, with a mechanism for taking a turn. I'd expect this interface to undergo changes when we actually get to the design/implementation of that area.

public interface IGameController
{
    Attack TakeTurn(Player target);
}

Figure 2.

This issue in the past would have caused us problems at this early stage in development when it comes to testing the game play. We could have written a test stub class that we could talk to that provided us with responses to exercise the object. These days you'll often hear people refer to mocking instead. Mocking is not an easy concept to grasp - it took me a while and it's probably a lot harder to explain. We're using Rhino.Mocks for mocking as it is probably the closest mocking library to the one we've used when developing Java. At first I wasn't entirely sure how I was going to test the game play and my first attempt resulted in me writing the steps that I wanted to do, along with what I'd expect to be returned as comments. I then went to bed...

The following evening, I started to create the mock with a little help from some sample code that Jon happened to produce for a presentation to our colleagues on mocking using Rhino.Mocks, figure 3 shows the test in all it's glory.

[Test]
public void VerifyMultipleShipHealthBars()
{
    // Test the game with 3 separate attacks to only 2 targets.
    // Create and program the mocks to do this.
    MockRepository factory = new MockRepository();
    IGameController controller = factory.CreateMock<IGameController>();

    Expect.Call(controller.TakeTurn(evan))
          .Return(new Attack(evan, emma, 2, 3)); // should be a hit for ship1
    Expect.Call(controller.TakeTurn(emma))
          .Return(new Attack(emma, caroline, 3, 2)); // should be a miss
    Expect.Call(controller.TakeTurn(caroline))
          .Return(new Attack(caroline, emma, 2, 3)); // should be a hit for ship1

    factory.ReplayAll();

    // Create the component under test (CUT) and proceed with behaviour.
    Game game = Game.TheGame;
    game.AddBoard(evansBoard);
    game.AddBoard(emmasBoard);
    game.AddBoard(carolinesBoard);

    game.GameController = controller;

    game.NextTurn();
    game.NextTurn();
    game.NextTurn();

    Assert.AreEqual(2, ship1.GetHealth(evan));
    Assert.AreEqual(2, ship1.GetHealth(caroline));

    // Verify that the mock executed as expected.
    factory.VerifyAll();
}

Figure 3.

Mocking will only get you so far and this test is far from complete because it's now highlighted the need to move onto the IGameController interface. As you can see from the NextTurn method in the Game class, we're losing the result from the ProcessAttack method call. This return value would be valuable to the client so that it could update the EnemyBoard for the player. Although the Game.NextTurn() is incomplete, it's still functioning at the moment and it has helped immensely to have a unit test mocking the IGameController concept at this early stage in development. It served it's purpose as it found one or two bugs in my Board logic.

I envisage my next steps to be designing the GameController interface some more, looking at the possibility of having the following calls to complete the next turn functionality.

Figure 4.

Once this communication has been established, I can then revisit my unit test around gameplay and modify that accordingly to exercise the game class with the expanded IGameController implementation. It was however a great exercise and it gave me a chance to play with mocking in .NET.

Wednesday 2 May 2007

I've been here before - Lessons Learned

The past 6 weeks have not been very eventful against this project, though I have learned some serious and valuable lessons from it. Both Jon and I have changed jobs in this period and as a result we haven't been able to meet regularly to stay informed with the others activities on Battleships.

Communication is key

You may remember from my last post that I was happily developing the domain classes for Battleships when the niggling issue regarding multi player capabilities and how we handled it emerged. I was always thinking about it, but I never really sat down until then to work out the fundamentals. All I did was try to work out how a second player could place a ship. This little thought just opened up a whole load of issues and I suddenly realised that my grid of ships was not going to work because I'd need to allow several ships to be placed at the same location.

Jon did raise a few concerns and we did have a few brief conversations over email. I've learned that it is vital that we communicate more regularly. This applies more so with designs, as it's best to identify issues in the early stages to avoid writing code that will eventually get thrown away.

Do you ever think you're going round in circles?

We'll use the example around ships being placed on a board as this will demonstrate when I have experienced a design/implementation where I have just completed a 360 degree over a period of 2 months.

I thought I had designed it at the beginning, but obviously not in the depth that it required; I naively ignored the multi player aspect because I thought we could do something with that later. A big red flashing light should have been going off in my head, but I'll blame the lack of batteries :).

Design 1: Single Ship Locations

Figure 1 shows the grid that the player has configured with his ships.
Figure 1

Figure 2 shows the relationship between the domain classes. The ship knows about the player that it belongs to and the board will have a list of Guid's that have been inserted into the list at an offset corresponding to the real location in the grid.
Figure 2

Offset Calculation Example:

Take a grid that has dimensions of 4x4; you place a ship with a length of 3 going in a vertical direction from coordinate 0,0. In the locations list in the Board it occupied elements [0], [4] and [8]. I'm not sure why I chose to do it this way, I've had bad experiences with multi dimensional arrays in my C++ days and they've haunted me ever since.

This all appeared to work fine, until I started to wonder about the multi player aspect and how the board could cope with another player's ships and not alert to them that a ship already occupied a square.

Design 2: Multiple Ship Locations

After a brief chat with Jon, he convinced me that using a multi dimensional array was much better than my weird offset list. He did say he had concerns about the direction I was going but we never really managed to find time to get together to discuss this any further. Again, alarm bells should have been ringing and I think they were but I wanted to continue my work with Battleships and I didn't want to have to stop work on it.

My board class underwent massive life saving surgery and ended up being something like figure 3.
    public sealed class Board
    {
        int width;
        public int Width { get { return width; } }

        int height;
        public int Height { get { return height; } }

        /// <summary>
        /// The collection of ships on this board.
        /// </summary>
        IDictionary<Guid, Ship> ships = null;
        public IDictionary<Guid, Ship> Ships { get { return ships; } }

        /// <summary>
        /// A representation of the grid to store the attacks made on this grid.
        /// </summary>
        bool[,] grid;

        /// <summary>
        /// A mirror of the grid but this time detailing the location of the ships.
        /// </summary>
        IList<Ship>[,] locations = null;

        public Board(int width, int height)
        {
            this.width = width;
            this.height = height;
            
            // Initialise the bit array.
            grid = new bool[width, height];
            locations = new List<Ship>[width, height];

            // Populate the list with empty placeholders as we will need to
            // be able to insert into the middle of the list.
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    List<Ship> emptyList = new List<Ship>();
                    locations[i, j] = emptyList;
                }
            }
        }

        public bool ProcessAttack(Attack attack)
        {
            bool hit = false;
            IList<Ship> ships = locations[attack.X, attack.Y];

            // Tell all the ships that they have taken damage.
            foreach (Ship ship in ships)
            {
                // If we've hit any ships, the result is a successful attack
                hit = true;
                ship.TakeDamage(attack);
            }

            // Tell the grid we've had an attack at some coordinates.
            grid[attack.X, attack.Y] = true;
            return hit;
        }
    }
Figure 3

Luckily I had issues with the multi-dimensional array syntax when trying to initialise it with empty list's of Guid's, probably the reason I avoided them in the first place. This issue held me up for a month and I went through a period of time where I didn't work on the project because I knew I had a compile error waiting for me when I returned. This is the sort of issue you dread when working on a solo project; you don't have other people on hand to help you resolve silly user errors like this and Battleships was starting to feel like a solo project.

I finally managed to get a 30 minute slot with Jon; I knew he'd be able to fix it. I also wanted to use this opportunity to talk him through my concerns. I'd become too focused on this issue around multiple ships at one location that the overall design that I had chosen became horridly broken. I had this idea of a BoardView into the game board that would show a player the grid from his perspective. Figure 4 shows an overview of how I envisaged this working.
Figure 4

The BoardView was going to have 2 board's, one with the player's ships and the one that he will attack on. With everything going on and the refactoring of the Board to manage multiple ships I lost sight of this design and one day I realised that everything was broken. I had a task to unit test the BoardView and when I couldn't think of anything to test, I realised that I had a flawed design.

I identified the following issues with the design as it stood:

* As it stands the Board had no way of given the Player it's BoardView.

* The BoardView wasn't being utilised at all and we weren't doing anything with the enemyboard.

Design 3: Single Ships Per Board - Again

Rightly so, after my session with Jon we identified several things that neither of us were happy with. We've decided to revert back to a simpler board design like we had in Design 1. I had Guid's everywhere to act as ID values. Too many Guid's obfuscate how the data is linked and sometimes a Guid as an identifier is just unnecessary. It may be easier to transport a Guid remotely but we're moving towards a new design that includes having a Player object as the unique identifier.

Figure 5 shows the new relationships between the classes. I tried to treat the enemy board and player board as the same object previously and this was not necessary. The enemy board will not need to be a domain class, this is something that can remain local to the client as the server will not need to know about these, only the attacks being made.
Figure 5

Jon and I now envisage the Game consisting of a sorted collection of boards (so that we know what the turn order should be for each player). A Board will know which player owns it. The Board will now revert back to it's simpler design of just monitoring 1 set of a ships with no more than one ship occupying a location. When an attack is made, the game would ask all the boards to process the attack, figure 6 demonstrates visually how we envisage this happening.
Figure 6

Orientation had also been made complicated by me in that I was taking the start coordinate of a ship and when placing it on the board, I knew what direction the ship was facing. Depending on the direction the ship was facing, the ship occupied a different set of cells on the board. Jon talked me through how the UI might look for a player and you could see him possibly dragging this ship object over the grid, highlighting all the cells it would occupy. The UI could easily pass the most left set of coordinates meaning that we wouldn't need a complicated orientation algorithm, we'd just need to know if it was facing a horizontal or vertical direction. It's at times like this when you benefit from more heads than one, there was nothing wrong with my design, but it was easily made simpler.

All I have to do now is implement this new design and hopefully this one will manage to stick around longer than the rest.

Labels: