diff --git a/.idea/AdventOfCode.iml b/.idea/AdventOfCode.iml
index a1dbddc..4661634 100644
--- a/.idea/AdventOfCode.iml
+++ b/.idea/AdventOfCode.iml
@@ -5,6 +5,8 @@
+
+
diff --git a/Cargo.lock b/Cargo.lock
index 06c327d..7219f43 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,6 +23,7 @@ dependencies = [
name = "aoc2023"
version = "0.1.0"
dependencies = [
+ "itertools",
"regex",
"shared",
"substring",
@@ -34,6 +35,21 @@ 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 0b76455..81acc05 100644
--- a/aoc2023/Cargo.toml
+++ b/aoc2023/Cargo.toml
@@ -6,4 +6,5 @@ edition = "2021"
[dependencies]
shared = { path = "../shared" }
regex = "1.10.2"
-substring = "1.4.5"
\ No newline at end of file
+substring = "1.4.5"
+itertools = "0.12.0"
\ No newline at end of file
diff --git a/aoc2023/src/day05.rs b/aoc2023/src/day05.rs
index 5567f84..e315821 100644
--- a/aoc2023/src/day05.rs
+++ b/aoc2023/src/day05.rs
@@ -133,10 +133,6 @@ 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 517959e..afa65a1 100644
--- a/aoc2023/src/day10.rs
+++ b/aoc2023/src/day10.rs
@@ -1,193 +1,108 @@
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 width: i64 = input.lines().next().unwrap().len() as i64;
- let grid: Vec<_> = input.lines().collect::>().join("").chars().map(Pipes::from).collect();
+ 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 animal = grid.iter().position(|x| x == &Pipes::Animal).unwrap();
+ replace_animal(&mut grid, &animal);
- let mut seen: HashSet = HashSet::new();
+ let mut pos = animal;
+ let mut last: Option<(usize, usize)> = None;
- 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);
+ let mut len_of_loop = 0;
loop {
- 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!() }
+ len_of_loop += 1;
+ let tmp = pos;
+ pos = traverse(&grid[pos.0][pos.1], &pos, &last);
+ last = Some(tmp);
+
+ if pos == animal {break}
}
- Answer::from(len)
+ Answer::from(len_of_loop / 2)
}
fn part_2(&self, input: &str) -> Answer {
- let width: usize = input.lines().next().unwrap().len();
- let mut grid: Vec<_> = input.lines().collect::>().join("").chars().map(Pipes::from).collect();
+ 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 animal = grid.iter().position(|x| x == &Pipes::Animal).unwrap();
- replace_animal(&mut grid, animal, width);
- println!("{:?}", grid[animal]);
+ replace_animal(&mut grid, &animal);
- let mut seen: HashSet = HashSet::new();
+ let mut pos = animal;
+ let mut last: Option<(usize, usize)> = None;
- 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);
+ let mut in_loop: HashSet<(usize, usize)> = HashSet::new();
loop {
- 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!() }
+ in_loop.insert(pos);
+ let tmp = pos;
+ pos = traverse(&grid[pos.0][pos.1], &pos, &last);
+ last = Some(tmp);
+
+ if pos == animal {break}
}
- let mut parity = grid.chunks(width).map(|_| [0].repeat(width)).collect::>();
+ let mut total = 0;
- for (i, line) in grid.chunks(width).enumerate() {
+ for (i, line) in grid.iter().enumerate() {
+ let mut parity = false;
for (j, letter) in line.iter().enumerate() {
- 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
- }
- }
+ if in_loop.contains(&(i, j)) {
+ if ['|', 'J', 'L'].contains(letter) { parity = !parity }
+ continue
+ }
+ else if parity {
+ total += 1
}
}
}
- for i in seen {
- parity[i / width][i % width] = 0;
- }
- 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)
+ Answer::from(total)
}
}
-#[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!()
+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}")
};
- // println!("{next1}, {next2}: Current={current}, Width={width}");
+ 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);
- // 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,
+ match last {
+ None => new1,
+ Some(p) if p == &new1 => new2,
+ Some(p) if p == &new2 => new1,
_ => 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 2e4945b..ae38301 100644
--- a/aoc2023/src/day11.rs
+++ b/aoc2023/src/day11.rs
@@ -1,13 +1,55 @@
use shared::{Solution, Answer};
+use itertools::Itertools;
pub struct Day11;
impl Solution for Day11 {
fn part_1(&self, input: &str) -> Answer {
- Answer::Unimplemented
+ Answer::from(solve(input, 2))
}
fn part_2(&self, input: &str) -> Answer {
- Answer::Unimplemented
+ Answer::from(solve(input, 1_000_000))
}
}
+
+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 c82f532..95e23cb 100644
--- a/aoc2023/src/day12.rs
+++ b/aoc2023/src/day12.rs
@@ -1,13 +1,74 @@
+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 {
- Answer::Unimplemented
+ 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)
}
fn part_2(&self, input: &str) -> Answer {
- Answer::Unimplemented
+ 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)
}
}
+
+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
new file mode 100644
index 0000000..e77b2e6
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,80 @@
+{
+ "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
new file mode 100644
index 0000000..11a3b21
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,50 @@
+{
+ 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 8ac0506..61a10dd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,21 +1,20 @@
use shared::Solution;
-use aoc2023;
-use shared::{load_actual};
+use shared::load_actual;
use std::time::Instant;
fn main() {
let now = Instant::now();
- let Ok(data) = load_actual(2023, 10)
+ let Ok(data) = load_actual(2023, 12)
else { panic!("No Input Data"); };
- let result = aoc2023::day10::Day10.part_2(&data);
+ let result = aoc2023::day12::Day12.part_2(&data);
let elapsed = now.elapsed();
println!("{}", result);
println!("{:?}", elapsed);
-}
\ No newline at end of file
+}