428 lines
11 KiB
C#
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);
|
|
}
|
|
}
|
|
|