Implementing John Conway’s Game of Life in Xamarin Forms

John Conway’s Game of Life, presented to the world in the October 1970 issue of Scientific American magazine, is one of the most popular computer simulations of all time. In it, you draw a pattern of cells in a 2-dimensional grid. Then you “evolve” the cells from one generation to the next. Whether a cell lives, dies, or is “born” in the next generation depends on a simple set of genetic rules specified by Conway. A live cell with two or fewer neighbors dies from starvation in the next generation, while a live cell with five or more neighbors dies from overpopulation. Meanwhile, a cell that isn’t alive in the current generation comes alive in the next if it has precisely three neighbors. From this simple rule set arises a cellular automata game of nearly infinite scope and complexity.

I’ve implemented the Game of Life on multiple platforms over the years, including Silverlight, Windows, and Windows Phone. Thus, it seemed natural to write an implementation for other platforms, too. And what better way to do it than write it in Xamarin Forms? That way, it can work on Windows Phone, Android, and iOS, too.

Writing a Xamarin Forms version of the game presented a few challenges, not the least of which was figuring out how to detect when a finger dragging across the screen enters a grid cell, as described in my previous article. But with a little determination, the app, which I call MonoLife, came together pretty quickly. Here’s the result, with the UI populated with a bunch of cells for good measure. You toggle cells on and off by dragging a finger across the screen (or simply tapping individual cells if you prefer). You can tap the Start button to start a simulation running, and the Stop button to stop a simulation. The number of rows and columns in the simulation depends on the size of the screen you run it on, because at startup, MonoLife measures the screen real estate available to it and fills it with as many rows and columns as it can. You can download the source code and run a few simulations yourself. Or you can read on and learn more about how it works, and about some cool features of Xamarin Forms that you might not be aware of already.

MonoLife

Drawing Cells and Responding to Touch Input

The “cells” you see in the app’s UI are little more than Xamarin BoxViews. At run-time, code in MainPage.xaml.cs measures the amount of screen space available and dynamically adds rows and columns to an empty Grid declared in MainPage.xaml. Then it creates one instance of SmartBoxView – an enhanced version of BoxView – per grid cell and adds the boxes to the grid:

// Compute number of rows and columns
var cols = (int)(width / cx);
var rows = (int)(height / cy);

// Add rows and columns to the Grid
for (int i = 0; i < cols; i++)
    SmartGrid.ColumnDefinitions.Add(new ColumnDefinition());

for (int j = 0; j < rows; j++)
    SmartGrid.RowDefinitions.Add(new RowDefinition());

// Fill the Grid cells with SmartBoxViews
for (int i = 0; i < cols; i++)
{
    for (int j = 0; j < rows; j++)
    {
        var box = new SmartBoxView();
        box.Color = Color.Blue;
        box.Opacity = _opacity;
        box.SetValue(Grid.RowProperty, j);
        box.SetValue(Grid.ColumnProperty, i);
        box.Tag = ((j + (_xrows / 2)) * (cols + _xcols)) + i + (_xcols / 2);
        box.Entered += OnBoxEntered;
        SmartGrid.Children.Add(box);
    }
}

I populated the grid with SmartBoxViews because SmartBoxView fires Entered events when a finger touches down over a grid cell or slides into it, and Exited events when a finger comes up over a grid cell or slides out of it. MonoLife doesn’t use Exited events, but it does use Entered events to toggle cells on and off when a finger moves across them. (You can read all about SmartBoxView and the custom renderers that drive it here.) I did make one change to SmartBoxView before using it in MonoLife: I added a Tag property of type Object to allow arbitrary data to be associated with each grid cell. Microsoft XAML developers are familiar with the Tag property; it’s present on every visual element and proves extremely handy at times. I duplicated it in SmartBoxView:

public class SmartBoxView : BoxView
{
    public object Tag { get; set; }
    public event EventHandler Entered;
    public event EventHandler Exited;

    public void RaiseEntered()
    {
        if (Entered != null)
            Entered(this, EventArgs.Empty);
    }

    public void RaiseExited()
    {
        if (Exited != null)
            Exited(this, EventArgs.Empty);
    }
}

Each SmartBoxView’s Tag property is initialized with an index representing the position of that cell in a state array maintained by the view-model. When an Entered event fires and the cell under the finger is toggled on or off (a visual trick that’s accomplished by changing the SmartBoxView’s opacity), the view communicates the state change to the view-model by calling the view-model’s UpdateCell method:

