Files
AdventOfCode/2022/Day09CSharp/Program.cs
2025-11-30 20:28:10 -05:00

264 lines
7.0 KiB
C#

// 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<Position> rope = new List<Position>();
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<Position, bool> visited = new Dictionary<Position, bool>();
List<Tuple<char, int>> simulationSteps = new List<Tuple<char, int>>();
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<char, int>(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);
}
}
}