// See https://aka.ms/new-console-template for more information using System.Reflection; Part01(); Part02(); void Part01() { Simulation exampleSim = new Simulation(2); Simulation puzzleSim = new Simulation(2); exampleSim.LoadDataFile("example-input.txt"); exampleSim.SimulateAll(0, 5, 0, 5); Console.WriteLine("Part01, Example Visited: " + exampleSim.TailVisitedCount); puzzleSim.LoadDataFile("puzzle-input.txt"); puzzleSim.SimulateAll(-50, 50, -50, 50); Console.WriteLine("Part01, Puzzle Visited: " + puzzleSim.TailVisitedCount); } void Part02() { Simulation exampleSim = new Simulation(10); Simulation example2Sim = new Simulation(10); Simulation puzzleSim = new Simulation(10); exampleSim.LoadDataFile("example-input.txt"); exampleSim.SimulateAll(0, 6, 0, 5, false); Console.WriteLine("Part02, Example Visited: " + exampleSim.TailVisitedCount); example2Sim.LoadDataFile("example-input2.txt"); example2Sim.SimulateAll(-15,15,-10,10, false); Console.WriteLine("Part02, Example2 Visited: " + example2Sim.TailVisitedCount); puzzleSim.LoadDataFile("puzzle-input.txt"); puzzleSim.SimulateAll(-50, 50, -50, 50); Console.WriteLine("Part02, Puzzle Visited: " + puzzleSim.TailVisitedCount); } class Position { public int x; public int y; public Position() : this(0,0) { } public Position(int x, int y) { this.x = x; this.y = y; } public Position(Position other) : this(other.x, other.y) { } public override bool Equals(object? obj) { if (!(obj is Position)) return false; Position p = (Position)obj; return p.x == x & p.y == y; } public override int GetHashCode() { return x ^ y; } public double distanceFrom(Position p) { return Math.Sqrt(Math.Pow(p.x - x, 2) + Math.Pow(p.y - y, 2)); } public void distanceFrom(Position p, out int x, out int y) { x = p.x - this.x; y = p.y - this.y; } } class RopeBridge { public Position Head { get { return rope[0]; } } public Position Tail { get { return rope[rope.Count - 1]; } set { rope[rope.Count - 1] = value; } } List rope = new List(); public RopeBridge(int bridgeLength) { for (int i = 0; i < bridgeLength; i++) { rope.Add(new Position()); } } private void UpdateKnot(Position parent, int index) { double distance = rope[index - 1].distanceFrom(rope[index]); if (distance >= 2) { int x, y; rope[index].distanceFrom(parent, out x, out y); if (x > 0) rope[index].x++; if (y > 0) rope[index].y++; if (x < 0) rope[index].x--; if (y < 0) rope[index].y--; // Only update if it actually moved if (index + 1 < rope.Count) { UpdateKnot(rope[index], index + 1); } } } public void MoveHead(char direction) { Position oldHead = new Position(Head); switch (direction) { case 'U': Head.y++; break; case 'D': Head.y--; break; case 'L': Head.x--; break; case 'R': Head.x++; break; default: throw new InvalidDataException(); } UpdateKnot(Head, 1); } public void PrintBridge(int x_min, int x_max, int y_min, int y_max) { for (int j = y_max; j >= y_min; j--) { for (int i = x_min; i < x_max; i++) { var index = rope.IndexOf(new Position(i, j)); if (index != -1) { var query = rope.Where(pos => pos.x == i && pos.y == j); if (query.Count() > 1) { for (index = 0; index < rope.Count; index++) { if (rope[index].x == i && rope[index].y == j) { break; } } } if (index == 0) { Console.Write('H'); } else if (index + 1 == rope.Count) { Console.Write('T'); } else { Console.Write(index); } } else { Console.Write('.'); } } Console.WriteLine(); } Console.WriteLine(); } } class Simulation { RopeBridge bridge; Dictionary visited = new Dictionary(); List> simulationSteps = new List>(); public Simulation(int bridgeLength) { bridge = new RopeBridge(bridgeLength); } public void LoadDataFile(string filename) { using (StreamReader reader = System.IO.File.OpenText(filename)) { while (!reader.EndOfStream) { string? line = reader.ReadLine(); if (line == null) throw new InvalidDataException(); var split = line.Split(' '); if (!Char.IsAsciiLetter(split[0][0])) throw new InvalidDataException(); if (!Char.IsAsciiDigit(split[1][0])) throw new InvalidDataException(); simulationSteps.Add(new Tuple(split[0][0], Int32.Parse(split[1]))); } } } public int TailVisitedCount { get { return visited.Count; } } public void SimulateAll(int x_min, int x_max, int y_min, int y_max, bool print = false) { if (print) bridge.PrintBridge(x_min, x_max, y_min, y_max); foreach (var pair in simulationSteps) { if (print) Console.WriteLine(String.Format(" === {0} {1} ===", pair.Item1, pair.Item2)); Simulate(pair.Item1, pair.Item2, x_min, x_max, y_min, y_max, print); } } public void Simulate(char direction, int count, int x_min, int x_max, int y_min, int y_max, bool print = false) { // Visit initial state always visited.TryAdd(new Position(bridge.Tail), true); for (int i = 0; i < count; i++) { bridge.MoveHead(direction); visited.TryAdd(new Position(bridge.Tail), true); if (print) bridge.PrintBridge(x_min, x_max, y_min, y_max); } } }