0 Comments

So I’m still just about managing to keep up with the Advent of Code challenges. Here’s me talking through my solution to day 6’s problem:

Here’s my solution to part a done in three stages in C#

var instructions = File.ReadAllLines("day6.txt");

var pattern = @"(turn on|toggle|turn off)\ (\d+)\,(\d+)\ through\ (\d+)\,(\d+)";

var instructionsFlattened = instructions
    .Select(i => Regex.Match(i, pattern).Groups)
    .Select(g => new
    {
        Action = g[1].Value,
        From = new { 
            X = int.Parse(g[2].Value), 
            Y = int.Parse(g[3].Value) },
        To = new { 
            X = int.Parse(g[4].Value), 
            Y = int.Parse(g[5].Value) }
    })
    .SelectMany(i =>
        from x in Enumerable.Range(i.From.X, 1 + i.To.X - i.From.X)
        from y in Enumerable.Range(i.From.Y, 1 + i.To.Y - i.From.Y)
        select new { i.Action, x, y });

// apply the instructions
var lights = new bool[1000, 1000];
foreach (var i in instructionsFlattened)
{    
    if (i.Action == "turn on") 
        lights[i.x, i.y] = true;
    else if (i.Action == "turn off")
        lights[i.x, i.y] = false;
    else if (i.Action == "toggle")
        lights[i.x, i.y] = !lights[i.x, i.y];
}

// count the lights on
(from x in Enumerable.Range(0, 1000)
 from y in Enumerable.Range(0, 1000)
 where lights[x, y]
 select 1).Sum().Dump("lights on");

But I decided I wanted to solve this in a single LINQ expression, and this is possible using LINQ’s Aggregate method, so here’s part b solved in C# with Aggregate

File.ReadAllLines("day6.txt")
    .Select(i => Regex.Match(i,
        @"(turn on|toggle|turn off)\ (\d+)\,(\d+)\ through\ (\d+)\,(\d+)").Groups)
    .Select(g => new
    {
        Action = g[1].Value,
        From = new { X = int.Parse(g[2].Value), Y = int.Parse(g[3].Value) },
        To = new { X = int.Parse(g[4].Value), Y = int.Parse(g[5].Value) }
    })
    .SelectMany(i =>
        from x in Enumerable.Range(i.From.X, 1 + i.To.X - i.From.X)
        from y in Enumerable.Range(i.From.Y, 1 + i.To.Y - i.From.Y)
        select new { i.Action, x, y })
    .Aggregate(new int[1000,1000], (acc, next) => {
        var brightness = acc[next.x,next.y];
        if (next.Action == "turn on") 
            brightness+=1;
        else if (next.Action == "turn off") 
            brightness = Math.Max(0, brightness - 1);
        else if (next.Action == "toggle") 
            brightness += 2;
        acc[next.x, next.y] = brightness;
        return acc;
        })
    .Cast<int>() // flattens a multi-dimensional array
    .Sum()
// brightness is 14687245

And finally of course, as part of my on-going bid to improve my F# skills, I made an F# version of this solution:

let instructions = File.ReadAllLines("day6.txt")
let turnOn n = n + 1
let turnOff n = max (n - 1) 0
let toggle n = n + 2

let selectAction = function 
    | "turn on" -> turnOn 
    | "turn off" -> turnOff
    | "toggle" -> toggle

let parseInstruction actionSelector i =
    let pattern = @"(turn on|toggle|turn off)\ (\d+)\,(\d+)\ through\ (\d+)\,(\d+)"
    let groups = Regex.Match(i, pattern).Groups
    let action = actionSelector groups.[1].Value
    let fromPos = (int groups.[2].Value), (int groups.[3].Value)
    let toPos = (int groups.[4].Value), (int groups.[5].Value)
    (action, fromPos, toPos)
    
let expandPositions (x0,y0) (x1,y1) =
    seq { for x in x0 .. x1 do
          for y in y0 .. y1 do
          yield (x,y) }

let applyAction (acc:int[,]) (action,(x,y)) =
    acc.[x,y] <- action acc.[x,y]
    acc

let calculate actionSelector (input:string[]) =
    let startState = Array2D.create 1000 1000 0
    input
        |> Seq.map (parseInstruction actionSelector)
        |> Seq.collect (fun (action,fromPos,toPos) -> 
            seq { for pos in (expandPositions fromPos toPos) do
                    yield (action,pos) })
        |> Seq.fold applyAction startState
        |> Seq.cast<int> // same trick works to flatten 2d array
        |> Seq.sum

calculate selectAction instructions |> Dump

let turnOnA n = 1
let turnOffA n = 0
let toggleA n = if n = 0 then 1 else 0

let selectActionA = function
    | "turn on" -> turnOnA
    | "turn off" -> turnOffA
    | "toggle" -> toggleA

calculate selectActionA instructions |> Dump
As always, comments on ways I could improve my solutions are very welcome.
Want to learn more about LINQ? Be sure to check out my Pluralsight course More Effective LINQ.
Vote on HN
comments powered by Disqus