XNA Game – Rotation – Rotating square selection – Part 8

 

Today I started to work on the part of the game where Rotation gets it’s name from.  That’s right, the square rotation classes.

I got to the point where I had to make a few key decisions about how the game was going to work.  A lot of the operations in the game happen to the board like selecting a square, filling the board, rotating the board.  There were 3 ways that I could’ve gone with the infrastructure:

  1. Added all of the methods directly onto the board class & interface
  2. Added a bunch of extension methods onto the board interface
  3. Added a set of interfaces that have methods that manipulate the board

Let’s evaluate the options that I could’ve taken in turn.  Firstly option 1, adding the methods directly onto board.  This would’ve left me with board having methods like:

board.SelectSquare(int x, int y);
board.RotateSelectionLeft();
board.Fill(IBoardFiller boardFiller);

The thing that put me off this approach is that board would’ve very quickly become a monolithic beast.  I am always very wary when a class gets bigger than what can comfortably fit on a screen or two.  The other reason why I was put off this approach was the fact that it tied the implementation of board to how squares are selected, selections are rotated etc.  So if in the future I just wanted to change dynamically the way squares are rotated this would’ve been difficult.

Option 2 solves the problem of being able to swap out a piece of functionality e.g. how tiles are selected as I can just reference a different extension method with the same signature (although obviously this is tricky to do at runtime).  But extension methods don’t feel right in this situation either as they are really meant to extend classes that you don’t have access to or they can be used to extend classes to make unit testing easier but it may not be appropriate to include that code in production.  So they don’t feel right in this situation either.

So I’ve gone with option 3, providing a set of interfaces that manipulate the board.  I have defined an interface for selecting squares on the board, rotating them etc.  By doing this each interface (and class that implements it) is very small, concise and has a single responsibility.  The interface for rotating the board for example is defined as follows:

public interface IBoardRotator
{
    void Left(IBoard board);
    void Right(IBoard board);
}

I can now test each of the components on their own and dynamically swap them out very easily later on if I want to change game behaviour in later levels or for a power up or whatever.

To implement rotating a square selection left or right I needed to have the board in a known state.  To do this I built an alphabetical board filler that fills the board (yep you guessed it) in alphabetical order.  I have added unit tests for the alphabetical board filler even though it is only used in my unit tests.  This is an important point as if I am going to be using the alphabetical board filler to set the board in a known state then I had better be sure that the board is in the state that I think it’s in.  This is something that really frustrates me in code that I see in the outside world (but that is a rant for another day).

Once I know that the board is going to be in a known state it is a pretty trivial job to write unit tests for rotating a square selection left or right.  If you want to know what the board looks like visually before and after a rotation see this earlier post.

Here is the psuedo code for implementing rotating a square selection right

  • get centre square selection coordinates
  • set numSqures to 1
  • while a square is selected and exists in all directions of the centre square selection coordinates
  • do loop->
  •     move the tile numSqures squares up from the centre of selection to numSqures squares right of selection
  •     move the tile numSqures squares right from the centre of selection to numSqures squares down of selection
  •     move the tile numSqures squares down from the centre of selection to numSqures squares left of selection
  •     move the tile numSqures squares left from the centre of selection to numSqures squares up of selection
  •     increment numSquares
  • end loop

Quite a simple algorithm.  But again the key to making this easy is by writing our tests first.  This gives us the confidence that the code is working when we write it.

To get the version of the code as it was at the end of this post issue the following commands:

If you don’t have the source code:

git clone git@github.com:kevholditch/Rotation.git

git checkout -b part8 origin/part8

If you have the code on cloned already then issue the following git commands:

git fetch
git checkout -b part8 origin/part8

As always comments & suggestions invited, until next time…

Advertisements