class Program
{
public static void Main()
{
var file = File.ReadAllLines("../../../Input.txt");
Console.WriteLine(PartOne(file));
Console.WriteLine(PartTwo(file));
}
public static string PartOne(string[] input)
{
var storage = Storage.ParseTerminalOutput(in input);
return storage.AllDirectories.Where(x => x.Size < 100_000)
.Sum(x => x.Size)
.ToString();
}
public static string PartTwo(string[] input)
{
var storage = Storage.ParseTerminalOutput(input);
var needSpace = 30_000_000 - (70_000_000 - storage.Size);
return storage.AllDirectories.Where(x => x.Size >= needSpace)
.Min(x => x.Size)
.ToString();
}
class Storage
{
private readonly Dictionary<string, Directory> _allDirectories;
public readonly Directory Root;
public Storage()
{
_allDirectories = new();
Root = new("root");
_allDirectories.Add(Root.Fullname, Root);
}
public IEnumerable<Directory> AllDirectories => _allDirectories.Values;
public int Size => Root.Size;
public void AddDirectory(Directory parent, string name)
{
var directory = parent.AddDirectory(name);
if (directory != null)
{
_allDirectories.Add(directory.Fullname, directory);
}
}
public void AddFile(Directory directory, string name, int size)
{
directory.AddFile(name, size);
}
internal static Storage ParseTerminalOutput(in string[] inputLines)
=> Parser.Parse(in inputLines);
internal class Directory
{
public readonly Directory Parent;
public readonly string Name;
private readonly Dictionary<string, Directory> _directories;
private readonly Dictionary<string, File> _files;
public Directory(string name) : this(null, name) { }
public Directory(Directory parent, string name)
{
_directories = new Dictionary<string, Directory>();
_files = new Dictionary<string, File>();
Parent = parent;
Name = name;
Size = 0;
}
public int Size { get; private set; }
public string Fullname => Parent != null ? $"{Parent.Fullname}\\{Name}" : Name;
private void IncreaseSize(int size)
{
Size += size;
Parent?.IncreaseSize(size);
}
internal Directory? AddDirectory(string name)
{
if (_directories.ContainsKey(name)) return null;
var directory = new Directory(this, name);
_directories.Add(name, directory);
return directory;
}
internal File? AddFile(string name, int size)
{
if (_files.ContainsKey(name)) return null;
var file = new File(this, name, size);
_files.Add(name, file);
IncreaseSize(size);
return file;
}
internal Directory GetDirectory(string name) => _directories[name];
public override string ToString() => $"{Name} {Size}";
}
internal class File
{
public File(Directory directory, string name, int size)
{
Directory = directory;
Name = name;
Size = size;
}
public Directory Directory { get; private set; }
public string Name { get; private set; }
public int Size { get; private set; }
public override string ToString() => $"{Name} {Size}";
public string Fullname => $"{Directory.Fullname}\\{Name}";
}
internal static class Parser
{
private static readonly string ListAll = "$ ls";
private static readonly string GoToRoot = "$ cd /";
private static readonly string GoBack = "$ cd ..";
private static readonly string GoTo = "$ cd";
private static readonly string DirPrefix = "dir";
private static readonly int GoToJumpCount = GoTo.Length + 1;
private static readonly int DirPrefixJumpCount = DirPrefix.Length + 1;
public static Storage Parse(in string[] inputLines)
{
var storage = new Storage();
var currecntDirectory = storage.Root;
foreach (var inputLineRaw in inputLines)
{
var inputLine = inputLineRaw.AsSpan();
if (inputLine.StartsWith(ListAll))
{
continue;
}
if (inputLine.StartsWith(GoToRoot))
{
currecntDirectory = storage.Root;
continue;
}
if (inputLine.StartsWith(GoBack))
{
currecntDirectory = currecntDirectory?.Parent ?? storage.Root;
continue;
}
if (inputLine.StartsWith(GoTo))
{
var directoryName = inputLine[GoToJumpCount..].ToString();
currecntDirectory = currecntDirectory.GetDirectory(directoryName) ?? currecntDirectory;
continue;
}
if (inputLine.StartsWith(DirPrefix))
{
var directoryName = inputLine[DirPrefixJumpCount..].ToString();
storage.AddDirectory(currecntDirectory, directoryName);
continue;
}
var spaceIndex = inputLine.IndexOf(' ');
var size = int.Parse(inputLine[..spaceIndex]);
var fileName = inputLine[spaceIndex..].ToString();
storage.AddFile(currecntDirectory, fileName, size);
continue;
}
return storage;
}
}
}
}