diff --git a/.idea/AdventOfCode.iml b/.idea/AdventOfCode.iml index 4661634..a1dbddc 100644 --- a/.idea/AdventOfCode.iml +++ b/.idea/AdventOfCode.iml @@ -5,8 +5,6 @@ - - diff --git a/Cargo.lock b/Cargo.lock index 7219f43..06c327d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,6 @@ dependencies = [ name = "aoc2023" version = "0.1.0" dependencies = [ - "itertools", "regex", "shared", "substring", @@ -35,21 +34,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "itertools" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" -dependencies = [ - "either", -] - [[package]] name = "memchr" version = "2.6.4" diff --git a/aoc2023/Cargo.toml b/aoc2023/Cargo.toml index 81acc05..0b76455 100644 --- a/aoc2023/Cargo.toml +++ b/aoc2023/Cargo.toml @@ -6,5 +6,4 @@ edition = "2021" [dependencies] shared = { path = "../shared" } regex = "1.10.2" -substring = "1.4.5" -itertools = "0.12.0" \ No newline at end of file +substring = "1.4.5" \ No newline at end of file diff --git a/aoc2023/src/day05.rs b/aoc2023/src/day05.rs index e315821..5567f84 100644 --- a/aoc2023/src/day05.rs +++ b/aoc2023/src/day05.rs @@ -133,6 +133,10 @@ struct Rule { } impl Rule { + fn new(start: i64, end: i64, offset: i64) -> Rule { + Rule {start, end, offset} + } + fn from_rules(dest: i64, source: i64, range: i64) -> Rule { Rule {start: source, end: source + range - 1, offset: (dest - source)} } diff --git a/aoc2023/src/day10.rs b/aoc2023/src/day10.rs index afa65a1..517959e 100644 --- a/aoc2023/src/day10.rs +++ b/aoc2023/src/day10.rs @@ -1,108 +1,193 @@ use std::collections::HashSet; +use std::process::abort; use shared::{Solution, Answer}; pub struct Day10; impl Solution for Day10 { fn part_1(&self, input: &str) -> Answer { - let mut grid: Vec> = input.lines().map(|x| x.chars().collect()).collect(); - let full_animal = grid.iter().flatten().position(|&x| x == 'S').unwrap(); - let animal = (full_animal / grid.len(), full_animal % grid.len()); + let width: i64 = input.lines().next().unwrap().len() as i64; + let grid: Vec<_> = input.lines().collect::>().join("").chars().map(Pipes::from).collect(); - replace_animal(&mut grid, &animal); + let animal = grid.iter().position(|x| x == &Pipes::Animal).unwrap(); - let mut pos = animal; - let mut last: Option<(usize, usize)> = None; + let mut seen: HashSet = HashSet::new(); - let mut len_of_loop = 0; + let mut len = 2; + let mut current ; + + if grid[animal + 1] == Pipes::Horizontal || grid[animal + 1] == Pipes::EtoS || grid[animal + 1] == Pipes::NtoE { + current = animal + 1; + } else if grid[animal - 1] == Pipes::Horizontal || grid[animal - 1] == Pipes::WtoN || grid[animal - 1] == Pipes::StoW { + current = animal - 1; + } else { current = animal + width as usize } + + seen.insert(animal); + seen.insert(current); loop { - len_of_loop += 1; - let tmp = pos; - pos = traverse(&grid[pos.0][pos.1], &pos, &last); - last = Some(tmp); - - if pos == animal {break} + let next = traverse(&grid, current as i64, width); + if next.is_none() { panic!() } + let (next1, next2) = next.unwrap(); + if seen.contains(&next1) && seen.contains(&next2) { + break; + } else if !seen.contains(&next1) { + current = next1; + seen.insert(next1); + len += 1; + } else if !seen.contains(&next2) { + current = next2; + seen.insert(next2); + len += 1; + } else { panic!() } } - Answer::from(len_of_loop / 2) + Answer::from(len) } fn part_2(&self, input: &str) -> Answer { - let mut grid: Vec> = input.lines().map(|x| x.chars().collect()).collect(); - let full_animal = grid.iter().flatten().position(|&x| x == 'S').unwrap(); - let animal = (full_animal / grid.len(), full_animal % grid.len()); + let width: usize = input.lines().next().unwrap().len(); + let mut grid: Vec<_> = input.lines().collect::>().join("").chars().map(Pipes::from).collect(); - replace_animal(&mut grid, &animal); + let animal = grid.iter().position(|x| x == &Pipes::Animal).unwrap(); + replace_animal(&mut grid, animal, width); + println!("{:?}", grid[animal]); - let mut pos = animal; - let mut last: Option<(usize, usize)> = None; + let mut seen: HashSet = HashSet::new(); - let mut in_loop: HashSet<(usize, usize)> = HashSet::new(); + let mut len = 2; + let mut current ; + + if grid[animal + 1] == Pipes::Horizontal || grid[animal + 1] == Pipes::EtoS || grid[animal + 1] == Pipes::NtoE { + current = animal + 1; + } else if grid[animal - 1] == Pipes::Horizontal || grid[animal - 1] == Pipes::WtoN || grid[animal - 1] == Pipes::StoW { + current = animal - 1; + } else { current = animal + width as usize } + + seen.insert(animal); + seen.insert(current); loop { - in_loop.insert(pos); - let tmp = pos; - pos = traverse(&grid[pos.0][pos.1], &pos, &last); - last = Some(tmp); - - if pos == animal {break} + let next = traverse(&grid, current as i64, width as i64); + if next.is_none() { panic!() } + let (next1, next2) = next.unwrap(); + if seen.contains(&next1) && seen.contains(&next2) { + break; + } else if !seen.contains(&next1) { + current = next1; + seen.insert(next1); + len += 1; + } else if !seen.contains(&next2) { + current = next2; + seen.insert(next2); + } else { panic!() } } - let mut total = 0; + let mut parity = grid.chunks(width).map(|_| [0].repeat(width)).collect::>(); - for (i, line) in grid.iter().enumerate() { - let mut parity = false; + for (i, line) in grid.chunks(width).enumerate() { for (j, letter) in line.iter().enumerate() { - if in_loop.contains(&(i, j)) { - if ['|', 'J', 'L'].contains(letter) { parity = !parity } - continue - } - else if parity { - total += 1 + if seen.contains(&(width * i + j)) { + for k in j..width { + parity[i][k] += match letter { + Pipes::Vertical => 2, // using 2 and 1 instead of 1 and .5 for half parity, because integers + Pipes::StoW => 1, + Pipes::NtoE => 1, + Pipes::EtoS => -1, + Pipes::WtoN => -1, + _ => 0 + } + } } } } + for i in seen { + parity[i / width][i % width] = 0; + } - Answer::from(total) + let mut enclosed = 0; + + for line in parity { + enclosed += line.iter().map(|x| (x % 4 / 2)).map(i32::abs).sum::(); + println!("{:?}", line); + } + + Answer::from(enclosed) } } -fn traverse(pipe: &char, pos: &(usize, usize), last: &Option<(usize, usize)>) -> (usize, usize) { - let (offset1, offset2) = match *pipe { - '|' => ((-1, 0), (1, 0)), - '-' => ((0, -1), (0, 1)), - 'J' => ((-1, 0), (0, -1)), - 'L' => ((-1, 0), (0, 1)), - 'F' => ((1, 0), (0, 1)), - '7' => ((1, 0), (0, -1)), - l => panic!("Invalid letter {l}") +#[derive(Debug, PartialEq, Eq)] +enum Pipes { + Ground, + Vertical, + Horizontal, + WtoN, + NtoE, + EtoS, + StoW, + Animal +} + +impl From for Pipes { + fn from(value: char) -> Self { + match value { + '|' => Pipes::Vertical, + '-' => Pipes::Horizontal, + 'J' => Pipes::WtoN, + 'L' => Pipes::NtoE, + 'F' => Pipes::EtoS, + '7' => Pipes::StoW, + '.' => Pipes::Ground, + 'S' => Pipes::Animal, + _ => unreachable!() + } + } +} + +fn traverse(grid: &Vec, current: i64, width: i64) -> Option<(usize, usize)> { + let next1 = current + match grid[current as usize] { + Pipes::Vertical => -width, + Pipes::Horizontal => -1, + Pipes::WtoN => -1, + Pipes::NtoE => -width, + Pipes::EtoS => 1, + Pipes::StoW => width, + Pipes::Animal => 0, + Pipes::Ground => unreachable!() + }; + let next2 = current + match grid[current as usize] { + Pipes::Vertical => width, + Pipes::Horizontal => 1, + Pipes::WtoN => -width, + Pipes::NtoE => 1, + Pipes::EtoS => width, + Pipes::StoW => -1, + Pipes::Animal => 0, + Pipes::Ground => unreachable!() }; - let new1 = ((offset1.0 + pos.0 as i32) as usize, (offset1.1 + pos.1 as i32) as usize); - let new2 = ((offset2.0 + pos.0 as i32) as usize, (offset2.1 + pos.1 as i32) as usize); + // println!("{next1}, {next2}: Current={current}, Width={width}"); - match last { - None => new1, - Some(p) if p == &new1 => new2, - Some(p) if p == &new2 => new1, + // if next1 - current == -1 || next1 - current == width || next1 == -1 || next1 == (grid.len() as i64) { return None }; + // if next2 - current == -1 || next2 - current == width || next2 == -1 || next2 == (grid.len() as i64) { return None }; + + Some((next1 as usize, next2 as usize)) +} + +fn replace_animal(grid: &mut Vec, animal: usize, width: usize) { + let east = grid[animal + 1] == Pipes::Horizontal || grid[animal + 1] == Pipes::WtoN || grid[animal + 1] == Pipes::StoW; + let north = grid[animal - width] == Pipes::Vertical || grid[animal - width] == Pipes::WtoN || grid[animal - width] == Pipes::NtoE; + let west = grid[animal - 1] == Pipes::Horizontal || grid[animal - 1] == Pipes::WtoN || grid[animal - 1] == Pipes::StoW; + let south = grid[animal + width] == Pipes::Vertical || grid[animal + width] == Pipes::StoW || grid[animal + width] == Pipes::EtoS; + + match (east, north, south, west) { + (false, false, true, true) => grid[animal] = Pipes::StoW, + (false, true, true, false) => grid[animal] = Pipes::Vertical, + (true, true, false, false) => grid[animal] = Pipes::NtoE, + (false, true, false, true) => grid[animal] = Pipes::WtoN, + (true, false, true, false) => grid[animal] = Pipes::EtoS, + (true, false, false, true) => grid[animal] = Pipes::Horizontal, _ => unreachable!() } } -fn replace_animal(grid: &mut Vec>, pos: &(usize, usize)) { - let left = match grid.get(pos.0) { Some(x) => x.get((pos.1 as i32 - 1) as usize), _ => None}; - let right = match grid.get(pos.0) { Some(x) => x.get(pos.1 + 1), _ => None}; - let top = match grid.get((pos.0 as i32 - 1) as usize) { Some(x) => x.get(pos.1), _ => None}; - let bottom = match grid.get(pos.0 + 1) { Some(x) => x.get(pos.1), _ => None}; - - grid[pos.0][pos.1] = match (left, right, top, bottom) { - (Some(x), Some(y), _, _) if ['-', 'L', 'F'].contains(x) && ['-', 'J', '7'].contains(y) => '-', - (Some(x), _, Some(y), _) if ['-', 'L', 'F'].contains(x) && ['|', '7', 'F'].contains(y) => 'J', - (Some(x), _, _, Some(y)) if ['-', 'L', 'F'].contains(x) && ['|', 'J', 'L'].contains(y) => '7', - (_, Some(x), Some(y), _) if ['-', 'J', '7'].contains(x) && ['|', '7', 'F'].contains(y) => 'L', - (_, Some(x), _, Some(y)) if ['-', 'J', '7'].contains(x) && ['|', 'J', 'L'].contains(y) => 'F', - (_, _, Some(x), Some(y)) if ['|', '7', 'F'].contains(x) && ['|', 'J', 'L'].contains(y) => '|', - _ => unreachable!() - } -} diff --git a/aoc2023/src/day11.rs b/aoc2023/src/day11.rs index ae38301..2e4945b 100644 --- a/aoc2023/src/day11.rs +++ b/aoc2023/src/day11.rs @@ -1,55 +1,13 @@ use shared::{Solution, Answer}; -use itertools::Itertools; pub struct Day11; impl Solution for Day11 { fn part_1(&self, input: &str) -> Answer { - Answer::from(solve(input, 2)) + Answer::Unimplemented } fn part_2(&self, input: &str) -> Answer { - Answer::from(solve(input, 1_000_000)) + Answer::Unimplemented } } - -fn solve(input: &str, inflation_arg: usize) -> u64 { - let grid = input.lines().map(|line| line.chars().map(|x| x == '#').collect::>()).collect::>(); - - let mut distances = vec![vec![1; grid[0].len()]; grid.len()]; - - let mut empty_col = vec![true; grid[0].len()]; - for (i, line) in grid.iter().enumerate() { - empty_col = empty_col.iter().enumerate().map(|(i, &l)| l && !line[i]).collect(); - if !line.iter().any(|&x| x) { - distances[i] = vec![inflation_arg; line.len()]; - } - } - - for (i, &col) in empty_col.iter().enumerate() { - if col { - for j in 0..distances.len() { - distances[j][i] = inflation_arg; - } - } - } - - let galaxies: Vec<(usize, usize)> = grid.iter(). - enumerate(). - map(|(i, line)| line.iter() - .enumerate() - .filter_map(|(j, &x)| if x { Some((i, j)) } else { None }) - .collect::>() - ).flatten().collect::>(); - - galaxies.iter().permutations(2).map(|x| { - let (x1, y1): (usize, usize) = *x[0]; - let (x2, y2): (usize, usize) = *x[1]; - let mut out = 0; - let xrange = x1.min(x2)..x1.max(x2); - let yrange = if y1 <= y2 { y1..y2 } else { (y2+1)..(y1+1) }; - for i in xrange { out += distances[i][if x1 <= x2 {y1} else {y2}] } - for j in yrange { out += distances[x1.min(x2)][j] } - out as u64 - } ).sum::() / 2 -} diff --git a/aoc2023/src/day12.rs b/aoc2023/src/day12.rs index 95e23cb..c82f532 100644 --- a/aoc2023/src/day12.rs +++ b/aoc2023/src/day12.rs @@ -1,74 +1,13 @@ -use std::iter; -use std::iter::zip; -use itertools::Itertools; use shared::{Solution, Answer}; pub struct Day12; impl Solution for Day12 { fn part_1(&self, input: &str) -> Answer { - let lines = input.lines() - .map(|x| x.split_once(' ').unwrap().0.chars().collect()) - .collect::>>(); - let groups: Vec> = input.lines() - .map(|x| x.split_once(' ').unwrap().1.split(',').map(|y| y.parse::().unwrap()).collect()) - .collect(); - - let mut out = 0; - - for (line, group) in zip(lines, groups) { - out += solve(&line, &group) - } - - Answer::from(out) + Answer::Unimplemented } fn part_2(&self, input: &str) -> Answer { - let lines = input.lines() - .map(|x| iter::repeat(x.split_once(' ').unwrap().0).take(5).join("?").chars().collect()) - .collect::>>(); - let groups: Vec> = input.lines() - .map(|x| iter::repeat( - x.split_once(' ') - .unwrap().1 - .split(',') - .map(|a| a.parse::().unwrap()) - ).take(5) - .flatten() - .collect() - ).collect(); - - let mut out = 0; - - for (line, group) in zip(lines, groups) { - out += solve(&line, &group) - } - - Answer::from(out) + Answer::Unimplemented } } - -fn solve(springs: &Vec, groups: &Vec) -> u64 { - let n = springs.len(); - let m = groups.len(); - - let mut possibilities: Vec> = vec![vec![0; m+1]; n+1]; - - possibilities[0][0] = 1; - - for i in 0..n { - for j in 0..=m { - if springs[i] != '#' { - possibilities[i+1][j] += possibilities[i][j]; - } - if j < m - && (i + groups[j] as usize) <= n - && !springs[i..(i + groups[j] as usize)].contains(&'.') - && (i + groups[j] as usize >= n || springs[i + groups[j] as usize ] != '#') { - possibilities[(i + groups[j] as usize + 1).min(n)][j+1] += possibilities[i][j] - } - } - } - - possibilities[n][m] -} diff --git a/flake.lock b/flake.lock deleted file mode 100644 index e77b2e6..0000000 --- a/flake.lock +++ /dev/null @@ -1,80 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1703134684, - "narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "d6863cbcbbb80e71cecfc03356db1cda38919523", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" - } - }, - "rust-overlay": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1689302058, - "narHash": "sha256-yD74lcHTrw4niXcE9goJLbzsgyce48rQQoy5jK5ZK40=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "7b8dbbf4c67ed05a9bf3d9e658c12d4108bc24c8", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 11a3b21..0000000 --- a/flake.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ - description = "A Nix-flake-based Rust development environment"; - - inputs = { - rust-overlay = { - url = "github:oxalica/rust-overlay"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { - self, - nixpkgs, - rust-overlay, - }: let - overlays = [ - rust-overlay.overlays.default - (final: prev: { - rustToolchain = let - rust = prev.rust-bin; - in - if builtins.pathExists ./rust-toolchain.toml - then rust.fromRustupToolchainFile ./rust-toolchain.toml - else if builtins.pathExists ./rust-toolchain - then rust.fromRustupToolchainFile ./rust-toolchain - else rust.stable.latest.default; - }) - ]; - supportedSystems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; - forEachSupportedSystem = f: - nixpkgs.lib.genAttrs supportedSystems (system: - f { - pkgs = import nixpkgs {inherit overlays system;}; - }); - in { - devShells = forEachSupportedSystem ({pkgs}: { - default = pkgs.mkShell { - packages = with pkgs; [ - rustToolchain - openssl - pkg-config - cargo-deny - cargo-edit - cargo-watch - rust-analyzer - ]; - }; - }); - }; -} diff --git a/src/main.rs b/src/main.rs index 61a10dd..8ac0506 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,21 @@ use shared::Solution; -use shared::load_actual; +use aoc2023; +use shared::{load_actual}; use std::time::Instant; fn main() { let now = Instant::now(); - let Ok(data) = load_actual(2023, 12) + let Ok(data) = load_actual(2023, 10) else { panic!("No Input Data"); }; - let result = aoc2023::day12::Day12.part_2(&data); + let result = aoc2023::day10::Day10.part_2(&data); let elapsed = now.elapsed(); println!("{}", result); println!("{:?}", elapsed); -} +} \ No newline at end of file