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.