aoc

My solutions for the Advent of Code
git clone https://git.tronto.net/aoc
Download | Log | Files | Refs | README

commit fa26f694d8cf98272763952a698301ee9935c19e
parent f0159b7d36e2c81182f2d047a34bf883394894db
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Thu, 26 Jun 2025 09:47:23 +0200

Added 2022, first few problems

Diffstat:
A2022/01/a.rs | 8++++++++
A2022/01/b.rs | 9+++++++++
A2022/01/common.rs | 18++++++++++++++++++
A2022/02/a.rs | 13+++++++++++++
A2022/02/b.rs | 13+++++++++++++
A2022/02/common.rs | 10++++++++++
A2022/03/a.rs | 26++++++++++++++++++++++++++
A2022/03/b.rs | 29+++++++++++++++++++++++++++++
A2022/03/common.rs | 7+++++++
A2022/04/a.rs | 7+++++++
A2022/04/b.rs | 8++++++++
A2022/04/common.rs | 20++++++++++++++++++++
A2022/05/a.rs | 17+++++++++++++++++
A2022/05/b.rs | 20++++++++++++++++++++
A2022/05/common.rs | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2022/06/a.rs | 9+++++++++
A2022/06/b.rs | 9+++++++++
A2022/06/common.rs | 20++++++++++++++++++++
A2022/07/a.rs | 12++++++++++++
A2022/07/b.rs | 13+++++++++++++
A2022/07/common.rs | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2022/08/a.rs | 42++++++++++++++++++++++++++++++++++++++++++
A2022/08/b.rs | 47+++++++++++++++++++++++++++++++++++++++++++++++
A2022/08/common.rs | 13+++++++++++++
A2022/09/a.rs | 6++++++
A2022/09/b.rs | 6++++++
A2022/09/common.rs | 46++++++++++++++++++++++++++++++++++++++++++++++
A2022/README.md | 12++++++++++++
A2022/run.sh | 12++++++++++++
29 files changed, 655 insertions(+), 0 deletions(-)

