Global Day of CodeRetreat
December 05, 2011 -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.