Day 7, Parts 1 + 2
Doing this one in Rust kicked my ass. I fought with the compiler for three days and never was able to figure out the borrows and pointers. I gave up yesterday and wrote up a solution in Python. Maybe I'll come back to it one day.
This commit is contained in:
		
							parent
							
								
									59156832c7
								
							
						
					
					
						commit
						ea5d557f0b
					
				
					 5 changed files with 1391 additions and 0 deletions
				
			
		
							
								
								
									
										1040
									
								
								2022/Data/day7-input.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1040
									
								
								2022/Data/day7-input.txt
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										23
									
								
								2022/Data/day7-test-input.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								2022/Data/day7-test-input.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
$ cd /
 | 
			
		||||
$ ls
 | 
			
		||||
dir a
 | 
			
		||||
14848514 b.txt
 | 
			
		||||
8504156 c.dat
 | 
			
		||||
dir d
 | 
			
		||||
$ cd a
 | 
			
		||||
$ ls
 | 
			
		||||
dir e
 | 
			
		||||
29116 f
 | 
			
		||||
2557 g
 | 
			
		||||
62596 h.lst
 | 
			
		||||
$ cd e
 | 
			
		||||
$ ls
 | 
			
		||||
584 i
 | 
			
		||||
$ cd ..
 | 
			
		||||
$ cd ..
 | 
			
		||||
$ cd d
 | 
			
		||||
$ ls
 | 
			
		||||
4060174 j
 | 
			
		||||
8033020 d.log
 | 
			
		||||
5626152 d.ext
 | 
			
		||||
7214296 k
 | 
			
		||||
							
								
								
									
										202
									
								
								2022/aoc2022/src/day7.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								2022/aoc2022/src/day7.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,202 @@
 | 
			
		|||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
