Thought Cloud structure

May 16th, 2011

I am building Thought Cloud, a collaborative mind mapper, for an upcoming demo. Please grab the code and follow along.

I’ve created the structure of the application:

FacetedWorlds.ThoughtCloud.sln

FacetedWorlds.ThoughtCloud is the main Silverlight application. It has out-of-browser settings enabled. I added a reference to the NuGet package “Correspondence.Silverlight.App”.

The UnitTest project is also a Silverlight application, as required by Jeff Wilcox’s Silverlight Unit Test Framework. But I didn’t create it with the project template from the Silverlight Toolkit. Instead, I added a reference to the NuGet package “Correspondence.Silverlight.UnitTest”, which has a dependency on the framework’s package.

The ViewModel project is a Silverlight class library to which I simply added the “Correspondence” package. Not much is required for a Correspondence view model, since it is built on Update Controls.

Finally, the Model project is a Silverlight class library to which I added “Correspondence.Model”. This package adds a “Models” folder, which is great when using the all-in-one package. But since this project structure is broken down, that folder is redundant. So I moved the contents of the folder up to the root of the project, and change the namespaces to drop the folder name.

This structure gives me the ability to build Thought Cloud in a test-driven manner. I write separate tests for the view models and the models, even though the view models use the models. I’ll show you why as we go along.

Preparing a Correspondence example

May 16th, 2011

On the 9th of June, I’ll be presenting Correspondence to the Dallas .NET User Group. I will reprise the presentation on the 13th for North Texas Silverlight. Please come see whichever one you can attend.

I’ll be showing how to use Correspondence to build Silverlight out-of-browser applications. These apps will reside on your desktop (Mac or PC), use isolated storage, synchronize with the server while online, and push updates in real time as they happen. Not only is it good for keeping your data on two or more machines, this pattern is ideal for collaboration with other users.

You can help me to build the demo. It is a collaborative mind mapper called Thought Cloud. I’m hosting it on GitHub, so please pull the code and try it out.

Windows Phone navigation workarounds

April 26th, 2011

Silverlight for Windows Phone has adopted a navigation mechanism reminiscent of a web browser. Each page is authored as a separate resource. The application issues commands to a NavigationService to move from one page to the next. The phone even has a hardware back button. This choice of web-like navigation causes no end of difficulties to application developers.

Navigation in Windows Phone starts with a URI. But rather than identifying an HTML resource, this URI identifies a XAML resource. The application doesn’t create pages; it just identifies them. The runtime creates each page based on the given URI. This causes three problems.

  • Parameters
  • View model location
  • Back-stack modification

Parameters

The URI must contain all of the information required to construct the page. For the main page of the application, this is usually fine. Just bring up the XAML representation of the main menu. But subsequent pages often drill down into detail. If the main page displays a list of articles, and we want to bring up a specific article when the user taps, then we have to identify the article in the URI. All of the article links target the same XAML resource. It’s the data that differs.

To differentiate the links, many developers take the typical web-based approach. They add query string parameters to the URL. Chris Koenig gives an excellent example of parameter-passing. The Silverlight NavigationContext object exposes these parameters via the QueryString property, specifically for this purpose.

The problem with query string parameters is that they requires an application to drop down to string-based identification, rather than rely upon direct object access. To generate the query string, an application has to extract an ID from an object. To consume the query string, it has to look that ID up in an object model. It usually ends up getting right back to the object that it started with. Wouldn’t it be easier to use that object reference directly?

The web was designed for stateless operation. Each request is isolated from the ones that came before. No objects are assumed to be kept on the server for the user’s next request. Each request must contain enough information to reload those objects and generate the required page. This is what allows the web to scale.

But the phone isn’t like a stateless web server. It has rich object state. It exists to serve just one user. It can afford to load objects into memory, and use references among those objects as that user navigates through the application. To drop down to a string only to come back to the same object is not taking full advantage of the platform. What’s good for the web is not necessarily good for the phone.

View model location

