first commit

This commit is contained in:
Zenn 2023-09-12 09:55:00 +02:00
commit cfe3798d17
42 changed files with 9617 additions and 0 deletions

382
2022/day7/src/main.rs Executable file
View file

@ -0,0 +1,382 @@
#![allow(unused_imports)]
use std::{
borrow::BorrowMut,
env::current_dir,
fmt::{format, Display},
fs,
iter::empty,
ops::DerefMut,
};
use transiter::{IntoTransIter, TransIter};
use dbg_pls::{color, DebugPls};
use nom::{
branch::alt,
bytes::complete::{tag, take_till1, take_until1, take_while1},
character::{
complete::{digit1, newline, not_line_ending},
is_alphabetic,
},
combinator::{map_res, opt},
multi::many0,
AsChar, IResult,
};
mod utils;
// PUZZLE: https://web.archive.org/web/20230223113025/https://adventofcode.com/2022/day/7
#[derive(Debug, DebugPls)]
enum File {
Plain { name: String, size: usize },
Dir { name: String, children: Vec<File> },
}
#[derive(Debug, DebugPls)]
#[allow(non_camel_case_types)]
enum Command {
cd { target: String },
ls { files: Vec<File> },
}
pub fn main() {
println!("Hello, AdventOfCode - day 7!");
let content = fs::read_to_string("./data").expect("Read ./input");
println!("PART 1 - RESULT: {}", process_part1(content.clone()));
println!();
println!("PART 2 - RESULT: {}", process_part2(content));
}
fn process_part1(content: String) -> String {
let root = parse_session(content.as_str());
let dirs = root.dirs_recursive();
println!("Dirs: {}", color(&dirs.iter().map(|f| f.name()).collect::<Vec<String>>()));
let dirs_under_size_limit = dirs
.iter()
.filter(|dir| dir.total_size() <= 100000)
.collect::<Vec<_>>();
println!("Dirs under size limit: {}", color(&dirs_under_size_limit));
let total_size = dirs_under_size_limit.iter().map(|f| f.total_size()).sum::<usize>();
total_size.to_string()
}
fn process_part2(content: String) -> String {
let root = parse_session(content.as_str());
let used = root.total_size();
let capacity = 70000000;
let available = capacity - used;
println!("Total size: {} ({} available)", used, available);
let required = 30000000;
let to_be_freed = required - available;
println!("Must be freed: {}", to_be_freed);
let dirs = root.dirs_recursive();
let mut big_enough = dirs
.iter()
.filter(|dir| dir.total_size() >= to_be_freed)
.collect::<Vec<_>>();
big_enough.sort_unstable_by_key(|f| f.total_size());
println!(
"Big enough: {}",
color(
&big_enough
.iter()
.map(|f| format!("[{}] {}", f.total_size(), f.name()))
.collect::<Vec<String>>()
)
);
let smallest_sufficient = big_enough.first().expect("a big enough dir");
smallest_sufficient.total_size().to_string()
}
fn parse_session(content: &str) -> File {
let mut root = File::Dir {
name: "/".into(),
children: vec![],
};
let mut input = content;
let mut cwd: Vec<String> = vec![];
// let mut current_dir = root;
while input.len() > 0 {
let (remaining_input, cmd) = parse_command(input).expect("parse command");
input = remaining_input;
color!(&cmd);
let current_dir: &mut File = get_dir(&mut root, &cwd);
if let File::Dir {
children,
name: current_dir_name,
} = current_dir
{
match cmd {
Command::cd { target } => {
match target.as_str() {
"/" => {
cwd = vec![];
}
".." => {
cwd.pop();
}
_ => {
let target = children
.iter()
.find(|f| {
if let File::Dir { name, .. } = f {
name == &target
} else {
false
}
})
.expect("find cd target");
// current_dir = target;
cwd.push(target.name())
}
}
println!(
"Changed into {} => cwd: {}",
target,
cwd /* .iter().map(|f| f.get_name()).collect::<Vec<_>>() */
.join("/")
)
}
Command::ls { mut files } => {
println!("Adding children to {}: {:#?}", current_dir_name, color(&cwd));
children.append(&mut files);
}
}
} else {
panic!("cwd {} is not a dir: {:?}", cwd.join("/"), current_dir)
}
}
println!();
println!("Final Directory structure: {}", color(&root));
root
}
fn get_dir<'a>(root: &'a mut File, path: &'a Vec<String>) -> &'a mut File {
let mut cwd = root;
for target in path {
if let File::Dir {
ref mut children, ..
} = cwd
{
cwd = children
.into_iter()
.find(|f| {
if let File::Dir { name, .. } = f {
name == target
} else {
false
}
})
.expect("find cd target")
.borrow_mut()
} else {
panic!("get_dir path '{}' is not a dir: {:?}", path.join("/"), cwd)
}
}
cwd
}
fn parse_command(input: &str) -> IResult<&str, Command> {
let (input, cmd_name) = parse_command_name(input).expect("parse command name");
Ok(match cmd_name {
"ls" => {
let (input, files) = parse_ls_output(input).expect("parse command output");
(input, Command::ls { files })
}
"cd" => {
let (input, target) = parse_command_arg(input).expect("parse command arg");
let (input, _) = newline(input)?;
(
input,
Command::cd {
target: target.into(),
},
)
}
&_ => panic!("invalid command {}", cmd_name),
})
}
fn parse_ls_output(input: &str) -> IResult<&str, Vec<File>> {
let (input, output) = parse_command_output(input)?;
let (remaining_output, files) = many0(parse_file)(output)?;
if remaining_output.len() != 0 {
panic!("ls output not fully consumed: '{}'", remaining_output);
}
Ok((input, files))
}
fn parse_file(input: &str) -> IResult<&str, File> {
let (input, dir_or_size) = alt((tag("dir"), digit1))(input)?;
let (input, _) = tag(" ")(input)?;
let (input, name) = till_eol(input)?;
let (input, _) = opt(newline)(input)?;
let file = match dir_or_size {
"dir" => File::Dir {
name: name.into(),
children: vec![],
},
size => File::Plain {
name: name.into(),
size: size.parse().expect("parsable size"),
},
};
color!(&file, input);
Ok((input, file))
}
fn parse_command_name(input: &str) -> IResult<&str, &str> {
let (input, _) = tag("$")(input)?;
let (input, _) = tag(" ")(input)?;
let (input, cmd_name) = take_while1(AsChar::is_alpha)(input)?;
Ok((input, cmd_name))
}
fn parse_command_arg(input: &str) -> IResult<&str, &str> {
let (input, _) = tag(" ")(input)?;
let (input, arg) = till_eol(input)?;
Ok((input, arg))
}
fn parse_command_output(input: &str) -> IResult<&str, &str> {
let (input, _) = newline(input)?;
let (input, output) = take_while1(|c: char| c != '$')(input)?;
Ok((input, output))
}
fn till_eol(input: &str) -> IResult<&str, &str> {
Ok(take_till1(|c: char| c.is_whitespace())(input)?)
}
impl File {
fn name(&self) -> String {
match self {
File::Dir { name, .. } => name.clone(),
File::Plain { name, .. } => name.clone(),
}
}
fn total_size(&self) -> usize {
match self {
File::Plain { size, .. } => *size,
File::Dir { children, .. } => children.into_iter().map(|f| f.total_size()).sum(),
}
}
fn dirs_recursive(&self) -> Vec<&File> {
self.trans_iter_with(|file| {
if let File::Dir { children, .. } = file {
children
.into_iter()
.filter(|f| matches!(f, File::Dir { .. }))
.collect::<Vec<_>>()
} else {
vec![]
}
})
.collect()
}
}
// impl Display for File {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// match self {
// Self::cd { target } => {
// write!(f, "cd {}", target)?;
// }
// Self::ls { files } => {
// write!(
// f,
// "ls\n{:?}",
// files
// .iter()
// .map(|f| format!("{}", color(f)))
// .collect::<Vec<_>>()
// .join("\n")
// )?;
// }
// }
// Ok(())
// }
// }
// impl Display for Command {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// match self {
// Self::cd { target } => {
// write!(f, "cd {}", target)?;
// }
// Self::ls { files } => {
// write!(
// f,
// "ls\n{:?}",
// files
// .iter()
// .map(|f| format!("{}", color(f)))
// .collect::<Vec<_>>()
// .join("\n")
// )?;
// }
// }
// Ok(())
// }
// }
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE: &str = "$ 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";
#[test]
fn example_part1() {
let result = process_part1(EXAMPLE.into());
assert_eq!(result, "95437");
}
#[test]
fn real_part1() {
let result = process_part1(fs::read_to_string("./input").expect("Read ./input"));
assert_eq!(result, "1443806");
}
#[test]
fn example_part2() {
let result = process_part2(EXAMPLE.into());
assert_eq!(result, "24933642");
}
#[test]
fn real_part2() {
let result = process_part2(fs::read_to_string("./input").expect("Read ./input"));
assert_eq!(result, "942298");
}
}

12
2022/day7/src/utils.rs Executable file
View file

@ -0,0 +1,12 @@
/// https://stackoverflow.com/a/69324393/1633985
#[macro_export]
macro_rules! cast {
($target: expr, $pat: path) => {{
if let $pat(a) = $target {
// #1
a
} else {
panic!("mismatch variant when cast to {}", stringify!($pat)); // #2
}
}};
}