Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Your Code Sunk My Battleship!

Clarity Battleship is a multi-player game in which players code the "artificial intelligence" needed to command their fleet to victory on the high seas. The game is built using WPF and WCF.
Bryan's Blog

Difficulty: Intermediate
Time Required: 1-3 hours to create your own fleet
Cost: Free
Software: Visual Basic or Visual C# Express Editions
Hardware:
Download: Download

Clarity Battleship

At Clarity Consulting we’re always looking for a little friendly competition. That’s why a few times a year we hold coding competitions that we call Tech Challenges. I recently organized one based on the classic board game Battleship.

The flow of the game is based loosely on the board game. The simple goal is to sink all of your opponents’ ships. Since we’re writing code though, I decided to step up the game play and make it a more interesting, requiring more strategy than luck.  Just like the board game, the battlefield is a grid. Unlike the classic, however, multiple teams can battle at the same time. Each team’s fleet is assigned to a region of the Battlefield.

In our tournament we had six teams battle at a time and held one “Battle Royale” with eleven teams.  Below you can see a sample screen shot of a game with three teams. Blocks in red indicate damage. The bottom of the screen displays messages broadcast or information about fleets that have been sunk. 

Screen Shot

Teams position ships at the beginning of the game and give ships orders to execute including:

  • Attacking locations on the battlefield
  • Performing reconnaissance on an area of the battlefield
  • Broadcasting messages to opponents or conspirators via an “open radio frequency” (collusion and deception are allowed and encouraged!)

Game play is pseudo turn-based. Teams are notified by the game when their ships are ready to execute an order. Teams are also notified as ships are damaged or sunk. The game ends when only one team remains.

Application Architecture

Now that you have a feel for the game play, let’s talk about the code. One reason for building this game was to give myself a fun way to learn more about Windows Communication Foundation (WCF) and Windows Presentation Foundation (WPF), new features of the .NET Framework 3.0.

WCF provides a new unified mechanism for client-server communication that greatly simplifies that amount of work necessary to by standardizing the way you build your communication pieces. I recommend reading this article as a good place to start learning about WCF.

WPF enables richer user interfaces and better integration between developer and designer through the use of XAML. As you can see by my simple (though I think pretty cool) UI, I focused my interest on WCF, but did take advantage of a bit of what WPF has to offer.

Overview

The Battleship application is divided into three parts. The Battleship.Components project is a shared assembly that contains the classes which manage the communication and the logic of the game as well as class definitions for all of the ships (i.e. AircraftCarrier, Battleship, etc.). The Battleship.Gameboard uses WPF to display the action on the battlefield. Finally, console applications are built by each team wishing to compete.

The diagram below shows how the pieces fit together.

Architecture Diagram - Overview

WCF Communication

As I mentioned, my focus in development was using WCF to manage the communication between the fleets and the game. I was very interested in exploring the bidirectional communication that can be achieved with WCF because it seemed like a perfect fit for the scenario. A traditional web service can receive messages from clients and send back responses. However, it can’t originate a message on its own to send to the client. For example, with a web service I could easily have a fleet tell the game about an order to execute (i.e. “I want my Battleship to attack 7,2”). However, I can’t have that web service inform another team that its ship was just sunk.

With WCF I was able to achieve this without writing a lot of custom code. There is some complexity in the code, but most of that is because my goal was to hide the communication layer from the building of a fleet so that players could simply focus on writing the code to implement their strategy. The following sections will walk through the flow of the communication.

Server Side

While the Battleship.GameBoard is Windows Forms App, it actually should be considered the server in this application. That’s because it hosts the WCF service that the Fleets call.

To define a WCF service, you start by defining the interface for the service. Similar to the WSDL of a web service, this is going to tell consumers what the contract, or inputs and outputs, of the service are. Callers will call the different operations exposed by the service.

In this application, the IBattleshipGameService interface seen below defines the contract for the service.

[ServiceContract(CallbackContract=typeof(IBattleshipGameServiceCallback))]
public interface IBattleshipGameService
{
    [OperationContract(IsOneWay = true)]
    void RegisterFleet(Fleet fleet);

    [OperationContract(IsOneWay = true)]
    void ExecuteOrder(string shipID, Order order);
}

The interface shows that there are two methods for consumers to call, RegisterFleet and ExecuteOrder. The OperationContract attribute indicates that these are methods. The IsOneWay parameter of the attributes indicates that no response is sent back to callers. That probably seems straightforward for the RegsterFleet call, but you might expect that ExecuteOrder might return a result.

Actually what happens is that when a fleet is registered, the service holds on to a channel for calling back to the fleets. And the ExecuteOrder method only queues up an order for processing, but doesn’t immediately execute it. Before queuing it up, the game logic ensures that there aren’t already too many pending orders in the queue for the ship. This prevents a team from cheating by blasting the server with orders. We’ll delve more into how the communication back to the client is achieved later.