The second problem is pairing a view model with a view. Since navigation is based on identifying the view, Windows Phone forces you into a view-first approach. The view is left with the responsibility of finding its view model. To complicate matters, the view must sometimes pass its query string parameters to the view model. The view model uses the parameters to find a reference to the model object.

A common solution to this problem is the view model locator pattern. A view model locator is a single-instance object registered as an application resource. Each view binds its DataContext to a property of this resource, which is an instance of a view model.

    <Application.Resources>
        <vm:ViewModelLocator x:Key="Locator"/>
    </Application.Resources>

public class ViewModelLocator
{
    private readonly MainViewModel _main;

    public ViewModelLocator()
    {
        _main = new MainViewModel();
    }

    public MainViewModel Main
    {
        get { return _main; }
    }
}
<UserControl x:Class="ToDoList.MainPage"
    ...
    DataContext="{Binding Main, Source={StaticResource Locator}}">

The biggest problem with the view model locator pattern is that it treats view models as singletons. Since the view model locator is an application resource, there is only one instance serving all of the view models. And since each view data binds to a property, the view model locator can only have one instance of each view model. It is not a view model factory. There is no way to pass query string parameters to a method in order to construct an instance of a view model. You have to write code in the view to pass the parameters to the view model in its DataContext. That one and only instance of the view model must be able to change its behavior to point to a different model instance every time the user navigates.

Back-stack modification

The third problem is removing pages from the back-stack. Some interactions have intermediate steps: the user logs in before seeing content, or a wizard guides a user through a multi-page process to achieve a result. When the user has completed these intermediate steps, they don’t want to return to them. If they have completed the steps prior to reaching the main menu (as in the login example), then they want to exit the application when they hit back from the main menu. They don’t want to see the login screen again. Or if the intermediate steps occur after the main page (as in the wizard example), then they want to return to the main page when they are finished. These intermediate pages should be popped off the stack.

Unfortunately, the Windows Phone OS is completely in charge of the back-stack. It keeps track of each URI that the user visits, and returns to past URIs when they hit the back button. There is no API for removing a URI from the stack. We are left with a couple of hacks. To skip intermediate pages at the beginning of the stack (login), we can exit the application prematurely by throwing an unhandled exception. To skip pages later in the stack (wizard), we can bounce to each intermediate page in the main page’s OnNavigatedTo. Neither solution is particularly appealing.

Conclusion

The Windows Phone navigation system is based on the web metaphor. Unfortunately, the web is the wrong model for a single-user device. The decision to model navigation in this way has caused phone applications to act like web pages. The intent was to bring a well-understood interaction language to the phone. But in practice, it just forced application developers to work around the shortcomings of a misappropriated model.

This decision leads developers into a view-first approach, rather than beginning with the view model. It forces developers to convert object references to strings, only to convert them back into object references. And it requires ugly hacks to produce even the simplest of interactions. If you write Windows Phone apps, prepare to use these workarounds.

How to Make the MacBook Pro Trackpad Less Jumpy

April 21st, 2011

To right-click on the MacBook Pro, you put two fingers on the trackpad, then click. To scroll, you put two fingers on the trackpad and move up or down. In Visual Studio, the scrolling is extremely sensitive. When you try to right-click something, it often scrolls out from underneath the mouse. You end up right-clicking on something else entirely.

It seems that the problem is related to the horizontal motion rather than the vertical. If you reduce the number of lines that the mouse rocker moves left and right, the problem subsides. But the Mouse control panel applet won’t let you set this value to zero, so you can’t make it go away entirely.

Unless you hack the registry.

Download the attached file, examine it to make sure you understand what it does, then merge it into your registry. Reboot, and you will be able to right-click without the jumpiness.

Building an occasionally connected Silverlight application with Correspondence

April 18th, 2011

I posted a to-do list application built with Correspondence. It holds the user’s data in isolated storage, and pushes it to a synchronization service. It took about 56 minutes and 41 seconds to build.

