Advent of Code Day 3 Solved in C# and F#
December 8. 2015 Posted in:
OK, here’s my solution to Advent of Code day 3:
C# part a (using Scan from MoreLINQ):
File.ReadAllText("day3.txt")
.Scan(new { x = 0, y = 0 }, (state, c) =>
c == '>' ? new { x = state.x + 1, y = state.y } :
c == '^' ? new { x = state.x, y = state.y + 1 } :
c == '<' ? new { x = state.x - 1, y = state.y } :
new { x = state.x, y = state.y - 1 })
.Select(p => String.Format("{0},{1}", p.x, p.y))
.GroupBy(p => p)
.Count()
C# part b:
void Main()
{
File.ReadAllText("day3.txt")
.Batch(2)
.Scan(new { Santa = new Pos(0, 0), RoboSanta = new Pos(0, 0) }, (state, c) =>
new { Santa = Move(c.First(), state.Santa),
RoboSanta = Move(c.Last(), state.RoboSanta)
})
.SelectMany(p => new[] { p.Santa, p.RoboSanta } )
.Select(p => String.Format("{0},{1}", p.X, p.Y))
.GroupBy(p => p)
.Count()
.Dump();
}
public class Pos
{
public Pos(int x, int y)
{
X = x; Y = y;
}
public int X { get; }
public int Y { get; }
}
Pos Move(char direction, Pos startingPoint)
{
return
direction == '>' ? new Pos(startingPoint.X + 1, startingPoint.Y) :
direction == '^' ? new Pos(startingPoint.X, startingPoint.Y + 1) :
direction == '<' ? new Pos(startingPoint.X - 1, startingPoint.Y) :
new Pos(startingPoint.X, startingPoint.Y - 1);
}
F# part a:
File.ReadAllText("day3.txt")
|> Seq.map (fun c -> match c with | '>' -> (1,0) | '^' -> (0,1) | '<' -> (-1,0) | _ -> (0,-1))
|> Seq.scan (fun (x1,y1) (x2,y2) -> (x1 + x2, y1 + y2)) (0,0)
|> Seq.distinct
|> Seq.length
F# part b:
let getVector c = match c with | '>' -> (1,0) | '^' -> (0,1) | '<' -> (-1,0) | _ -> (0,-1)
let addVector (x1,y1) (x2,y2) = (x1 + x2, y1 + y2)
let directions =
File.ReadAllText("day3.txt")
let startState = ((0,0),(0,0),0)
let update (santa,roboSanta,index) nextDir =
if (index % 2) = 0 then
((addVector santa nextDir), roboSanta, index+1)
else
(santa, (addVector roboSanta nextDir), index+1)
directions
|> Seq.map getVector
|> Seq.scan update startState
|> Seq.map (fun (santa,roboSanta,index) -> if index % 2 = 0 then santa else roboSanta)
|> Seq.distinct
|> Seq.length
|> Dump
Want to learn more about LINQ? Be sure to check out my Pluralsight course LINQ Best Practices.
Comments
You can replace the
by
That said we have the same logic ; just written differently (I use a set to replace your distinct)
The other difference is for separating santa to robot-santa ; I insert items in two list swapping them after each insertion (effectively getting odd/even separation) Sehnsucht
nice, didn't know about set - that will come in handy for today's one. might refactor if I get a chance
Mark Heathhow to use Scan in Linqpad?
amalgaI did some search and i didnt find anyway to add MoreLinq to Linqpad
any help please?
Unfortunately you do need a developer license to use NuGet packages with LinqPad. I think its quite reasonably priced, but for a free alternative, you can use the community edition of Visual Studio to try these problems.
Mark HeathOk i gonna try Visual Studio, Thanks alot ^^
amalgaYour c# solution for part b has a bug. If the input has an odd number of moves then using Last() for RoboSantas moves makes an uncommanded move on the last batch, which has only one item. Depending on the prior move instructions this may or may not result in the final answer being out by one.
Steve CraneTo work around this I used
Robot = m.Skip(1).Any() ? l.Robot.Move(m.Last()) : l.Robot
Can you suggest a better way?
yes, good spot, it does assume the instructions will always be in pairs. Shouldn't be using Last() without checking that it is a two element batch (could insert another select turning each batch into an array, allowing checking of length)
Mark Heath