With the interface to the service defined, let’s turn our attention to a portion of the application configuration file.

<configuration>
  <system.serviceModel>
    <services>
      <service name="Clarity.Battleship.Components.GameSide.BattleshipGameService" 
                    behaviorConfiguration="BattleshipGameServiceBehavior">
        <endpoint address="http://localhost:8088/BattleshipGameService"
                  binding="wsDualHttpBinding"
                  contract="Clarity.Battleship.Components.GameSide.IBattleshipGameService"/>
      </service>

This shows the definition of the service. The contract attribute indicates the service will adhere to the IBattleshipGameService interface. The binding attribute indicates that the WSDualHttpBinding will be used. This is the plumbing that allows both services and clients to send and receive messages.

With the interface and configuration in place, a concrete implementation has to be built. The BattleshipGameService class provides this. This class implements the IBattleshipGameService interface described above. The actual code for registering a fleet and executing an order are therefore defined here.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,UseSynchronizationContext = false)]
public class BattleshipGameService : IBattleshipGameService

Note the ServiceBehavior attribute and its InstanceContextMode property that decorate the BattleshipGameService class. WCF uses this attribute to decide how to execute the operation when a call comes into the service. In this case, the attributes indicate that the service should run as a singleton. In other words, all calls will be executed by a single instance of the BattleshipGameService. You can further see this in the following code taken from the constructor of the Game class that shows how the service is actually brought to life.

Uri u = new Uri("http://localhost:8088/BattleshipService");
_gameHost = new ServiceHost(new BattleshipGameService(), new Uri[] { u });
_gameHost.Open();

The ServiceHost (which does what it sounds like and hosts the service) is created by passing in an instance of our service object and the uri for the address of the service (I know the uri seems redundant since it’s in the config, but as far as I saw I had to use this constructor). Since we know the service host will just use the single instance of the BattleShipGameService, we can track that instance through a private property for convenience and hook its events as seen in the code snippets below.

private BattleshipGameService BattleshipGameService
{
    get
    {
        return (BattleshipGameService)_gameHost.SingletonInstance;
    }
}

this.BattleshipGameService.FleetRegistered +=
   new FleetRegisteredEventHandler(BattleshipGameService_FleetRegistered);
this.BattleshipGameService.OrderExecuted += 
   new OrderExecutedEventHandler(BattleshipGameService_OrderExecuted);

This allows us to elegantly pass off the calls to the Game class. The service then acts as a simple pass through and leaves the Game class to manage the game.

Client Side

Now that we’ve covered the basics of the server side, let’s look at what happens on the client side. In each console app, a class is created that derives from the FleetCommander class (there are complete directions on how to do this with the downloadable source). The FleetCommander class exposes all of the ships of the Fleet for the team to program against.

To avoid cheating, (some would call it creative programming) each ship gets a unique identifier set by the Game at startup. That way the Game knows which orders are legitimate. To hide that id and details from the FleetCommander and make a very simple and intuitive interface, the method for executing an order is exposed off of the ship classes. The code snippet below shows how FleetCommander could react to an event indicating that a ship is ready for orders. In this case it is saying that the ship should attack the coordinate at location _targetX, _targetY.