Building an occasionally connected Silverlight application with Correspondence from Michael L Perry on Vimeo.

Correspondence for Silverlight in NuGet

April 16th, 2011

With Correspondence for Silverlight, you can create occasionally-connected client applications that run either in or out of the browser. They use isolated storage to save the user’s changes before pushing them up to the synchronization service. From there, they are pushed out to other Silverlight or Windows Phone clients. Create a Silverlight application and use NuGet to add a reference to:

Correspondence.Silverlight.AllInOne

The package will add the following features to your application:

  • Main view model
  • View model locator
  • Model
  • Navigation model
  • Synchronization service

Open the Readme.txt file for instructions on adding the view model locator to App.xaml and MainPage.xaml.

To-do list

As an example, try out this to-do list application. Log in using any user name you like; this example app doesn’t authenticate. You can add tasks, update their name, and mark them completed.

Get Microsoft Silverlight

Some things to try. Log in using your name, enter some tasks, then switch to another user. Those tasks disappear. Switch back and they reappear.

Close the browser, then come back to this page. Your data is still here. Disconnect from the network and this still works.

Now go to a different machine and log in using your name again. At first the list will be empty, but you’ll see tasks come down as the app synchronizes.

A web page that lets you share data between two browsers is nothing new. What makes this different is:

  • Data is still accessible while off line. This is particularly important for out-of-browser applications.
  • There is no application-specific server-side schema or code. The data model and all of your logic is in the client.
  • Silverlight applications can synchronize with Windows Phone applications.

Here’s how the to-do list example is built. Download the source code and follow along.

Log on

A User is a fact identified by a user name. We represent the user in the Factual model:

fact User {
key:
    string userName;
}

Any time we create a User with a give user name, we get back the same instance. This allows the user to log in. Of course, a production system would authenticate the user, but it would still follow the same pattern. The user input is captured first in a transient navigation model:

public class NavigationModel
{
    private Independent<string> _userName = new Independent<string>();

    public string UserName
    {
        get { return _userName; }
        set { _userName.Value = value; }
    }
}

This is where the user name is stored while the user is logging in. We expose this property and a login command through the view model.

public string UserName
{
    get { return _navigationModel.UserName; }
    set { _navigationModel.UserName = value; }
}

public ICommand LogIn
{
    get
    {
        return MakeCommand
            .When(() => !String.IsNullOrEmpty(_navigationModel.UserName))
            .Do(() =>
            {
                User user = _community.AddFact(new User(_navigationModel.UserName));
                _navigationModel.CurrentUser = user;
                _navigationModel.UserName = string.Empty;
            });
    }
}

The login command is only enabled when a user name has been entered. When the user clicks the button, a new User is created with the name that they entered. That User is then stored in the navigation model, and the entered name is cleared out.

List tasks

Once we have a User fact in our navigation model, we can list the tasks assigned to that user. A task is another fact, which references the User to which it is assigned.

fact Task {
key:
    unique;
    User assignedTo;
}

Because the key doesn’t already contain enough information to distinguish one task from another, it is marked as “unique”. This causes Correspondence to generate a new Task every time one is created, rather than looking for an existing one.

The User queries for related tasks.

fact User {
    ...

query:
    Task* openTasks {
        Task t : t.assignedTo = this
            where not t.isCompleted
    }
}

That query is exposed through the view model. The MainViewModel generates a list of TaskViewModels to wrap the Tasks.

public IEnumerable<TaskViewModel> OpenTasks
{
    get
    {
        if (_navigationModel.CurrentUser != null)
            return
                from Task t in _navigationModel.CurrentUser.OpenTasks
                select new TaskViewModel(t);
        else
            return Enumerable.Empty<TaskViewModel>();
    }
}

Add a task

The user can enter the name of a task, then hit the Add button to add it to the list. Just like the user name, the task name is stored in the navigation model temporarily. When a user is logged on and a name has been entered, the button becomes enabled. When clicked, it adds a task assigned to the user, and sets the task name.