const INPUT: &'static str = include_str!("../../Data/day7-input.txt");
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct DirectoryStack<'a> {
 | 
			
		||||
    stack: Vec<&'a Directory>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> DirectoryStack<'a> {
 | 
			
		||||
    fn new() -> DirectoryStack<'a> {
 | 
			
		||||
        DirectoryStack { stack: Vec::new() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn clear(&mut self) {
 | 
			
		||||
        self.stack.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn push<'b>(&mut self, dir: &'b Directory)
 | 
			
		||||
    where
 | 
			
		||||
        'b: 'a,
 | 
			
		||||
    {
 | 
			
		||||
        self.stack.push(dir);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn pop(&mut self) -> Option<&Directory> {
 | 
			
		||||
        self.stack.pop()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn peek(&mut self) -> Option<&Directory> {
 | 
			
		||||
        self.stack.last().cloned()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
struct File {
 | 
			
		||||
    name: String,
 | 
			
		||||
    size: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl File {
 | 
			
		||||
    fn new(name: &str, size: u32) -> File {
 | 
			
		||||
        File {
 | 
			
		||||
            name: name.to_string(),
 | 
			
		||||
            size: size,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
struct Directory {
 | 
			
		||||
    name: String,
 | 
			
		||||
    directories: HashMap<String, Directory>,
 | 
			
		||||
    files: Vec<File>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Directory {
 | 
			
		||||
    fn new(name: &str) -> Directory {
 | 
			
		||||
        Directory {
 | 
			
		||||
            name: name.to_string(),
 | 
			
		||||
            directories: HashMap::new(),
 | 
			
		||||
            files: Vec::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_subdirectory(&mut self, name: &str) -> Option<&Directory> {
 | 
			
		||||
        self.directories.get(name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn add_subdirectory(&mut self, directory_name: &str) -> std::result::Result<(), String> {
 | 
			
		||||
        let name = directory_name.to_string();
 | 
			
		||||
        if self.directories.contains_key(&name) {
 | 
			
		||||
            return Err(format!(
 | 
			
		||||
                "'{}' already contains subdirectory '{}'",
 | 
			
		||||
                &self.name, directory_name
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.directories
 | 
			
		||||
            .insert(name, Directory::new(directory_name));
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn add_file(&mut self, file: File) {
 | 
			
		||||
        self.files.push(file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size(&self) -> u32 {
 | 
			
		||||
        self.files.iter().map(|f| f.size).sum::<u32>()
 | 
			
		||||
            + self.directories.values().map(|d| d.size()).sum::<u32>()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn main(_filename: &str) -> std::io::Result<()> {
 | 
			
		||||
    let line_reader = INPUT.lines();
 | 
			
		||||
 | 
			
		||||
    let root = Directory::new("/");
 | 
			
		||||
    let mut directory_stack = DirectoryStack::new();
 | 
			
		||||
 | 
			
		||||
    for line in line_reader {
 | 
			
		||||
        let (prompt, command) = line.split_at(2);
 | 
			
		||||
        if prompt == "$ " {
 | 
			
		||||
            let split_command: Vec<&str> = command.split(" ").collect();
 | 
			
		||||
            println!("split command: {:?}", split_command);
 | 
			
		||||
            match split_command[0] {
 | 
			
		||||
                "cd" => {
 | 
			
		||||
                    let directory_name = split_command[1];
 | 
			
		||||
                    if directory_name == ".." {
 | 
			
		||||
                        directory_stack.pop();
 | 
			
		||||
                    } else if directory_name == "/" {
 | 
			
		||||
                        directory_stack.clear();
 | 
			
		||||
                        directory_stack.push(&root);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        let cwd = directory_stack.peek().expect("No working directory!");
 | 
			
		||||
                        let subdirectory = cwd
 | 
			
		||||
                            .get_subdirectory(directory_name)
 | 
			
		||||
                            .expect(&format!("No subdirectory named '{}'", directory_name));
 | 
			
		||||
                        directory_stack.push(&subdirectory);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                "ls" => {}
 | 
			
		||||
                _ => {}
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            let split_output: Vec<&str> = line.split(" ").collect();
 | 
			
		||||
            println!("{:?}", split_output);
 | 
			
		||||
            let directory_name = split_output[1];
 | 
			
		||||
 | 
			
		||||
            if split_output[0] == "dir" {
 | 
			
		||||
                dbg!(&directory_stack);
 | 
			
		||||
                let cwd = directory_stack.peek().expect("No working directory!");
 | 
			
		||||
                println!("Creating subdirectory {:?}", directory_name);
 | 
			
		||||
                cwd.add_subdirectory(directory_name);
 | 
			
		||||
            } else if let Ok(size) = u32::from_str_radix(split_output[0], 10) {
 | 
			
		||||
                let cwd = directory_stack.peek().expect("No working directory!");
 | 
			
		||||
                let file = File::new(directory_name, size);
 | 
			
		||||
                println!("Appending file {:?} to {:?}", &file, cwd.name);
 | 
			
		||||
                cwd.add_file(file);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::{Directory, DirectoryStack, File};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn size_of_empty_directory_is_zero() {
 | 
			
		||||
        let root = Directory::new("/");
 | 
			
		||||
        assert!(root.size() == 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn add_subdirectory() {
 | 
			
		||||
        let mut root = Directory::new("/");
 | 
			
		||||
        assert!(root.add_subdirectory("abc").is_ok());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn add_file() {
 | 
			
		||||
        let mut root = Directory::new("/");
 | 
			
		||||
        assert!(root.size() == 0);
 | 
			
		||||
 | 
			
		||||
        root.add_file(File::new("abc", 123));
 | 
			
		||||
        assert!(root.size() == 123);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn init_directory_stack() {
 | 
			
		||||
        let _ds = DirectoryStack::new();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn add_directory_to_stack() {
 | 
			
		||||
        let mut ds = DirectoryStack::new();
 | 
			
		||||
        let dir = Directory::new("abc");
 | 
			
		||||
        ds.push(&dir);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn add_directory_to_stack_and_pop() {
 | 
			
		||||
        let mut ds = DirectoryStack::new();
 | 
			
		||||
        let dir = Directory::new("abc");
 | 
			
		||||
        ds.push(&dir);
 | 
			
		||||
        ds.pop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn peek_at_top_of_stack() {
 | 
			
		||||
        let mut ds = DirectoryStack::new();
 | 
			
		||||
        let dir = Directory::new("abc");
 | 
			
		||||
        ds.push(&dir);
 | 
			
		||||
 | 
			
		||||
        let top = ds.peek();
 | 
			
		||||
        assert!(top.is_some());
 | 
			
		||||
        assert!(*top.unwrap() == dir);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ fn main() {
 | 
			
		|||
        day4::main,
 | 
			
		||||
        day5::main,
 | 
			
		||||
        day6::main,
 | 
			
		||||
        //day7::main,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    let args: Vec<String> = env::args().collect();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										125
									
								
								2022/day7.py
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										125
									
								
								2022/day7.py
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,125 @@
 | 
			
		|||
#!/usr/bin/env python3
 | 
			
		||||
# Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
New script.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
TOTAL_DISK_SIZE = 70000000
 | 
			
		||||
SIZE_REQUIRED_FOR_UPDATE = 30000000
 | 
			
		||||
 | 
			
		||||
class File:
 | 
			
		||||
    def __init__(self, name, size):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.size = size
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return f'{self.__class__.__name__}({self.name!r}, {self.size!r})'
 | 
			
		||||
 | 
			
		||||
class Directory:
 | 
			
		||||
    def __init__(self, name):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.directories = {}
 | 
			
		||||
        self.files = []
 | 
			
		||||
        self.parent = None
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return f'{self.__class__.__name__}({self.name!r})'
 | 
			
		||||
 | 
			
		||||
    def add_subdirectory(self, subdirectory):
 | 
			
		||||
        assert subdirectory.name not in self.directories
 | 
			
		||||
        self.directories[subdirectory.name] = subdirectory
 | 
			
		||||
        subdirectory.parent = self
 | 
			
		||||
 | 
			
		||||
    def add_file(self, file):
 | 
			
		||||
        self.files.append(file)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def size(self):
 | 
			
		||||
        size_of_directories = sum(d.size for d in self.directories.values())
 | 
			
		||||
        size_of_files = sum(f.size for f in self.files)
 | 
			
		||||
        return size_of_directories + size_of_files
 | 
			
		||||
 | 
			
		||||
    def pretty_print(self, indent=0):
 | 
			
		||||
        print(' ' * indent, self.name, f'({self.size})')
 | 
			
		||||
        for d in self.directories.values():
 | 
			
		||||
            d.pretty_print(indent=indent+2)
 | 
			
		||||
        for f in self.files:
 | 
			
		||||
            print(' ' * (indent + 2), repr(f))
 | 
			
		||||
 | 
			
		||||
def parse_args(argv, *a, **kw):
 | 
			
		||||
    parser = argparse.ArgumentParser(*a, **kw)
 | 
			
		||||
    parser.add_argument('input')
 | 
			
		||||
    args = parser.parse_args(argv)
 | 
			
		||||
    return args
 | 
			
		||||
 | 
			
		||||
def main(argv):
 | 
			
		||||
    args = parse_args(argv[1:], prog=argv[0])
 | 
			
		||||
 | 
			
		||||
    with open(args.input) as f:
 | 
			
		||||
        lines = f.readlines()
 | 
			
		||||
 | 
			
		||||
    print(f'{len(lines)} total lines in input file {args.input}')
 | 
			
		||||
 | 
			
		||||
    root = Directory('/')
 | 
			
		||||
    cwd = root
 | 
			
		||||
 | 
			
		||||
    for line in lines:
 | 
			
		||||
        split_line = line.strip().split(' ')
 | 
			
		||||
        if split_line[0] == '$':
 | 
			
		||||
            if split_line[1] == 'cd':
 | 
			
		||||
                dir_name = split_line[2].strip()
 | 
			
		||||
                if dir_name == '/':
 | 
			
		||||
                    cwd = root
 | 
			
		||||
                elif dir_name == '..':
 | 
			
		||||
                    cwd = cwd.parent
 | 
			
		||||
                else:
 | 
			
		||||
                    cwd = cwd.directories[dir_name]
 | 
			
		||||
                print(f'Set cwd to {cwd}')
 | 
			
		||||
            elif split_line[1] == 'ls':
 | 
			
		||||
                print(f'Nothing to do for `ls` in {cwd}')
 | 
			
		||||
                # Nothing to do here.
 | 
			
		||||
                pass
 | 
			
		||||
        elif split_line[0] == 'dir':
 | 
			
		||||
            # dir [name]
 | 
			
		||||
            name = split_line[1]
 | 
			
		||||
            print(f'Found subdirectory {name} in {cwd}')
 | 
			
		||||
            cwd.add_subdirectory(Directory(name))
 | 
			
		||||
        else:
 | 
			
		||||
            # [size] [name]
 | 
			
		||||
            name = split_line[1]
 | 
			
		||||
            size = int(split_line[0])
 | 
			
		||||
            print(f'Found file {name} with size {size} in {cwd}')
 | 
			
		||||
            cwd.add_file(File(name, size))
 | 
			
		||||
 | 
			
		||||
    root.pretty_print()
 | 
			
		||||
    print(f'Part 0: total size of the entire tree: {root.size}')
 | 
			
		||||
 | 
			
		||||
    root_size = root.size
 | 
			
		||||
    free_space = TOTAL_DISK_SIZE - root_size
 | 
			
		||||
    minimum_size_of_directory_to_delete = SIZE_REQUIRED_FOR_UPDATE - free_space
 | 
			
		||||
 | 
			
		||||
    part1_sum = 0
 | 
			
		||||
    part2_directories = []
 | 
			
		||||
    dfs_stack = [root]
 | 
			
		||||
    while len(dfs_stack):
 | 
			
		||||
        d = dfs_stack.pop()
 | 
			
		||||
        dfs_stack.extend(d.directories.values())
 | 
			
		||||
        dir_size = d.size
 | 
			
		||||
        if dir_size <= 100000:
 | 
			
		||||
            part1_sum += dir_size
 | 
			
		||||
        if dir_size > minimum_size_of_directory_to_delete:
 | 
			
		||||
            part2_directories.append(d)
 | 
			
		||||
 | 
			
		||||
    print(f'Part 1: total size of directories with size less than 100,000: {part1_sum}')
 | 
			
		||||
 | 
			
		||||
    directory_to_delete = min(part2_directories, key=lambda d: d.size)
 | 
			
		||||
    print(f'Part 2: size of directory to delete: {directory_to_delete.size}')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    import sys
 | 
			
		||||
    result = main(sys.argv)
 | 
			
		||||
    sys.exit(0 if not result else result)
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue