Day 16: Reindeer Maze
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
C#
Ended up modifying part 1 to do part 2 and return both answers at once.
using System.Collections.Immutable; using System.Diagnostics; using Common; namespace Day16; static class Program { static void Main() { var start = Stopwatch.GetTimestamp(); var smallInput = Input.Parse("smallsample.txt"); var sampleInput = Input.Parse("sample.txt"); var programInput = Input.Parse("input.txt"); Console.WriteLine($"Part 1 small: {Solve(smallInput)}"); Console.WriteLine($"Part 1 sample: {Solve(sampleInput)}"); Console.WriteLine($"Part 1 input: {Solve(programInput)}"); Console.WriteLine($"That took about {Stopwatch.GetElapsedTime(start)}"); } static (int part1, int part2) Solve(Input i) { State? endState = null; Dictionary<(Point, int), int> lowestScores = new(); var queue = new Queue<State>(); queue.Enqueue(new State(i.Start, 1, 0, ImmutableHashSet<Point>.Empty)); while (queue.TryDequeue(out var state)) { if (ElementAt(i.Map, state.Location) is '#') { continue; } if (lowestScores.TryGetValue((state.Location, state.DirectionIndex), out var lowestScoreSoFar)) { if (state.Score > lowestScoreSoFar) continue; } lowestScores[(state.Location, state.DirectionIndex)] = state.Score; var nextStatePoints = state.Points.Add(state.Location); if (state.Location == i.End) { if ((endState is null) || (state.Score < endState.Score)) endState = state with { Points = nextStatePoints }; else if (state.Score == endState.Score) endState = state with { Points = nextStatePoints.Union(endState.Points) }; continue; } // Walk forward queue.Enqueue(state with { Location = state.Location.Move(CardinalDirections[state.DirectionIndex]), Score = state.Score + 1, Points = nextStatePoints, }); // Turn clockwise queue.Enqueue(state with { DirectionIndex = (state.DirectionIndex + 1) % CardinalDirections.Length, Score = state.Score + 1000, Points = nextStatePoints, }); // Turn counter clockwise queue.Enqueue(state with { DirectionIndex = (state.DirectionIndex + CardinalDirections.Length - 1) % CardinalDirections.Length, Score = state.Score + 1000, Points = nextStatePoints, }); } if (endState is null) throw new Exception("No end state found!"); return (endState.Score, endState.Points.Count); } public static void DumpMap(Input i, ISet<Point>? points, Point current) { for (int row = 0; row < i.Bounds.Row; row++) { for (int col = 0; col < i.Bounds.Col; col++) { var p = new Point(row, col); Console.Write( (p == current) ? 'X' : (points?.Contains(p) ?? false) ? 'O' : ElementAt(i.Map, p)); } Console.WriteLine(); } Console.WriteLine(); } public static char ElementAt(string[] map, Point location) => map[location.Row][location.Col]; public record State(Point Location, int DirectionIndex, int Score, ImmutableHashSet<Point> Points); public static readonly Direction[] CardinalDirections = [Direction.Up, Direction.Right, Direction.Down, Direction.Left]; } public class Input { public string[] Map { get; init; } = []; public Point Start { get; init; } = new(-1, -1); public Point End { get; init; } = new(-1, -1); public Point Bounds => new(this.Map.Length, this.Map[0].Length); public static Input Parse(string file) { var map = File.ReadAllLines(file); Point start = new(-1, -1), end = new(-1, -1); foreach (var p in map .SelectMany((line, i) => new [] { new Point(i, line.IndexOf('S')), new Point(i, line.IndexOf('E')), }) .Where(p => p.Col >= 0) .Take(2)) { if (map[p.Row][p.Col] is 'S') start = p; else end = p; } return new Input() { Map = map, Start = start, End = end, }; } }