private void OnBoxEntered(object sender, EventArgs e)
{
    if (!_vm.IsRunning)
    {
        var box = (SmartBoxView)sender;

        // Toggle the cell on or off in the view
        box.Opacity = (box.Opacity < 1.0) ? 1.0 : _opacity;

        // Communicate the change to the view model
        _vm.UpdateCell((int)box.Tag, box.Opacity == 1.0);
    }
}

Passing an index rather than, say, a row and column is purely a performance measure. Rather than have to compute the position of the corresponding element in the state array, the view-model can use the index to go directly to that position.

MonoLife is an MVVM app, but the connection between the state array in the view-model and the cells in the view is handled programmatically, not through data binding. You can’t bind an array of Booleans to a Grid full of boxes without writing a custom control, and even with a custom control, you’d have to be careful about performance. For this reason, I elected to make targeted calls between the view and view-model conveying just the right amount of information rather than abstract communications between the two through binding objects.

Toolbars and Toolbar Buttons

Ever wanted to include a toolbar in a Xamarin Forms app? It’s easy to do – once you know how. Don’t look for a Toolbar control; it’s not there. Instead, use the ToolbarItems property of the Xamarin Forms page. In MainPage.xaml, here’s how I declared the toolbar containing Start and Stop buttons – the one that appears at the top of the screen in iOS and Android and the bottom of the screen in Windows Phone:

<ContentPage.ToolbarItems>
  <ToolbarItem Text="Start" Command="{Binding StartCommand}">
    <ToolbarItem.Icon>
      <OnPlatform x:TypeArguments="FileImageSource" WinPhone="Toolkit.Content/Play.png" />
    </ToolbarItem.Icon>
  </ToolbarItem>
  <ToolbarItem Text="Stop" Command="{Binding StopCommand}">
    <ToolbarItem.Icon>
      <OnPlatform x:TypeArguments="FileImageSource" WinPhone="Toolkit.Content/Pause.png" />
    </ToolbarItem.Icon>
  </ToolbarItem>
</ContentPage.ToolbarItems>

Each button is represented by a ToolbarItem, which fires a Click event when clicked or executes a command if a command has been bound to the button’s Command property. A toolbar can contain up to four ToolbarItems, and each ToolbarItem exposes the following properties:

  • Text, which controls the text shown for the ToolbarItem
  • Icon, which controls the icon (if any) shown for the ToolbarItem
  • Priority, which controls the left-to-right ordering of ToolbarItems (by default, they’re arranged in the order in which they’re declared or added to the toolbar)
  • Order, which can be set to “Primary” (the default) to display ToolbarItems like the ones in MonoLife, or “Secondary” to place the ToolbarItems in popup menus in Windows Phone and Android and in a secondary toolbar in iOS

You’re never required to provide icon images for toolbar buttons, but on Windows Phone you’ll want to, or else Windows Phone will display placeholder images instead. The Toolkit.Content folder in your Windows Phone project contains a handful of images you can start with, but you can get more from the Windows Phone SDK. In MonoLife, I imported a couple of images named Play.png and Pause.png into Toolkit.Content and referenced them in my ToolbarItems.

In iOS and Android, a toolbar won’t appear on the screen unless you wrap the page containing the toolbar in a NavigationPage. Accordingly, in App.cs, I wrapped a NavigationPage around an instance of MainPage and assigned it to the application’s MainPage property:

public App()
{
    this.MainPage = new NavigationPage(new MainPage());
}

A final note regarding toolbar buttons has to do with enabling and disabling them. Currently, ToolbarItem lacks an IsEnabled property allowing toolbar buttons to be explicitly enabled and disabled. This was a problem in MonoLife, because I wanted the Stop button to be disabled when a simulation isn’t running, and the Start button to be disabled while a simulation is running. The solution was commanding. When you bind a toolbar button to a command object (an object that implements ICommand), the command object includes a CanExecute method and a CanExecuteChanged event. If you implement these members properly, returning true or false from CanExecute indicating whether the button should be enabled or disabled, and firing a CanExecuteChanged event when CanExecute changes, Xamarin Forms will honor that and not execute the button’s command if the button is disabled (that it, if CanExecute returns false). Unfortunately, a disabled button doesn’t render any differently; it isn’t grayed out. I suspect that will change in a future version of Xamarin Forms, because it’s an obvious miss that probably flew under the radar in current releases.