public ICommand AddTask
{
    get
    {
        return MakeCommand
            .When(() =>
                _navigationModel.CurrentUser != null &&
                !String.IsNullOrEmpty(_navigationModel.TaskName))
            .Do(() =>
            {
                Task task = _community.AddFact(new Task(_navigationModel.CurrentUser));
                task.Name = _navigationModel.TaskName;
                _navigationModel.TaskName = String.Empty;
            });
    }
}

Change a task name

Notice that the constructor above only takes the User to which the task is assigned. That’s because the User is part of the key. The task name is not. It is a mutable property.

fact Task {
    ...

mutable:
    string name;
}

Because the property is mutable, the user can edit the name and hit the Update button. The button is only enabled when a task is selected and the name has been entered.

public ICommand UpdateTask
{
    get
    {
        return MakeCommand
            .When(() =>
                _navigationModel.SelectedTask != null &&
                !String.IsNullOrEmpty(_navigationModel.TaskName))
            .Do(() =>
            {
                _navigationModel.SelectedTask.Name = _navigationModel.TaskName;
                _navigationModel.TaskName = String.Empty;
            });
    }
}

Mark a task completed

Finally, the user can mark a task as completed. This is itself a new fact.

fact TaskCompleted {
key:
    Task completedTask;
}

The Task fact queries for related TaskCompleted facts to see if it is completed.

fact Task {
    ...

query:
    bool isCompleted {
        exists TaskCompleted c : c.completedTask = this
    }
}

This query is then used in the User’s query so that he sees only the open tasks.

fact User {
    ...

query:
    Task* openTasks {
        Task t : t.assignedTo = this
            where not t.isCompleted
    }
}

When the user clicks the Complete button, it completes the task. We added this method to the Task itself using a partial class.

public ICommand CompleteTask
{
    get
    {
        return MakeCommand
            .When(() => _navigationModel.SelectedTask != null)
            .Do(() =>
            {
                _navigationModel.SelectedTask.Complete();
            });
    }
}
public partial class Task
{
    public void Complete()
    {
        Community.AddFact(new TaskCompleted(this));
    }
}

Because this creates the TaskCompleted fact, it causes the list to refresh, and the task is removed.

Build your own apps

Please download Correspondence and try it out. If you have any questions about building your own models, please contact me by email or Twitter. If you’d like to use my synchronization server, just send me a request for an API key. I’ll be glad to help you get set up.

Correspondence in NuGet

April 1st, 2011

I have released Correspondence for Windows Phone 7 to NuGet. Correspondence is a NoSQL database for the phone and other occasionally connected clients. While offline, it queues all of your user’s changes. While online, it pushes those changes to a synchronization server, which in turn pushes it to other devices.Create a Windows Phone 7 application and install the following package:

Correspondence.WindowsPhone.AllInOne

This will add models, view models, a view model locator, and a synchronization service to your application.

Models

Correspondence models are expressed in a language called Factual. The package adds a folder called Models that contains a Factual file and a T4 template to parse it. The first thing you’ll want to do after adding the package reference is to hit the “Transform All Templates” button to generate some code.The starter model contains concepts useful for any Windows Phone 7 application. First it has an Identity:

fact Identity {
key:
   string anonymousId;
   ...
}

The Identity fact is uniquely identified by the user’s anonymous Windows Live ID. If they move to a different device, they carry their Identity with them. This makes it really easy to collaborate between users.The model also has the ability to disable and reenable toast notifications:

fact DisableToastNotification {
key:
   unique;
   Identity identity;
query:
   bool isReenabled {
       exists EnableToastNotification e : e.disable = this
   }
}

fact EnableToastNotification {
key:
   DisableToastNotification* disable;
}

The Identity fact queries to see if there are any DisableToastNotification facts:

fact Identity {
   ...
query:
   DisableToastNotification* isToastNotificationDisabled {
       DisableToastNotification d : d.identity = this
           where not d.isReenabled
   }
}

