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:
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) = ¤t_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"