diff --git a/2022/01/a.rs b/2022/01/a.rs @@ -0,0 +1,8 @@ +mod common; +use common::*; + +fn main() { + let e = get_elves_from_stdin(); + let m: i64 = e.iter().map(|a| a.iter().sum()).max().unwrap(); + println!("{m}"); +} diff --git a/2022/01/b.rs b/2022/01/b.rs @@ -0,0 +1,9 @@ +mod common; +use common::*; + +fn main() { + let e = get_elves_from_stdin(); + let mut w = e.iter().map(|a| a.iter().sum()).collect::<Vec<i64>>(); + w.sort_by(|a, b| b.cmp(a)); + println!("{}", w[0] + w[1] + w[2]); +} diff --git a/2022/01/common.rs b/2022/01/common.rs @@ -0,0 +1,18 @@ +pub fn get_elves_from_stdin() -> Vec<Vec<i64>> { + let mut e = Vec::<Vec::<i64>>::new(); + e.push(Vec::<i64>::new()); + let mut line = String::new(); + loop { + match std::io::stdin().read_line(&mut line).unwrap() { + 0 => break, + 1 => e.push(Vec::<i64>::new()), + _ => { + let n = line.trim().parse::<i64>().unwrap(); + let last = e.len() - 1; + e[last].push(n); + } + } + line.clear(); + } + e +} diff --git a/2022/02/a.rs b/2022/02/a.rs @@ -0,0 +1,13 @@ +mod common; +use common::*; + +pub fn main() { + let play_score = |s: &[u8]| (s[2] as i64) - ('X' as i64) + 1; + let win_score = |s: &[u8]| { + let other = s[0] as i64 - ('A' as i64); + let me = s[2] as i64 - ('X' as i64); + 3 * ((me - other + 4) % 3) + }; + let score = get_score(play_score, win_score); + println!("{score}"); +} diff --git a/2022/02/b.rs b/2022/02/b.rs @@ -0,0 +1,13 @@ +mod common; +use common::*; + +pub fn main() { + let play_score = |s: &[u8]| { + let other = s[0] as i64 - ('A' as i64); + let w = s[2] as i64 - ('X' as i64); + (other + w + 2) % 3 + 1 + }; + let win_score = |s: &[u8]| 3 * ((s[2] as i64) - ('X' as i64)); + let score = get_score(play_score, win_score); + println!("{score}"); +} diff --git a/2022/02/common.rs b/2022/02/common.rs @@ -0,0 +1,10 @@ +pub fn get_score(play_score: fn(&[u8]) -> i64, win_score: fn(&[u8]) -> i64) -> i64 { + let mut score = 0; + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + let l = line.as_bytes(); + score += play_score(l) + win_score(l); + line.clear(); + } + score +} diff --git a/2022/03/a.rs b/2022/03/a.rs @@ -0,0 +1,26 @@ +mod common; +use common::*; + +fn get_rep_val(rucksack: &[u8]) -> i64 { + let mut seen = [false; 256]; + let mid = rucksack.len()/2; + for i in 0..mid { + seen[rucksack[i] as usize] = true; + } + for i in mid..rucksack.len() { + if seen[rucksack[i] as usize] { + return value(rucksack[i] as char); + } + } + panic!("Could not find repeated char"); +} + +fn main() { + let mut sum = 0; + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + sum += get_rep_val(line.as_bytes()); + line.clear(); + } + println!("{sum}"); +} diff --git a/2022/03/b.rs b/2022/03/b.rs @@ -0,0 +1,29 @@ +mod common; +use common::*; + +fn get_rep_val(e: &[String; 3]) -> i64 { + let mut seen = [[false; 256]; 2]; + for i in 0..2 { + for c in e[i].chars() { + seen[i][c as usize] = true; + } + } + for c in e[2].chars() { + if seen[0][c as usize] && seen[1][c as usize] { + return value(c); + } + } + panic!("Could not find repeated char"); +} + +fn main() { + let mut sum = 0; + let mut lines: [String; 3] = Default::default(); + while std::io::stdin().read_line(&mut lines[0]).unwrap() > 0 { + let _ = std::io::stdin().read_line(&mut lines[1]); + let _ = std::io::stdin().read_line(&mut lines[2]); + sum += get_rep_val(&lines); + for l in &mut lines { l.clear(); } + } + println!("{sum}"); +} diff --git a/2022/03/common.rs b/2022/03/common.rs @@ -0,0 +1,7 @@ +pub fn value(c: char) -> i64 { + match c { + 'a'..='z' => (c as i64) - ('a' as i64) + 1, + 'A'..='Z' => (c as i64) - ('A' as i64) + 27, + _ => panic!("Error: unexpected character '{}'", c) + } +} diff --git a/2022/04/a.rs b/2022/04/a.rs @@ -0,0 +1,7 @@ +mod common; +use common::*; + +fn main() { + let contained = |a: (i64, i64), b: (i64, i64)| a.0 >= b.0 && a.1 <= b.1; + println!("{}", count(|a, b| contained(a, b) || contained(b, a))); +} diff --git a/2022/04/b.rs b/2022/04/b.rs @@ -0,0 +1,8 @@ +mod common; +use common::*; + +fn main() { + let overlap = |a: (i64, i64), b: (i64, i64)| + (a.0 >= b.0 && a.0 <= b.1) || (b.0 >= a.0 && b.0 <= a.1); + println!("{}", count(overlap)); +} diff --git a/2022/04/common.rs b/2022/04/common.rs @@ -0,0 +1,20 @@ +fn get_ints(line: &str) -> ((i64, i64), (i64, i64)) { + let i = line.find('-').unwrap(); + let j = line.find(',').unwrap(); + let k = line[j..].find('-').unwrap() + j; + ((line[..i].parse().unwrap(), line[i+1..j].parse().unwrap()), + (line[j+1..k].parse().unwrap(), line[k+1..line.len()-1].parse().unwrap())) +} + +pub fn count<F: Fn((i64, i64), (i64, i64)) -> bool>(condition: F) -> i64 { + let mut line = String::new(); + let mut sum = 0; + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + let (a, b) = get_ints(&line); + if condition(a, b) { + sum += 1; + } + line.clear(); + } + sum +} diff --git a/2022/05/a.rs b/2022/05/a.rs @@ -0,0 +1,17 @@ +mod common; +use common::*; + +fn apply_move(grid: &mut Vec::<Vec::<char>>, line: &str) { + let (n, from, to) = read_move(&line); + + for _ in 0..n { + let x = grid[from-1].pop().unwrap(); + grid[to-1].push(x); + } +} + +fn main() { + let mut grid = get_grid_from_stdin(); + apply_moves_from_stdin(&mut grid, apply_move); + print_top(&grid); +} diff --git a/2022/05/b.rs b/2022/05/b.rs @@ -0,0 +1,20 @@ +mod common; +use common::*; + +fn apply_move(grid: &mut Vec::<Vec::<char>>, line: &str) { + let (n, from, to) = read_move(&line); + + let first_moved = grid[from-1].len()-n; + let len = grid[from-1].len(); + for i in first_moved..len { + let x = grid[from-1][i]; + grid[to-1].push(x); + } + grid[from-1].drain(first_moved..len); +} + +fn main() { + let mut grid = get_grid_from_stdin(); + apply_moves_from_stdin(&mut grid, apply_move); + print_top(&grid); +} diff --git a/2022/05/common.rs b/2022/05/common.rs @@ -0,0 +1,73 @@ +pub type Grid = Vec<Vec<char>>; + +fn read_rows() -> Vec<Vec<char>> { + let mut rows = Vec::<Vec::<char>>::new(); + let mut line = String::new(); + loop { + let mut row = Vec::<char>::new(); + + std::io::stdin().read_line(&mut line).unwrap(); + let line_chars = line.as_bytes(); + if line_chars[1] == '1' as u8 { + let _ = std::io::stdin().read_line(&mut line); + break; + } + + let mut i = 0; + while i < line_chars.len() { + row.push(line_chars[i+1] as char); + i += 4; + } + + rows.push(row); + line.clear(); + } + + rows +} + +fn rows_to_grid(rows: &Vec<Vec<char>>) -> Grid { + let mut grid = vec![Vec::<char>::new(); rows[0].len()]; + for row in rows.iter().rev() { + for i in 0..row.len() { + if row[i] != ' ' { + grid[i].push(row[i]); + } + } + } + grid +} + +fn next_usize(line: &str, s: usize) -> (usize, usize) { + let i = line[s..].find(|c: char| c.is_digit(10)).unwrap() + s; + let j = line[i..].find(|c: char| c.is_whitespace()).unwrap() + i; + (line[i..j].parse::<usize>().unwrap(), j) +} + +pub fn read_move(line: &str) -> (usize, usize, usize) { + let (n, i) = next_usize(line, 0); + let (from, i) = next_usize(line, i); + let (to, _) = next_usize(line, i); + + (n, from, to) +} + +pub fn get_grid_from_stdin() -> Grid { + rows_to_grid(&read_rows()) +} + +pub fn apply_moves_from_stdin<F: Fn(&mut Grid, &str) -> ()>(grid: &mut Grid, apply_move: F) { + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + apply_move(grid, &line); + line.clear(); + } +} + +pub fn print_top(grid: &Grid) { + for column in grid { + let c = match column.last() { Some(d) => d, None => &'_' }; + print!("{}", c); + } + println!(); +} diff --git a/2022/06/a.rs b/2022/06/a.rs @@ -0,0 +1,9 @@ +mod common; +use common::*; + +fn main() { + let mut line = String::new(); + std::io::stdin().read_line(&mut line).unwrap(); + let i = first_index_n_distinct(line.as_bytes(), 4) + 1; + println!("{i}"); +} diff --git a/2022/06/b.rs b/2022/06/b.rs @@ -0,0 +1,9 @@ +mod common; +use common::*; + +fn main() { + let mut line = String::new(); + std::io::stdin().read_line(&mut line).unwrap(); + let i = first_index_n_distinct(line.as_bytes(), 14) + 1; + println!("{i}"); +} diff --git a/2022/06/common.rs b/2022/06/common.rs @@ -0,0 +1,20 @@ +fn all_distinct<T: PartialEq>(a: &[T]) -> bool { + for i in 0..a.len() { + for j in i+1..a.len() { + if a[i] == a[j] { + return false; + } + } + } + return true; +} + +pub fn first_index_n_distinct(a: &[u8], n: usize) -> usize { + assert!(n > 0, "{} must be greater than 0", n); + for i in 0..a.len()-n+1 { + if all_distinct(&a[i..i+n]) { + return i+n-1; + } + } + panic!("Cannot find {} distinct in a row", n); +} diff --git a/2022/07/a.rs b/2022/07/a.rs @@ -0,0 +1,12 @@ +mod common; +use common::*; + +fn main() { + let fs = FileSystem::build_from_stdin(); + let sum: usize = fs.iter() + .filter(|f| matches!(f.kind, FileType::Directory(_))) + .map(|f| f.real_size(&fs)) + .filter(|s| *s <= 100000) + .sum(); + println!("{sum}"); +} diff --git a/2022/07/b.rs b/2022/07/b.rs @@ -0,0 +1,13 @@ +mod common; +use common::*; + +fn main() { + let fs = FileSystem::build_from_stdin(); + let remaining = 70000000 - fs[0].real_size(&fs); + let ds: usize = fs.iter() + .filter(|f| matches!(f.kind, FileType::Directory(_))) + .map(|f| f.real_size(&fs)) + .filter(|s| *s + remaining >= 30000000) + .min().unwrap(); + println!("{ds}"); +} diff --git a/2022/07/common.rs b/2022/07/common.rs @@ -0,0 +1,130 @@ +use std::ops; + +pub enum FileType { + File(usize), + Directory(Vec<usize>) +} + +pub struct File { + pub kind: FileType, + name: String +} + +impl File { + pub fn real_size(&self, fs: &FileSystem) -> usize { + match &self.kind { + FileType::File(s) => *s, + FileType::Directory(c) => + c.iter().map(|x: &usize| fs[*x].real_size(&fs)).sum() + } + } +} + +pub struct FileSystem { + files: Vec<File> +} + +impl FileSystem { + fn new() -> Self { + Self { + files: vec![File { + kind: FileType::Directory(Vec::<usize>::new()), + name: String::from("/") + }] + } + } + + pub fn build_from_stdin() -> Self { + let mut fs = FileSystem::new(); + let mut path = Path::new(); + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + if &line[..5] == "$ cd " { + exec_cd(&line[5..], &mut path, &fs); + } else if &line[..3] == "dir" { + fs.add_dir(&line[4..line.len()-1], path.last()); + } else if line.as_bytes()[0] != '$' as u8 { + let i = line.find(' ').unwrap(); + let size = line[0..i].parse::<usize>().unwrap(); + fs.add_file(&line[i+1..line.len()-1], size, path.last()); + } + line.clear(); + } + fs + } + + fn make_parent(&mut self, id: usize, parent: usize) { + let p = &mut self.files[parent]; + if let FileType::Directory(v) = &mut p.kind { + v.push(id); + } else { + panic!("Parent is not a directory"); + } + } + + fn add_dir(&mut self, name: &str, parent: usize) { + let id = self.files.len(); + self.files.push( + File { + kind: FileType::Directory(Vec::<usize>::new()), + name: String::from(name) + } + ); + self.make_parent(id, parent); + } + + fn add_file(&mut self, name: &str, size: usize, parent: usize) { + let id = self.files.len(); + self.files.push( + File { + kind: FileType::File(size), + name: String::from(name) + } + ); + self.make_parent(id, parent); + } + + pub fn iter(&self) -> impl Iterator<Item = &File>{ + self.files.iter() + } +} + +impl ops::Index<usize> for FileSystem { + type Output = File; + fn index(&self, i: usize) -> &File { + &self.files[i] + } +} + +struct Path { + stack: Vec<usize> +} + +impl Path { + fn new() -> Self { Self { stack: vec![0] } } // 0 is the id of "/" + fn last(&self) -> usize { *self.stack.last().unwrap() } + fn clear(&mut self) { self.stack.drain(1..); } + fn pop(&mut self) { self.stack.pop(); } + fn push(&mut self, dir_id: usize) { self.stack.push(dir_id); } +} + +fn exec_cd(line: &str, path: &mut Path, fs: &FileSystem) { + if line.as_bytes()[0] == '/' as u8 { + path.clear(); + } else if &line[..2] == ".." { + path.pop(); + } else { + let current_dir = &fs[path.last()]; + if let FileType::Directory(children) = &current_dir.kind { + for c in children { + if fs[*c].name == &line[..line.len()-1] { + path.push(*c); + return; + } + } + } else { + panic!("Non-directory in path"); + } + panic!("Directory not found in current path"); + } +} diff --git a/2022/08/a.rs b/2022/08/a.rs @@ -0,0 +1,42 @@ +use std::cmp::max; +mod common; +use common::*; + +fn mark_visible(grid: &mut Grid) { + let update = |elem: &mut (i8, bool), m: &mut i8| { + elem.1 = elem.1 || elem.0 > *m; + *m = max(*m, elem.0); + }; + + for row in &mut *grid { + // From left + let mut m = -1; + for t in &mut *row { update(t, &mut m); } + + // From right + let mut m = -1; + for t in &mut row.iter_mut().rev() { update(t, &mut m); } + } + + for j in 0..grid[0].len() { + // From top + let mut m = -1; + for i in 0..grid.len() { update(&mut grid[i][j], &mut m); } + + // From bottom + let mut m = -1; + for i in (0..grid.len()).rev() { update(&mut grid[i][j], &mut m); } + } +} + +fn count_visible(grid: &Grid) -> usize { + grid.iter() + .map(|row| row.iter().filter(|c| c.1).count()) + .sum() +} + +fn main() { + let mut grid = read_grid_from_stdin(); + mark_visible(&mut grid); + println!("{}", count_visible(&grid)); +} diff --git a/2022/08/b.rs b/2022/08/b.rs @@ -0,0 +1,47 @@ +use std::cmp::max; +mod common; +use common::*; + +fn count_visible_from(grid: &Grid, i0: usize, j0: usize) -> usize { + let x = grid[i0][j0].0; + let mut top = 0; + for i in (0..i0).rev() { + top += 1; + if grid[i][j0].0 >= x { break; } + } + + let mut bottom = 0; + for i in i0+1..grid.len() { + bottom += 1; + if grid[i][j0].0 >= x { break; } + } + + let mut left = 0; + for j in (0..j0).rev() { + left += 1; + if grid[i0][j].0 >= x { break; } + } + + let mut right = 0; + for j in j0+1..grid[0].len() { + right += 1; + if grid[i0][j].0 >= x { break; } + } + + top * bottom * left * right +} + +fn max_view_factor(grid: &Grid) -> usize { + let mut m = 0; + for i in 0..grid.len() { + for j in 0..grid[0].len() { + m = max(m, count_visible_from(grid, i, j)); + } + } + m +} + +fn main() { + let grid = read_grid_from_stdin(); + println!("{}", max_view_factor(&grid)); +} diff --git a/2022/08/common.rs b/2022/08/common.rs @@ -0,0 +1,13 @@ +pub type Grid = Vec<Vec<(i8, bool)>>; + +pub fn read_grid_from_stdin() -> Grid { + let mut grid = Vec::<Vec<(i8, bool)>>::new(); + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + let row = line[..line.len()-1].as_bytes().iter() + .map(|b| ((*b as i8) - ('0' as i8), false)).collect(); + grid.push(row); + line.clear(); + } + grid +} diff --git a/2022/09/a.rs b/2022/09/a.rs @@ -0,0 +1,6 @@ +mod common; +use common::*; + +fn main() { + println!("{}", simulate(2).len()); +} diff --git a/2022/09/b.rs b/2022/09/b.rs @@ -0,0 +1,6 @@ +mod common; +use common::*; + +fn main() { + println!("{}", simulate(10).len()); +} diff --git a/2022/09/common.rs b/2022/09/common.rs @@ -0,0 +1,46 @@ +use std::collections::HashSet; + +fn get_dir(c: char) -> (i64, i64) { + match c { + 'R' => (1, 0), + 'L' => (-1, 0), + 'U' => (0, 1), + 'D' => (0, -1), + _ => panic!("Unexpected char") + } +} + +fn follow(lead: (i64, i64), trail: (i64, i64)) -> (i64, i64) { + let max1 = |x: i64| x / (2 - (x%2).abs()); + let d = (max1(lead.0 - trail.0), max1(lead.1 - trail.1)); + (trail.0 + d.0, trail.1 + d.1) +} + +fn step(r: &mut Vec<(i64, i64)>, d: (i64, i64)) { + r[0] = (r[0].0 + d.0, r[0].1 + d.1); + for i in 1..r.len() { + if (r[i].0 - r[i-1].0).abs() > 1 || (r[i].1 - r[i-1].1).abs() > 1 { + r[i] = follow(r[i-1], r[i]); + } else { + break; + } + } +} + +pub fn simulate(n: usize) -> HashSet<(i64, i64)> { + let mut r = vec![(0, 0); n]; + let mut visited = HashSet::<(i64, i64)>::new(); + visited.insert(r[n-1]); + + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 0 { + let dir = get_dir(line.chars().nth(0).unwrap()); + let l = line[2..line.len()-1].parse::<usize>().unwrap(); + for _ in 0..l { + step(&mut r, dir); + visited.insert(r[n-1]); + } + line.clear(); + } + visited +} diff --git a/2022/README.md b/2022/README.md @@ -0,0 +1,12 @@ +# Advent of Code 2022 + +I did not solve these problems in 2022, but in 2025, as a playground +to learn Rust. + +## Usage + +To run this code you'll need the `rustc` compiler (no `cargo`). + +From this folder run: `./run.sh day part`, for example `./run.sh 03 a`. +Input is read from standard input; paste your input in the terminal or +read it from a file with e.g. `<input.txt`. diff --git a/2022/run.sh b/2022/run.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +usage() { + echo "Usage: $0 day part" + echo "Example: $0 07 b" +} + +[ -n "$1" ] && [ -n "$2" ] || (usage; exit 1) +[ -d "$1" ] || (echo "Directory $1 does not exist"; exit 1) +[ -f "$1/$2.rs" ] || (echo "File $1/$2.rs does not exist"; exit 2) + +rustc "$1/$2.rs" -o "$1/$2.out" && time "./$1/$2.out"