#include #include #include #include #include #include #include std::vector SplitStr(std::string str, const std::string &delim) { std::vector rtn; for(auto split = str.find(delim); split != std::string::npos; split = str.find(delim)) { rtn.push_back(str.substr(0, split)); str = str.substr(split + delim.size()); // chop off beginning } if(str.size()) rtn.push_back(str); return rtn; } class File { public: File(const std::string &set_filename, unsigned long set_size) { this->size = set_size; this->filename = set_filename; } virtual ~File() { } const std::string& Filename() const { return filename; } virtual unsigned long Size() const { return size; } protected: unsigned long size; std::string filename; }; class Folder : public File { public: Folder(const std::string &filename) : File(filename, 0) { } unsigned long Size() const override { unsigned long size = 0; for(const auto *file : files) size += file->Size(); return size; } void AddFile(const std::string &filename, unsigned long size) { // make sure the file doesn't already exist: if(std::find_if(files.begin(), files.end(), [&](const File *file) { return file->Filename() == filename; }) != files.end()) { std::cerr << "Folder \"" << this->Filename() << "\" already contains " << "the file \"" << filename << "\"." << std::endl; exit(-1); } // increment this folder's size total: this->size += size; // add the file: files.push_back(new File(filename, size)); } void AddFolder(const std::string &name) { if(std::find_if(files.begin(), files.end(), [&](const File *file) { return file->Filename() == name; }) != files.end()) { std::cout << "NOTE: Folder \"" << this->Filename() << "\" already contains " << "the subfolder \"" << name << "\"." << std::endl; // exit(-1); } files.push_back(new Folder(name)); } const std::vector& Files() const { return files; } private: std::vector files; }; void DebugOutput(File *file) { static int indent = -1; ++indent; for(auto i = 0; i < indent; ++i) std::cout << " "; Folder *folder = dynamic_cast(file); if(folder) { std::cout << "Contents of folder: " << folder->Filename() << ", size: " << folder->Size() << std::endl; for(File *file : folder->Files()) DebugOutput(file); } else std::cout << "- " << file->Filename() << ", size: " << file->Size() << std::endl; --indent; } unsigned long PartOne(File *file, unsigned long &grand_total) { unsigned long rtn = 0; Folder *folder = dynamic_cast(file); if(folder) { for(File *file : folder->Files()) rtn += PartOne(file, grand_total); if(rtn <= 100000) { grand_total += rtn; std::cout << "Folder \"" << folder->Filename() << "\" has a size of " << rtn << std::endl; } } else rtn += file->Size(); return rtn; } void PartTwo(File *file, unsigned long at_least, File *&smallest) { Folder *folder = dynamic_cast(file); if(folder) { if(folder->Size() >= at_least && folder->Size() < smallest->Size()) { std::cout << "New smallest size of " << folder->Size() << " by folder \"" << folder->Filename() << "\"." << std::endl; smallest = file; } for(File *file : folder->Files()) PartTwo(file, at_least, smallest); } return; } int main() { std::ifstream ifs("data.txt"); if(!ifs.is_open()) { std::cerr << "Missing data.txt." << std::endl; return -1; } Folder root("/"); std::stack dir_stack; dir_stack.push(&root); // Step 1: Parse: for(std::string line; std::getline(ifs, line); ) { if(line == "") continue; if(line.starts_with("$ ls")) continue; else if(line.starts_with("$ cd")) { auto to = line.substr(5); if(to == "/") { while(dir_stack.size() > 1) dir_stack.pop(); } else if(to == "..") { if(dir_stack.size() > 1) dir_stack.pop(); } else { auto existing = std::find_if(dir_stack.top()->Files().begin(), dir_stack.top()->Files().end(), [&to](const File *f){ return f->Filename() == to; }); if(existing != dir_stack.top()->Files().end()) // found! { // Check to see if it's a folder, first: Folder *folder = dynamic_cast(*existing); if(folder) dir_stack.push(folder); else { std::cerr << (*existing)->Filename() << " is a file, not a folder." << std::endl; exit(-3); } } else std::cerr << "could not navigate to \"" << to << "\" from \"" << dir_stack.top()->Filename() << "\"." << std::endl; } } else if(line.starts_with("dir ")) { dir_stack.top()->AddFolder(line.substr(4)); } else { auto values = SplitStr(line, " "); dir_stack.top()->AddFile(values[1], std::atol(values[0].c_str())); } } // Step 1.5: Debug output: DebugOutput(&root); unsigned long grand_total = 0; PartOne(&root, grand_total); std::cout << "Grand Total (part 1): " << grand_total << std::endl; const unsigned long max_space = 70000000; const unsigned long space_needed = 30000000; const unsigned long currently_used = root.Size(); const unsigned long currently_unused = max_space - currently_used; const unsigned long amt_to_remove = space_needed - currently_unused; File *file_to_remove = &root; PartTwo(&root, amt_to_remove, file_to_remove); std::cout << "Remove \"" << file_to_remove->Filename() << "\" to free up " << file_to_remove->Size() << " which is the smallest amount >= " << amt_to_remove << " (part 2)." << std::endl; return 0; }