public override void OnShipAwaitingOrders(Ship ship, Order lastOrder)
{
      Order orderToExecute = new Order();
      orderToExecute.Coordinate = new Coordinate(_targetX, _targetY);
      orderToExecute.Type = OrderType.Attack;
      ship.ExecuteOrder(orderToExecute);

Under the covers, the Ship.ExecuteOrder method raises an event which is handled by the base FleetCommander class. The FleetCommander calls the appropriate method on a WCF client class which makes the call to the BattleshipGameService.

A portion of the definition of the WCF client can be seen below. You’ll see that it is a class that derives from the DuplexClientBase. This class manages the channels that pass messages back and forth. You can think of it as similar to a web service proxy class.

public partial class BattleshipGameServiceClient : 
    System.ServiceModel.DuplexClientBase<IBattleshipGameService>, IBattleshipGameService
{

So that covers how messages are sent to the server, but you may be asking yourself “How did a FleetCommander find out it that a ship was awaiting orders?” or for that matter “How does it get notified about anything?”

Well, each FleetCommander has an instance of a listener class which receives callbacks from the game service. The way this is set up for the listener is very similarly to how we set up the service. Once again we start with an interface, in this case the IBattleshipGameServiceCallback interface.

public interface IBattleshipGameServiceCallback
{
    [OperationContract(IsOneWay = true)]
    void OnFleetUpdated(Fleet fleet, FleetUpdateReason reason);

    [OperationContract(IsOneWay = true)]
    void OnGameStateChanged(GameInfo gameInfo);

    [OperationContract(IsOneWay=true)]
    void OnOrderExecuted(string shipID, Order lastOrder);

    [OperationContract(IsOneWay = true)]
    void OnMessageReceived(string data, Coordinate c);

    [OperationContract(IsOneWay = true)]
    void OnFleetSunk(string fleetName);
}

Just like before the OperationContract attributes are used to indicate the methods available. And just like before we need a concrete implementation of the interface. Enter the FleetCommanderListener.

public class FleetCommanderListener : IBattleshipGameServiceCallback

The FleetCommanderListener is used when creating the BattleshipGameServiceClient. As you can see in the constructor definition below, a callbackInstance parameter must be passed in. The BattleshipGameServiceClient can then handle communication to the server via the IBattleshipGameService interface and also listen for communication back that adheres to the IBattleshipGameServiceCallback interface!

public BattleshipGameServiceClient(System.ServiceModel.InstanceContext callbackInstance) :
     base(callbackInstance)

The last little piece of the puzzle is the client configuration file.

  <system.serviceModel>
    <client>
      <endpoint name="Clarity.Battleship.Components.IBattleshipGameService"
                address="http://localhost:8088/BattleshipGameService"
                binding="wsDualHttpBinding"
                bindingConfiguration="BattleshipBinding"
                contract="Clarity.Battleship.Components.GameSide.IBattleshipGameService" />
    </client>
    <bindings>
      <!-- configure a binding that support duplex communication -->
      <wsDualHttpBinding>
        <binding name="BattleshipBinding"
                 clientBaseAddress="http://localhost:6088/BattleshipGameClient/">
        </binding>
      </wsDualHttpBinding>
    </bindings>
  </system.serviceModel>

As you can see from the example above, the configuration is fairly straightforward. Just like in the server config, we say we’re using the wsDualHttpBinding. The one additional piece of info is the clientBaseAddress which tell the server how to call back into the client.

The Complete Picture

So after reading all of this, you might be thinking “I thought he said there wasn’t much custom code needed.” I think that is still true in terms of the WCF. While certainly not trivial, it really boils down to building a couple interfaces, their concrete implementations, and filling out some config files. Most of the complexity came in was in building out the eventing model to simplify the interface for teams.

The following diagram helps pull everything together by showing an example of the flow of the communication for execution of an order.

Architecture Diagram - Flow

1. The derived FleetCommander makes calls the ExecuteOrder method on a Ship (i.e. “I want my Battleship to attack 7,2”)

2. The Ship raises an event which is handled by the FleetCommander base class which in turn calls to the BattleshipGameServiceClient

3. This WCF client class makes the WCF call to execute the order

4. The single instance of the service raises an event to the Game class with the order information

5. The Game class which controls all the game flow enqueues the order

6. After processing an order from the queue, the Game calls the OnOrderExecuted WCF callback which gets invoked on the FleetCommanderListener

7. Finally the FleetCommanderListener raises an OrderExecuted event to the derived FleetCommander class (i.e. “Your Battleship’s attack at 7,2 resulted in a Hit”) which is turned calls the OnShipAwaitingOrders method and the cycle starts over

WPF User Interface

The last thing to cover with the architecture is the UI. It is pretty simple, but I wanted to experiment a little with WPF. The Gameboard assembly contains a class called Battlefield that is a WPF Window (comparable to a Winform Form). A Grid control makes up the main component of the gameboard. The grid lent itself to building the UI since everything stretches nicely to fit the screen, regardless of the number of teams competing.

Once the game starts, the game board is setup by adding rectangles and images to the grid control. Throughout the game, the colors of the rectangles are changed to indicate shots, recon, or hit targets.

Below is an example of some of the Battlefield code behind in which the rectangle for the location of the order is painted a different color depending on the result of an order.

Rectangle r = _rectangles[_lastOrderStarted.Order.Coordinate];
if (e.Order.Result == OrderResult.ShipHit)
{
    r.Fill = Brushes.Red;
    _rectangleFills[e.Order.Coordinate] = r.Fill;
}
else if (e.Order.Result == OrderResult.ShipSunk)
{
    r.Fill = Brushes.Red;
    _rectangleFills[e.Order.Coordinate] = r.Fill;
}
else if (e.Order.Result == OrderResult.Miss)
{
    r.Fill = _rectangleFills[e.Order.Coordinate];
}
else
{
    r.Fill = _rectangleFills[e.Order.Coordinate];
}

Conclusion

Hopefully you enjoyed this game and learned a little in process. I encourage you to download the code and build your own fleet. The download includes a document called “Clarity Battleship – Building a FleetCommander” which gives you step by step instructions. Feel free to post your own builds here for other people to challenge and good luck with your battles!

Bryan Dougherty is a Principal with Clarity Consulting, a software development consulting firm based in Chicago, IL. He is an MCP with several years of professional software engineering experience. Bryan earned a B.S. in Computer Science from Northwestern University and writes about topics related to software development on his blog.

Tags:

Follow the Discussion

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.