diff --git a/.gitignore b/.gitignore index a478d5f..c9e2ee2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ -input/ \ No newline at end of file +input/ +*.out \ No newline at end of file diff --git a/bacon.toml b/bacon.toml new file mode 100644 index 0000000..967af15 --- /dev/null +++ b/bacon.toml @@ -0,0 +1,123 @@ +# This is a configuration file for the bacon tool +# +# Complete help on configuration: https://dystroy.org/bacon/config/ +# +# You may check the current default at +# https://github.com/Canop/bacon/blob/main/defaults/default-bacon.toml + +default_job = "check" +env.CARGO_TERM_COLOR = "always" + +[jobs.check] +command = ["cargo", "check"] +need_stdout = false + +[jobs.check-all] +command = ["cargo", "check", "--all-targets"] +need_stdout = false + +# Run clippy on the default target +[jobs.clippy] +command = ["cargo", "clippy"] +need_stdout = false + +[jobs.aoc] +command = ["cargo", "aoc"] +need_stdout = true +on_change_strategy = "kill_then_restart" + +# Run clippy on all targets +# To disable some lints, you may change the job this way: +# [jobs.clippy-all] +# command = [ +# "cargo", "clippy", +# "--all-targets", +# "--", +# "-A", "clippy::bool_to_int_with_if", +# "-A", "clippy::collapsible_if", +# "-A", "clippy::derive_partial_eq_without_eq", +# ] +# need_stdout = false +[jobs.clippy-all] +command = ["cargo", "clippy", "--all-targets"] +need_stdout = false + +# This job lets you run +# - all tests: bacon test +# - a specific test: bacon test -- config::test_default_files +# - the tests of a package: bacon test -- -- -p config +[jobs.test] +command = ["cargo", "test"] +need_stdout = true + +[jobs.nextest] +command = [ + "cargo", + "nextest", + "run", + "--hide-progress-bar", + "--failure-output", + "final", +] +need_stdout = true +analyzer = "nextest" + +[jobs.doc] +command = ["cargo", "doc", "--no-deps"] +need_stdout = false + +# If the doc compiles, then it opens in your browser and bacon switches +# to the previous job +[jobs.doc-open] +command = ["cargo", "doc", "--no-deps", "--open"] +need_stdout = false +on_success = "back" # so that we don't open the browser at each change + +# You can run your application and have the result displayed in bacon, +# if it makes sense for this crate. +[jobs.run] +command = [ + "cargo", + "run", + # put launch parameters for your program behind a `--` separator +] +need_stdout = true +allow_warnings = true +background = true + +# Run your long-running application (eg server) and have the result displayed in bacon. +# For programs that never stop (eg a server), `background` is set to false +# to have the cargo run output immediately displayed instead of waiting for +# program's end. +# 'on_change_strategy' is set to `kill_then_restart` to have your program restart +# on every change (an alternative would be to use the 'F5' key manually in bacon). +# If you often use this job, it makes sense to override the 'r' key by adding +# a binding `r = job:run-long` at the end of this file . +[jobs.run-long] +command = [ + "cargo", + "run", + # put launch parameters for your program behind a `--` separator +] +need_stdout = true +allow_warnings = true +background = false +on_change_strategy = "kill_then_restart" + +# This parameterized job runs the example of your choice, as soon +# as the code compiles. +# Call it as +# bacon ex -- my-example +[jobs.ex] +command = ["cargo", "run", "--example"] +need_stdout = true +allow_warnings = true + +# You may define here keybindings that would be specific to +# a project, for example a shortcut to launch a specific job. +# Shortcuts to internal functions (scrolling, toggling, etc.) +# should go in your personal global prefs.toml file instead. +[keybindings] +# alt-m = "job:my-job" +c = "job:clippy-all" # comment this to have 'c' run clippy on only the default target +a = "job:aoc" diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..e74ce90 --- /dev/null +++ b/run.sh @@ -0,0 +1 @@ +cargo test \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..ffc467d --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +max_width = 200 diff --git a/src/day4.rs b/src/day4.rs index 3903882..8c819e5 100644 --- a/src/day4.rs +++ b/src/day4.rs @@ -1,31 +1,110 @@ use aoc_runner_derive::{aoc, aoc_generator}; #[aoc_generator(day4)] -fn parse(input: &str) -> String { - todo!() +fn parse(input: &str) -> Vec> { + input.split("\n").map(|line| line.chars().collect()).collect() } #[aoc(day4, part1)] -fn part1(input: &str) -> String { - todo!() +fn part1(d: &[Vec]) -> usize { + (0..d.len()) + .flat_map(|i| (0..d[0].len()).map(move |j| (j as i64, i as i64, j > 2, j < d.len() - 3, i > 2, i < d[0].len() - 3))) + .filter(|p| d[p.1 as usize][p.0 as usize] == 'X') + .flat_map(|p| { + (-1..=1) + .flat_map(|dx| (-1..=1).filter(move |i| dx != 0 || *i != 0).map(move |i| (dx, i))) + .filter(move |(a, b)| (*a == 0 || (*a == -1 && p.2) || (*a == 1 && p.3)) && (*b == 0 || (*b == -1 && p.4) || (*b == 1 && p.5))) + .filter(move |r| (1..=3).zip("MAS".chars()).all(|v| d[(p.1 + v.0 * r.1) as usize][(p.0 + v.0 * r.0) as usize] == v.1)) + }) + .count() +} + +fn _part1(input: &[Vec]) -> u64 { + let h = input.len(); + let w = input[0].len(); + let mut sum = 0; + for y in 0..h { + for x in 0..w { + if input[y][x] == 'S' { + if x < w - 3 && input[y][x + 1] == 'A' && input[y][x + 2] == 'M' && input[y][x + 3] == 'X' { + // println!("rev X {} {}", x, y); + sum += 1 + } + if y < h - 3 && input[y + 1][x] == 'A' && input[y + 2][x] == 'M' && input[y + 3][x] == 'X' { + // println!("rev Y {} {}", x, y); + sum += 1 + } + if x < w - 3 && y < h - 3 && input[y + 1][x + 1] == 'A' && input[y + 2][x + 2] == 'M' && input[y + 3][x + 3] == 'X' { + // println!("rdi Y {} {}", x, y); + sum += 1 + } + if x >= 3 && y < h - 3 && input[y + 1][x - 1] == 'A' && input[y + 2][x - 2] == 'M' && input[y + 3][x - 3] == 'X' { + // println!("rdi Y {} {}", x, y); + sum += 1 + } + } else if input[y][x] == 'X' { + if x < w - 3 && input[y][x + 1] == 'M' && input[y][x + 2] == 'A' && input[y][x + 3] == 'S' { + // println!(" X {} {}", x, y); + sum += 1 + } + if y < h - 3 && input[y + 1][x] == 'M' && input[y + 2][x] == 'A' && input[y + 3][x] == 'S' { + // println!(" Y {} {}", x, y); + sum += 1 + } + if x < w - 3 && y < h - 3 && input[y + 1][x + 1] == 'M' && input[y + 2][x + 2] == 'A' && input[y + 3][x + 3] == 'S' { + // println!("dia Y {} {}", x, y); + sum += 1 + } + if x >= 3 && y < h - 3 && input[y + 1][x - 1] == 'M' && input[y + 2][x - 2] == 'A' && input[y + 3][x - 3] == 'S' { + // println!("dia Y {} {}", x, y); + sum += 1 + } + } + } + } + sum } #[aoc(day4, part2)] -fn part2(input: &str) -> String { - todo!() +fn part2(input: &[Vec]) -> u64 { + let h = input.len(); + let w = input[0].len(); + let mut sum = 0; + for y in 1..h - 1 { + for x in 1..w - 1 { + if input[y][x] == 'A' { + let diag1 = (input[y - 1][x - 1] == 'M' && input[y + 1][x + 1] == 'S') || (input[y - 1][x - 1] == 'S' && input[y + 1][x + 1] == 'M'); + let diag2 = (input[y - 1][x + 1] == 'M' && input[y + 1][x - 1] == 'S') || (input[y - 1][x + 1] == 'S' && input[y + 1][x - 1] == 'M'); + if diag1 && diag2 { + sum += 1; + } + } + } + } + sum } - #[cfg(test)] mod tests { use super::*; #[test] fn part1_example() { - assert_eq!(part1(&parse("")), ""); + assert_eq!( + part1(&parse( + "MMMSXXMASM\nMSAMXMSMSA\nAMXSXMAAMM\nMSAMASMSMX\nXMASAMXAMM\nXXAMMXXAMA\nSMSMSASXSS\nSAXAMASAAA\nMAMMMXMMMM\nMXMXAXMASX" + )), + 18 + ); + assert_eq!(part1(&parse(include_str!("../input/2024/day4.txt"))), 2406); } #[test] fn part2_example() { - assert_eq!(part2(&parse("")), ""); + assert_eq!( + part2(&parse( + ".M.S......\n..A..MSMS.\n.M.S.MAA..\n..A.ASMSM.\n.M.S.M....\n..........\nS.S.S.S.S.\n.A.A.A.A..\nM.M.M.M.M.\n.........." + )), + 9 + ); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 998b5a1..80a5a1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ mod day4; -mod day3; mod day1; mod day2; +mod day3; extern crate aoc_runner;