Most of the team went along to the Perth Global Day of CodeRetreat at the Thoughtworks office on Saturday; I’d been interested in going to one for a while, so when a local event popped up, I was there. Most of the pairs were split 50/50 on Java/C# — conservative bunch we are in Perth — although there were some JavaScript pairs and I participated in an Objective-C one (with limited success).
Overall I found the experience quite worthwhile, but I did find it frustrating to attack the same problem (Conway’s Game of Life) over and over, and never finish it. So I went home and finished my last attempt.
This implementation adheres to most of the constraints we attempted at various stages during our event:
- No control of flow statements (no if, else, switch, do, while, loop, for, foreach etc)
- No Cell class
- Infinite Grid
- Methods no longer than 3 lines of code
It consists of one class and one struct, first a helper Point struct:
public struct Point { public int X { get; private set; } public int Y { get; private set; } public Point(int x, int y) : this() { X = x; Y = y; } public override bool Equals(object obj) { return X == ((Point)obj).X && Y == ((Point)obj).Y; } public static bool operator == (Point p1, Point p2) { return p1.Equals(p2); } public static bool operator != (Point p1, Point p2) { return !p1.Equals(p2); } public override int GetHashCode() { return X.GetHashCode() & Y.GetHashCode(); } public IEnumerable<Point> AdjacentPoints { get { return new List<Point> { new Point(X - 1, Y - 1), new Point(X - 1, Y), new Point(X - 1, Y + 1), new Point(X, Y - 1), new Point(X, Y + 1), new Point(X + 1, Y - 1), new Point(X + 1, Y), new Point(X + 1, Y + 1) }; } } }
and then the main ‘World’ class:
public class World { private List<Point> liveCells; public World(List<Point> liveCells) { this.liveCells = new List<Point>(liveCells); } public int LiveCellCount { get { return liveCells.Count(); } } public void Tick() { liveCells = liveCells.Where(p => NumberOfLiveNeighbours(p) == 2 || NumberOfLiveNeighbours(p) == 3) // stay alive .Union(AdjacentDeadCells.Where(p => NumberOfLiveNeighbours(p) == 3)) // become alive .ToList(); } private int NumberOfLiveNeighbours(Point point) { return liveCells.Intersect(point.AdjacentPoints).Count(); } private IEnumerable<Point> AdjacentDeadCells { get { return liveCells.SelectMany(p => p.AdjacentPoints).Except(liveCells).Distinct(); } } }
This is cheating slightly as the framework LINQ queries are probably implemented as foreaches, but technically I haven’t used any control of flow. The code is also not as easily testable as some of the iterations I went through during the CodeRetreat, but there is significantly less of it. I think there can be scenarios where attempting to modularise something can introduce too many interfaces to keep the code tight & concise.