The Identity partial class adds a property that turns these successor facts into a simple boolean property:

public partial class Identity
{
    public bool ToastNotificationEnabled
    {
        get { return !IsToastNotificationDisabled.Any(); }
        set
        {
            if (IsToastNotificationDisabled.Any() && value)
            {
                Community.AddFact(new EnableToastNotification(IsToastNotificationDisabled));
            }
            else if (!IsToastNotificationDisabled.Any() && !value)
            {
                Community.AddFact(new DisableToastNotification(this));
            }
        }
    }
}

Extend these facts and build your model in model.fact. Hit “Transform All Templates” after each change to regenerate the classes. Create partial classes to add your own business logic. ViewModelsThe package also adds a ViewModels folder, which contains two view models:

  • MainViewModel
  • SettingsViewModel

The main view model is just an empty starting place. Add properties to this view model to data bind to controls on your main page.Settings view model has one property started for you. It exposes the ToastNotificationEnabled property in the Identity. This is just a simple pass-through. You don’t raise PropertyChanged events or inherit a base class. Correspondence will raise the PropertyChanged event for you. This works because Correspondence is built on Update Controls.

public bool ToastNotificationEnabled
{
    get { return _identity.ToastNotificationEnabled; }
    set { _identity.ToastNotificationEnabled = value; }
}

The ViewModels folder also contains a ViewModelLocator class. Open the Readme.txt file for instructions on adding this class to your application resources, and referencing it in your views. As you create your own view models, follow the pattern demonstrated with these two existing view models to add them to the locator.

public class ViewModelLocator
{
    private readonly MainViewModel _main;
    public ViewModelLocator()
    {
        ...
        _main = new MainViewModel(_synchronizationService.Identity);
        ...
    }
    public object Main
    {
        get { return ForView.Wrap(_main); }
    }
}

SynchronizationService

Correspondence would be just another isolated-storage-based NoSQL database if it weren’t for the synchronization service. For the most part, this class will be invisible to you. It is initialized in the view model locator, and it works automatically.Every time you store a published fact, the synchronization service wakes up and pushes it to the server. If the device is off-line at the time, the fact is queued. The synchronization service will wake up when the network becomes available and start pushing facts.

When another device publishes a fact to which you have subscribed, the server will send a push notification. The synchronization service responds to these push notifications and adds the facts to the repository. Your app is notified via data binding, and the UI updates.The synchronization service is also watching the ToastNotificationEnabled property. When it is true, it subscribes for toast notifications. When it is false, it unsubscribes.

There are two parts of the synchronization service that you are likely to change. The first is the POXConfigurationProvider. When you create an account with a synchronization server, you will be issued a URL, a user name, and a password. Put these into this class.

public POXConfiguration Configuration
{
    get
    {
        string address = "https://historicalmodeling.com/correspondence_server_web/pox";
        string userName = "<<Your username>>";
        string password = "<<Your password>>";
        return new POXConfiguration(address, "MyCoolWindowsPhoneApplication", userName, password);
    }
}

The second is the subscriptions. The synchronization service subscribes to the Identity. The device will receive any facts published to the user’s Identity. As you develop your own model, you will create other publications. Add linq expressions here to subscribe to those facts.

_community = new Community(IsolatedStorageStorageStrategy.Load())
    .AddAsynchronousCommunicationStrategy(new POXAsynchronousCommunicationStrategy(configurationProvider))
    .Register<CorrespondenceModel>()
    .Subscribe(() => _identity)
    ;

More to come

There is definitely a learning curve for historical modeling in general, and Correspondence in particular. Please review the body of articles here and on Historical Modeling. I will also be posting videos and examples over the next few weeks.I am also opening my synchronization server for a private beta. Please email or mention me to reserve your place. mperry@mallardsoft.com or @michaellperry.

Mutable fields in Correspondence

March 29th, 2011

image Correspondence is a client-side NoSQL database that synchronizes with other clients through a shared server. If you’ve experimented with the bits, there is a breaking change in the latest changeset. A new build is coming soon, as well as a closed beta of the synchronization server.

