Monday, August 6, 2012

Learning MVC: Game of Life in MVC

I wrote some code and made a quick WPF application that implemented Conway's Game of Life earlier ( Game of Life Exercise and Extension Methods).

Next, I wanted to see how the game could be run on the MVC platform. The solution in short: use javaScript setInterval function to load the partial view into the div. Use a method in the controller to generate the partial view.

Here is how my solution looked:

public class GameController : Controller
{
    public ActionResult Index()
    {
        GameOfLifeHelpers.InitializeGame();
        return View(NextIteration());
    }

    [OutputCache(NoStore = true, Location = OutputCacheLocation.Client, Duration = 1)]
    public ActionResult Update()
    {
        return PartialView("_Table", NextIteration());
    }

    public HtmlString NextIteration()
    {
        GameOfLifeHelpers.DrawNextIteration();
        return new HtmlString(GameOfLifeHelpers.table.ToString());
    }
}

The partial view is called _Table and is nothing more than the HtmlString. Here is the partial view:

@model HtmlString
           
@{ Layout = null; }

@Model

The model is just the HtmlString which gets rendered, and the HtmlString itself is just a simple table of a specified number of cells. And here is the Index.cshtml

<script type="text/javascript">
    setInterval("$('#update').load('/Game/Update')", 1000);
</script>

@model HtmlString

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
<div id="update">@{Html.RenderPartial("_Table", Model);}</div>

Note how the interval is set to 1000 ms and the OutputCache duration in the controller is set to the same value. Every second the call to load will return a partial view from the Update method. What does the Update method return? When the game starts, and empty html table is created with each cell having a blue background.

public static void NewGameTable()
{
    table = new StringBuilder(@"<table border=1 bordercolor=black cellspacing=0 cellpadding=0>");
    for (int i = 0; i < y; i++)
    {
        table.Append("<tr>");
        for (int j = 0; j < x; j++)
        {
            table.Append("<td width=10px height=10px bgcolor=#0276FD></td>");
        }
        table.Append("</tr>");
    }
    table.Append("</table>");
}

Then, on each iteration, a new boolead array is filled to specify which cells will be "alive".

public static void DrawNextIteration()
{
    bool[,] arrCurrent = counter % 2 == 0 ? arrOne : arrTwo;
    bool[,] arrNext = counter % 2 == 0 ? arrTwo : arrOne;
    FillArray(arrNext, arrCurrent);
    counter++;
    for (int i = 0; i < y; i++)
    {
        for (int j = 0; j < x; j++)
        {
            if (arrNext[i, j] != arrCurrent[i, j])
            {
                table = arrNext[i, j] ? GameOfLifeTableReplaceCell(i, j, "#FF0000", table) : GameOfLifeTableReplaceCell(i, j, "#0276FD", table);
            }
        }
    }
}

The function that replaces the cell is very simple - it calculates the position where the font for the cell is specified based on the coordinates and makes the cell color red if it went from "dead" to "alive", and vice versa.

public static StringBuilder GameOfLifeTableReplaceCell(int i, int j, string colour, StringBuilder sb)
{
    const int rowLength = 48*x + 9;
    const int cellLength = 48;
    int start = 62 + i * rowLength + 4 + j * cellLength + 35;
    sb.Remove(start, 7);
    sb.Insert(start, colour);
    return sb;
}

The rest of the code is omitted because it can be found in my earlier post and used with little or no change.

by . Also posted on my website

No comments: