// 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 Value { get; set; } public Step() { Type = StepType.First; Value = new Tuple("AA", "0"); } public Step(string sourceValve, string targetValve) { Type = StepType.Move; Value = new Tuple(sourceValve, targetValve); } public Step(string valvename, int stepNumber) { Type = StepType.Open; Value = new Tuple(valvename, stepNumber.ToString()); } } private List Steps { get; } private Dictionary OpenedValves { get; } private IReadOnlyDictionary Valves { get; } public bool isValveOpen(string valveName) { return OpenedValves.ContainsKey(valveName); } public StepList(IReadOnlyDictionary valves, int maxSteps = 31) { MaxSteps = maxSteps; Valves = valves; OpenedValves = new Dictionary(); Steps = new List(); } public StepList(StepList other) { MaxSteps = other.MaxSteps; Valves = other.Valves; OpenedValves = new Dictionary(); Steps = new List(); 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 paths = new List(); 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 AdjacentNodes { get; set; } = new List(); public List AdjacentPathWeights { get; set; } = new List(); public Valve Valve { get; } public string Name { get { return Valve.Name; } } public int FlowRate { get { return Valve.FlowRate; } } } private void CompressNodes(CaveNode cn, List targets, int currentWeight) { foreach (var v in targets) { if (v.FlowRate > 0) { cn.AdjacentNodes.Add(CaveNodes[v.Name]); } } } public Dictionary CaveNodes { get; } public CaveGraph(Dictionary valves) { CaveNodes = new Dictionary(); 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(); 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 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(); } public List AdjacentValves { get; } public int FlowRate { get; set; } public string Name { get; } public override string ToString() { return String.Format("Valve: ({0}) Flow ({1}): ", Name, FlowRate); } }