Correspondence is built on the concept of historical modeling. This is a set of theory describing historical facts with partial order. The facts have predecessor/successor relationships that define both the order in which they can consistently be applied, and the mechanism for graph traversal.

One of the core tenets of historical modeling is that facts are immutable. This makes them easy to synchronize. Fields also determine the fact’s identity. Two facts having equal fields are actually the same fact.

In many applications, it is desirable to have mutable fields. In historical modeling, you have to use the Entity Property pattern to simulate mutability. This pattern builds on the strengths of historical modeling, allowing participants to unambiguously synchronize state changes. It also has the added benefit of detecting conflicts, and allowing users and applications to resolve them. Unfortunately, this pattern can be cumbersome to apply.

The mutable keyword

A Correspondence model is defined using a language called “Factual”. The upcoming release adds three keywords to the Factual modeling language. These three keywords break a fact into sections:

  • key - Required. Contains all key fields.
  • mutable - Optional. Contains all mutable fields.
  • query - Optional. Contains all queries and predicates.

The key and query sections contain members that have always been in the language. The mutable section adds new functionality.

Fields in the mutable section are mutable. The compiler generates the Entity Property pattern on your behalf. These fields are not part of the key, since they are not actually stored within the fact itself. They are stored in successor facts that track changes.

You write this:

fact Person {
key:
    unique;

mutable:
    string name;
}

The Factual compiler generates this:

fact Person {
key:
    unique;

query:
    PersonName* nameCandidates {
        PersonName n : n.person = this
            where n.isCurrent
    }
}

fact PersonName {
key:
    Person person;
    string value;
    PersonName* prior;

query:
    bool isCurrent {
        not exists PersonName next : next.prior = this
    }
}

Disputable properties

The Entity Property pattern detects conflicts and allows the application to resolve them. To aide the application in this task, Correspondence defines Disputable<T>. This template can be converted to and from the raw type T, but it also exposes two additional properties.

  • InConflict – A boolean that is true if a conflict has been detected.
  • Candidates – A collection of possible values.

Using Disputable<T>, the view model can apply an alternate style when a conflict has been detected, and allow the user to choose from among the candidates to resolve it.

public class PersonViewModel
{
    private Person _person;

    public PersonViewModel(Person person)
    {
        _person = person;
    }

    public string Name
    {
        get { return _person.Name; }
        set { _person.Name = value; }
    }

    public bool ShowConflictStyle
    {
        get { return _person.Name.InConflict; }
    }

    public IEnumerable<string> CandidateNamesItemsSource
    {
        get { return _person.Name.Candidates; }
    }

    public string CandidateNamesSelectedItem
    {
        get { return _person.Name; }
        set { _person.Name = value; }
    }
}

The Factual modeling language specification has been updated to reflect the new mutable keyword. If you’ve created Correspondence models in the past, you will be required to add key and query section headers once you upgrade. Then you will be able to take advantage of mutability.

User identity on Windows Phone 7

March 11th, 2011

When you can challenge another person to a Faceted Reversi game, you provide their name. So Reversi needs a way of authenticating the user so it can send them the correct games. I didn’t want the user to have to enter a password to play my game, so I relied upon the anonymous Live ID already available on the phone. I just have to associate your anonymous ID with your chosen screen name.

Windows Live provides identity

Once I’ve gotten the anonymous Live ID using the technique I previously blogged about, I create an Identity fact. An Identity is defined in the factual model as:

fact Identity {
    string anonymousId;

    Claim* claims {
        Claim c : c.identity = this
    }
}

The anonymoudId string is the only field in an Identity. That means that it comprises the entire key of that fact. No matter how many times I construct an Identity with that anonymoudId, it will be the same instance. The Identity instance is created and added to the Community.

_community = new Community(storageStrategy)
    .AddAsynchronousCommunicationStrategy(new POXAsynchronousCommunicationStrategy(configurationProvider))
    .Register<CorrespondenceModule>()
    .Subscribe(() => _identity.Claims);

