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

428 lines
11 KiB
C#

// See https://aka.ms/new-console-template for more information
using System.Net.Http.Headers;
using System.Reflection;
Part01();
void Part01()
{
Console.WriteLine("!! === Part 01 === !!");
Console.WriteLine("=== Example ===");
var exampleData = new VolcanoData("example-input.txt");
Console.WriteLine("Most Pressure Released: {0}", exampleData.GetMostPossiblePressureReleased(31));
Console.WriteLine("=== Puzzle ===");
var puzzleData = new VolcanoData("puzzle-input.txt");
Console.WriteLine("Most Pressure Released: {0}", puzzleData.GetMostPossiblePressureReleased(31));
}
class StepList
: IComparable
{
public struct Step
{
public enum StepType
{
First,
Move,
Open
}
public StepType Type { get; set; }
public Tuple<string,string> Value { get; set; }
public Step()
{
Type = StepType.First;
Value = new Tuple<string, string>("AA", "0");
}
public Step(string sourceValve, string targetValve)
{
Type = StepType.Move;
Value = new Tuple<string, string>(sourceValve, targetValve);
}
public Step(string valvename, int stepNumber)
{
Type = StepType.Open;
Value = new Tuple<string, string>(valvename, stepNumber.ToString());
}
}
private List<Step> Steps { get; }
private Dictionary<string, bool> OpenedValves { get; }
private IReadOnlyDictionary<string, Valve> Valves { get; }
public bool isValveOpen(string valveName)
{
return OpenedValves.ContainsKey(valveName);
}
public StepList(IReadOnlyDictionary<string, Valve> valves, int maxSteps = 31)
{
MaxSteps = maxSteps;
Valves = valves;
OpenedValves = new Dictionary<string, bool>();
Steps = new List<Step>();
}
public StepList(StepList other)
{
MaxSteps = other.MaxSteps;
Valves = other.Valves;
OpenedValves = new Dictionary<string, bool>();
Steps = new List<Step>();
foreach (var item in other.OpenedValves)
{
OpenedValves[item.Key] = item.Value;
}
foreach (var item in other.Steps)
{
Steps.Add(item);
}
}
public int MaxSteps { get; }
public int StepCount
{
get
{
return Steps.Count;
}
}
public bool CanContinue
{
get
{
return StepCount < MaxSteps;
}
}
public Step Latest
{
get
{
if (Steps.Count == 0) return new Step();
return Steps[Steps.Count - 1];
}
}
public bool AddStep(Step step)
{
if (StepCount < MaxSteps)
{
Steps.Add(step);
if (step.Type == Step.StepType.Open)
{
if (OpenedValves.ContainsKey(step.Value.Item1)) throw new InvalidDataException();
OpenedValves[step.Value.Item1] = true;
}
return true;
}
return false;
}
public int GetPressureReleased()
{
int total = 0;
for (int i =0; i< Steps.Count; i++)
{
var step = Steps[i];
if (step.Type == Step.StepType.Open)
{
int timeOpen = MaxSteps - (Int32.Parse(step.Value.Item2)+1);
total += Valves[step.Value.Item1].FlowRate * timeOpen;
}
}
return total;
}
public int CompareTo(object? obj)
{
var other = obj as StepList;
if (other == null) throw new ArgumentException();
return this.GetPressureReleased().CompareTo(other.GetPressureReleased());
}
public static StepList GenerateBestPath(StepList sl)
{
if (!sl.CanContinue)
return sl;
var lastStep = sl.Latest;
Valve valve = sl.Valves[lastStep.Value.Item1];
List<StepList> paths = new List<StepList>();
paths.Add(sl);
// Just did a move, see if we need to open this valve
if (lastStep.Type == StepList.Step.StepType.Move || lastStep.Type == Step.StepType.First)
{
// if it was a move, then our current valve is the target
if (lastStep.Type == StepList.Step.StepType.Move)
valve = sl.Valves[lastStep.Value.Item2];
// Valve isnt open and maybe should be
if (!sl.isValveOpen(valve.Name) && valve.FlowRate > 0)
{
// Open valve path
{
StepList copy = new StepList(sl);
copy.AddStep(new Step(valve.Name, sl.StepCount));
copy = GenerateBestPath(copy);
paths.Add(copy);
}
// leave it closed path
foreach (var tunnel in valve.AdjacentValves)
{
if (lastStep.Type == Step.StepType.Move && lastStep.Value.Item1 == tunnel.Name)
continue; // Dont bounce back and forth, can go back if it was an open tho
StepList copy = new StepList(sl);
copy.AddStep(new Step(valve.Name, tunnel.Name));
copy = GenerateBestPath(copy);
paths.Add(copy);
StepList toRet = paths[0];
foreach (var item in paths)
{
if (item.GetPressureReleased() > toRet.GetPressureReleased())
{
toRet = item;
}
}
return toRet;
}
}
}
// Either the valve is already open or not worth opening
foreach (var tunnel in valve.AdjacentValves)
{
if (lastStep.Type == Step.StepType.Move && lastStep.Value.Item1 == tunnel.Name)
continue; // Dont bounce back and forth, can go back if it was an open tho
StepList copy = new StepList(sl);
copy.AddStep(new Step(valve.Name, tunnel.Name));
copy = GenerateBestPath(copy);
paths.Add(copy);
}
{
StepList toRet = paths[0];
foreach (var item in paths)
{
if (item.GetPressureReleased() > toRet.GetPressureReleased())
{
toRet = item;
}
}
return toRet;
}
}
}
class CaveGraph
{
public class CaveNode
{
public CaveNode(Valve v)
{
Valve = v;
}
public List<CaveNode> AdjacentNodes { get; set; } = new List<CaveNode>();
public List<int> AdjacentPathWeights { get; set; } = new List<int>();
public Valve Valve { get; }
public string Name
{
get
{
return Valve.Name;
}
}
public int FlowRate
{
get
{
return Valve.FlowRate;
}
}
}
private void CompressNodes(CaveNode cn, List<Valve> targets, int currentWeight)
{
foreach (var v in targets)
{
if (v.FlowRate > 0)
{
cn.AdjacentNodes.Add(CaveNodes[v.Name]);
}
}
}
public Dictionary<string, CaveNode> CaveNodes { get; }
public CaveGraph(Dictionary<string, Valve> valves)
{
CaveNodes = new Dictionary<string, CaveNode>();
foreach (var item in valves)
{
var valve = item.Value;
// TODO
CaveNode cn = new CaveNode(valve);
//CaveNodes.Add(cn);
}
}
}
class VolcanoData
{
public VolcanoData(string filename)
{
Valves = new Dictionary<string, Valve>();
StartingValve = new Valve("AA", 0);
using (StreamReader reader = System.IO.File.OpenText(filename))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line == null) throw new InvalidDataException();
// Read in the valve name
string newValveName = line.Split(' ')[1];
int flowRate = Int32.Parse(line.Split('=')[1].Substring(0, line.Split("=")[1].IndexOf(';')));
if (!Valves.ContainsKey(newValveName))
{
Valves[newValveName] = new Valve(newValveName, flowRate);
}
else
{
Valves[newValveName].FlowRate = flowRate;
}
// Read in adjacent valves
string[] valves;
if (line.Contains("to valve "))
{
valves = line.Split("to valve ")[1].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
}
else if (line.Contains("to valves "))
{
valves = line.Split("valves ")[1].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
}
else
{
throw new InvalidDataException();
}
foreach (var adjacentValve in valves)
{
if (!Valves.ContainsKey(adjacentValve))
{
Valves[adjacentValve] = new Valve(adjacentValve, -1);
}
Valves[newValveName].AdjacentValves.Add(Valves[adjacentValve]);
}
}
}
StartingValve = Valves["AA"];
CaveGraph cg = new CaveGraph(Valves);
}
public void Print()
{
foreach (var item in Valves)
{
Console.Write("{0}; tunnels lead to ", item.Value);
bool first = true;
foreach (var v in item.Value.AdjacentValves)
{
if (!first) Console.Write(", ");
Console.Write(v.Name);
first = false;
}
Console.WriteLine();
}
}
public Dictionary<string, Valve> Valves { get; }
public Valve StartingValve { get; }
public int GetMostPossiblePressureReleased(int maxSteps)
{
StepList sl = new StepList(Valves, maxSteps);
sl.AddStep(new StepList.Step());
sl = StepList.GenerateBestPath(sl);
return sl.GetPressureReleased();
}
}
class Valve
{
public Valve(string name, int flowRate)
{
Name = name;
FlowRate = flowRate;
AdjacentValves = new List<Valve>();
}
public List<Valve> AdjacentValves
{
get;
}
public int FlowRate { get; set; }
public string Name { get; }
public override string ToString()
{
return String.Format("Valve: ({0}) Flow ({1}): ", Name, FlowRate);
}
}