Running a Timer (Device.StartTimer)

One of the most underrated classes in Xamarin Forms is the Xamarin.Forms.Device class. It’s most often used to initialize values or execute code on a per-platform basis via its OnPlatform method, but it can also used to find out what operating system you’re running on (Device.OS), distinguish between phones and tablets (Device.Idiom), and marshal from a background thread to the application’s UI thread (Device.BeginInvokeOnMainThread). And it can be used to perform timer-based tasks via StartTimer.

Clicking the Start button in MonoLife executes a StartCommand, which in turn calls a method in the view-model to start a simulation. That method programs a timer to fire every 50 milliseconds. In each timer tick, the cells are evolved one generation and the view is updated to show the results:

Device.StartTimer(new TimeSpan(0, 0, 0, 0, 50), () =>
{
    // Stop now if the Stop button was clicked
    if (_halt)
    {
        _running = false;
        _start.RaiseCanExecuteChanged();
        _stop.RaiseCanExecuteChanged();
        return false;
    }

    // Evolve the cells another generation
    var changed = Evolve();

    // Stop if nothing changed between generations
    if (!changed)
    {
        _running = false;
        _start.RaiseCanExecuteChanged();
        _stop.RaiseCanExecuteChanged();
        return false;
    }

    // Otherwise update the view and continue for
    // another generation
    if (_view != null)
        ((MainPage)_view).UpdateView(_activeCells);

    return true;
});

There is no StopTimer method to complement Device.StartTimer. Instead, you stop the timer by returning false from the timer callback. When the user taps the Stop button, a StopCommand executes and calls over to the view-model, which sets a flag named _halt to true:

_halt = true;

That causes the timer callback to return false next time around and stop the timer. The timer is also stopped if the cells reach a steady state – that is, there is no change in the cells from one generation to the next.

Lifecycle Management

As I wrote in Writing Cross-Platform Mobile Apps with Visual Studio 2015 and Xamarin Forms, Xamarin Forms 1.3 added three virtual methods to the Application class to help developers respond to lifecycle events in a platform-agnostic manner:

  • OnStart, which is called when an app is launched or relaunched following termination
  • OnSleep, which is called when the app leaves the foreground, or is “deactivated” in Windows Phone parlance
  • OnResume, which is called when the app returns to the foreground after being deactivated (but not if the app was relaunched)

MonoLife overrides OnStart and OnSleep to preserve the state of the app between starts. Here are the overrides in the PCL project’s App.cs:

protected override void OnStart()
{
    if (Application.Current.Properties.ContainsKey(_key))
    {
        string state = (string)Application.Current.Properties[_key];
        _vm.SetState(Deserialize<LifeState>(state));
    }
}

protected override void OnSleep()
{
    string state = Serialize<LifeState>(_vm.GetState());
    Application.Current.Properties[_key] = state;
}

LifeState is a class that holds the app state, and Serialize and Deserialize are helper methods that serialize objects into JSON strings and deserialize them back. The view-model manages the state and therefore contains the methods called from App.cs to get and set the state. Try it: draw a pattern of cells. Then shut down the app and start it again. It should come up just as it was before. More importantly, the fact that it preserves state this way ensures that if the user switches away from the app for a moment and the operating system decides to shut it down, the app will be restored to its previous state if the user switches back to it.

Incidentally, in Xamarin Forms 1.3, values written to Application.Properties were automatically written to persistent storage when the app shut down, but there was no way to force them to be committed earlier. Version 1.4 adds a method named SavePropertiesAsync to the Application class to write the contents of Application.Properties to storage immediately. One use for the new method is to persist values on a page when the user navigates from one page to another. Even if the app crashes, the values will be there the next time the app starts up.

Summary

I’ve published a number of articles on Xamarin Forms in recent weeks and have still more planned. This might be a good time to provide links to all the ones published thus far in case you’re looking for a quick start on this exciting new platform and care to read the articles in order:

Much more to come. The fun of using XAML to build cross-platform mobile apps has just begun!

Xamarin-Partner_thumb1_thumb_thumb

Build your next mobile application with Wintellect and Xamarin!

Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X