string anid = UserExtendedProperties.GetValue("ANID") as string;
string anonymousUserId = String.IsNullOrEmpty(anid)
    ? "test:user12"
    : "liveid:" + ParseAnonymousId(anid);
_identity = _community.AddFact(new Identity(anonymousUserId));

The first time you run the app, it creates this Identity instance. Every subsequent run is simply loading it from isolated storage.

Notice that I’m subscribing to _identity.Claims before the _identity is even instantiated. The subscription is a lambda expression, so it will be executed later. So what are Claims?

A user claims a name

A Claim is the desire for a person to have a specific user name. It is defined in factual as:

fact Claim {
    publish Identity identity;
    User user;
    publish IdentityService identityService;

    ClaimResponse* responses {
        ClaimResponse r : r.claim = this
    }

    bool isPending {
        not exists ClaimResponse r : r.claim = this
    }
}

The Claim refers to the Identity from the phone, and the User that is being claimed. A User is:

fact User {
    string userName;

    Claim* claims {
        Claim c : c.user = this
    }
}

Just like Identity, User has only one field. This field is its key, so any User object that I create with userName = “michael” will be the same instance. This is what makes it possible to challenge another user: the User instance I create on my phone will be the same as the one that he creates on his phone.

A service approves and denies claims

So when the user selects a name, a Claim is created. This Claim is published to an IdentityService, which is defined as:

fact IdentityService {
    Claim* pendingClaims {
        Claim c : c.identityService = this
            where c.isPending
    }
}

Notice that IdentityService has no fields. Since fields define identity in Correspondence, there is nothing to distinguish one IdentityService from another. Every time I create a new IdentityService, I get back the same instance. It is a singleton.

There is a subscriber on my server looking for Claims published to the IdentityService. When it finds a Claim, it verifies that the user name is not taken by another Identity. It will then generate a ClaimResponse:

fact ClaimResponse {
    publish Claim claim;
    int approved;
}

This response is published to the Claim. Remember the lambda expression in the Community Subscribe method? The phone has subscribed to all Claims attached to the Identity. That means that the ClaimResponse will be pushed to the phone, and the user will know whether it was accepted or rejected.

This is an example of the Service pattern in Historical Modeling. With this pattern, I can allow people to choose their own name, but still rely upon Windows Live to authenticate them for me.

Flip pieces on the way out

March 9th, 2011

I recently released Faceted Reversi, the first Windows Phone 7 application based on the Correspondence framework. Let me take a few posts to tell you how it works, and how you can build your own Correspondence application.

Faceted Reversi is, of course, a Reversi game. Reversi pieces have two sides: black and white. Each color represents one player. When a player moves, he places a piece with his color up. He then flips all of the enemy pieces that were flanked by that move.

When we describe the rules, we say that the player flips the pieces as he makes a move. This is in fact not how the code is written. On a players turn, all the program does is record the move. Pieces are flipped on the way out.

GameBoard and GameState

The game logic is controlled by two classes: GameBoard and GameState. A GameBoard is an immutable object that records a fixed board position. A GameBoard can tell you:

  • which color is on each square
  • who’s move it is
  • how many pieces of each color are on the board
  • what moves are legal
  • what the board would look like after a legal move

Or saying the same thing in code:

public class GameBoard
{
    public static GameBoard OpeningPosition { get; }
    public int MoveIndex { get; }
    public PieceColor PieceAt(Square square);
    public PieceColor ToMove { get; }
    public int BlackCount { get; }
    public int WhiteCount { get; }
    public IEnumerable<Square> LegalMoves { get; }
    public GameBoard AfterMove(Square square);
}

GameState, on the other hand, is mutable. It records the current GameBoard and lets the user make a move.

The separation of immutable state from mutable state is significant. It makes it clear that the board position is dependent upon the sequence of moves. We represent that dependency using Update Controls. The current position is governed by a Dependent sentry that runs UpdateGameBoard when it becomes out-of-date.

public class GameState
{
    private Game _game;

    private GameBoard _gameBoard;
    private Dependent _depGameBoard;

    public GameState(Game game)
    {
        _game = game;

        _depGameBoard = new Dependent(UpdateGameBoard);
    }

    public PieceColor PieceAt(Square square)
    {
        GameBoard gameBoard = GetGameBoard();
        return gameBoard.PieceAt(square);
    }

    private GameBoard GetGameBoard()
    {
        _depGameBoard.OnGet();
        return _gameBoard;
    }

    private void UpdateGameBoard()
    {
        _gameBoard = GameBoard.OpeningPosition;
        if (_game != null)
        {
            List<Move> moves = _game.Moves.ToList();
            moves.Sort(new LocalMoveComparer());
            int expectedIndex = 0;
            foreach (Move move in moves)
            {
                if (move.Index != expectedIndex)
                    return;
                if (move.Player.Index == 0 && _gameBoard.ToMove != PieceColor.Black)
                    return;
                if (move.Player.Index == 1 && _gameBoard.ToMove != PieceColor.White)
                    return;

                Square square = Square.FromIndex(move.Square);
                if (!_gameBoard.LegalMoves.Contains(square))
                    return;

                _gameBoard = _gameBoard.AfterMove(square);
                ++expectedIndex;
            }
        }
    }
}

UpdateGameBoard runs through the list of moves in the game, sorted by move index. It validates each move against the current position. If an invalid or out-of-place move is found, it just gives up. The assumption is that moves were validated before they were added to the game, but we don’t want to crash the app or enter an invalid state if something goes wrong.

Record a move

So what happens when the player makes a move? Simple:

public partial class Player
{
    public void MakeMove(int index, int square)
    {
        Community.AddFact(new Move(this, index, square));
    }
}

All we do is record that move. This adds it to the Moves collection in the game, stores it in the local database, sends it to the other player, and makes GameBoard out-of-date. Let’s break that down.

Publish a move to other subscribers

The Move fact is declared in a language called “factual”, specifically designed for Correspondence.

fact Move {
    Player player;
    int index;
    int square;
}

Adding a fact to the Correspondence community stores it in the local database. It also sends the fact to the server, which forwards it to all interested subscribers. We can tell that the other player is interested because he subscribed to the Game.

_community = new Community(storageStrategy)
    .AddAsynchronousCommunicationStrategy(new POXAsynchronousCommunicationStrategy(configurationProvider))
    .Register<CorrespondenceModule>()
    .Subscribe(() => _identity.ApprovedUsers
        .SelectMany(user => user.ActivePlayers)
        .Select(player => player.Game)
    );

The linq query in the Subscribe call returns all of the Game facts that the player is currently involved in. When a player subscribes to a game, the server will send him all of the published facts. A Player fact is published to a game, as indicated by the “publish” keyword in the factual model.

fact Player {
    publish User user;
    publish Game game;
    int index;
}

A Move fact belongs to a Player, so it is pushed as well. This is how Correspondence knows to send the move to the other player.

Update the UI when a move arrives

So when the move reaches the other player, how does the game know to refresh the board? The Game fact queries for related moves, again in the factual model.

fact Game {
    unique;

    Move* moves {
        Move m : m.player.game = this
    }
}

This makes _game.Moves a live collection. Whenever a fact is added, whether on this phone or from another one, this collection changes. Update Controls recognizes that GameBoard depends upon this collection. So when it changes, GameBoard becomes out-of-date, and Update Controls notifies the view to redraw itself through data binding.

All Correspondence applications work this way. State changes cannot be side-effects of a user action. Instead, the program simply has to store the user action into the model. State changes occur on the way out, back toward the user interface. They are dependent upon the history of user actions.

It doesn’t matter if the move comes from the user interface, from the local database, or from another user. Flipping the pieces is not a side-effect of making a move. It is dependent upon the sequence of moves, regardless where they come from.