aoc

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

commit dbb5a4e5711fba371fbb011356bcd8a28cc963e4
parent 2641a35d5473beb6889b299b54de294fbf782b1f
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Tue,  8 Jul 2025 17:10:53 +0200

Day 22 2022

Diffstat:
A2022/22/a.rs | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2022/22/b.rs | 314+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2022/22/common.rs | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 506 insertions(+), 0 deletions(-)

diff --git a/2022/22/a.rs b/2022/22/a.rs @@ -0,0 +1,81 @@ +use std::cmp::max; +mod common; +use common::*; + +#[derive(Copy, Clone, Debug)] +pub struct Position { + pub i: usize, + pub j: usize, + pub d: Direction +} + +pub struct Map { + pub tiles: Vec<Vec<Tile>> +} + +type StepFn = fn(&Map, Position) -> Position; + +impl Map { + pub fn from_stdin() -> Map { + let mut maxlen = 0; + let mut tiles = vec![]; + let mut line = String::new(); + while std::io::stdin().read_line(&mut line).unwrap() > 1 { + let v = line[0..line.len()-1].as_bytes().iter() + .map(|b| Tile::from_byte(*b)).collect::<Vec<_>>(); + maxlen = max(maxlen, v.len()); + tiles.push(v); + line.clear(); + } + for v in &mut tiles { + while v.len() < maxlen { v.push(Tile::Skip); } + } + Map { tiles } + } + + pub fn start_position(&self) -> Position { + let first_valid = self.tiles[0].iter() + .position(|t| *t == Tile::Walk).unwrap(); + Position { i: 0, j: first_valid, d: Direction::Right } + } + + pub fn walk(&self, pos: Position, n: usize, step: StepFn) -> Position { + let mut p = pos; + for _ in 0..n { p = step(self, p); } + p + } +} + +fn step(map: &Map, position: Position) -> Position { + let imax = map.tiles.len(); + let jmax = map.tiles[0].len(); + let s = position.d.step(); + let mut p = position; + loop { + let inext = ((s.0 + imax as i64) as usize + p.i) % imax; + let jnext = ((s.1 + jmax as i64) as usize + p.j) % jmax; + p = Position { i: inext, j: jnext, d: p.d }; + match map.tiles[p.i][p.j] { + Tile::Walk => return p, + Tile::Wall => return position, + Tile::Skip => () + } + } +} + +fn password(p: Position) -> usize { + 1000 * (p.i+1) + 4 * (p.j+1) + p.d.value() +} + +fn main() { + let map = Map::from_stdin(); + let (turns, steps) = read_instruction_line_from_stdin(); + let mut position = map.start_position(); + + for i in 0..turns.len() { + position = map.walk(position, steps[i], step); + position.d = position.d.turn(turns[i]); + } + position = map.walk(position, steps[steps.len()-1], step); + println!("{}", password(position)); +} diff --git a/2022/22/b.rs b/2022/22/b.rs @@ -0,0 +1,314 @@ +use std::mem::swap; +mod common; +use common::*; + +// Use 4 for the test input, 50 for the real one +//const CUBE_SIZE: usize = 4; +const CUBE_SIZE: usize = 50; + +// Connections between faces to make the cube 3D. +// This is hard coded to work for my specific input. +// To make it work for a different input you have to change this. +// The faces are in top-to-bottom, left-to-right order of input. +// For each face, the 4 connections are in order right, down, left, up. + +// Test case. Configuration: +// 0 U = 0, D = 4 +// 123 R = 5, L = 2 +// 45 F = 3, B = 1 +/* +const CONNECTIONS: [[Connection; 4]; 6] = [ + // Face 0 = U + [ + Connection { face: 5, side: Direction::Right }, + Connection { face: 3, side: Direction::Up }, + Connection { face: 2, side: Direction::Up }, + Connection { face: 1, side: Direction::Up }, + ], + + // Face 1 + [ + Connection { face: 2, side: Direction::Left }, + Connection { face: 4, side: Direction::Down }, + Connection { face: 5, side: Direction::Down }, + Connection { face: 0, side: Direction::Up }, + ], + + // Face 2 + [ + Connection { face: 3, side: Direction::Left }, + Connection { face: 4, side: Direction::Left }, + Connection { face: 1, side: Direction::Right }, + Connection { face: 0, side: Direction::Left }, + ], + + // Face 3 + [ + Connection { face: 5, side: Direction::Up }, + Connection { face: 4, side: Direction::Up }, + Connection { face: 2, side: Direction::Right }, + Connection { face: 0, side: Direction::Down }, + ], + + // Face 4 + [ + Connection { face: 5, side: Direction::Left }, + Connection { face: 1, side: Direction::Down }, + Connection { face: 2, side: Direction::Down }, + Connection { face: 3, side: Direction::Down }, + ], + + // Face 5 + [ + Connection { face: 0, side: Direction::Right }, + Connection { face: 1, side: Direction::Left }, + Connection { face: 4, side: Direction::Right }, + Connection { face: 3, side: Direction::Right }, + ], +]; +*/ + +// Input. Configuration: +// 01 +// 2 +// 34 +// 5 +const CONNECTIONS: [[Connection; 4]; 6] = [ + // Face 0 = U + [ + Connection { face: 1, side: Direction::Left }, + Connection { face: 2, side: Direction::Up }, + Connection { face: 3, side: Direction::Left }, + Connection { face: 5, side: Direction::Left }, + ], + + // Face 1 + [ + Connection { face: 4, side: Direction::Right }, + Connection { face: 2, side: Direction::Right }, + Connection { face: 0, side: Direction::Right }, + Connection { face: 5, side: Direction::Down }, + ], + + // Face 2 + [ + Connection { face: 1, side: Direction::Down }, + Connection { face: 4, side: Direction::Up }, + Connection { face: 3, side: Direction::Up }, + Connection { face: 0, side: Direction::Down }, + ], + + // Face 3 + [ + Connection { face: 4, side: Direction::Left }, + Connection { face: 5, side: Direction::Up }, + Connection { face: 0, side: Direction::Left }, + Connection { face: 2, side: Direction::Left }, + ], + + // Face 4 + [ + Connection { face: 1, side: Direction::Right }, + Connection { face: 5, side: Direction::Right }, + Connection { face: 3, side: Direction::Right }, + Connection { face: 2, side: Direction::Down }, + ], + + // Face 5 + [ + Connection { face: 4, side: Direction::Down }, + Connection { face: 1, side: Direction::Up }, + Connection { face: 0, side: Direction::Up }, + Connection { face: 3, side: Direction::Down }, + ], +]; + + +#[derive(Copy, Clone, Debug)] +struct Connection { + pub face: usize, + pub side: Direction +} + +struct Face { + pub tiles: [[Tile; CUBE_SIZE]; CUBE_SIZE], + offset_i: usize, + offset_j: usize +} + +#[derive(PartialEq, Debug, Copy, Clone)] +struct Position { + f: usize, + i: usize, + j: usize, + d: Direction +} + +#[allow(dead_code)] +impl Face { + pub fn empty() -> Face { + Face { + tiles: [[Tile::Skip; CUBE_SIZE]; CUBE_SIZE], + offset_i: 0, + offset_j: 0 + } + } + + pub fn is_empty(&self) -> bool { + self.tiles[0][0] == Tile::Skip + } + + pub fn print(&self) { + for i in 0..CUBE_SIZE { + for j in 0..CUBE_SIZE { + print!("{}", self.tiles[i][j].to_char()); + } + println!(); + } + } +} + +struct Cube { + pub faces: Vec<Face> +} + +type StepFn = fn(&Cube, Position) -> Position; + +impl Cube { + pub fn from_stdin() -> Cube { + let mut faces = vec![]; + let mut line = String::new(); + let mut offset_i = 0; + while faces.len() < 6 { + let mut row = [Face::empty(), Face::empty(), Face::empty(), + Face::empty(), Face::empty(), Face::empty()]; + for k in 0..CUBE_SIZE { + let _ = std::io::stdin().read_line(&mut line); + let bytes = &line.as_bytes(); + for l in 0..line.len()-1 { + row[l / CUBE_SIZE].tiles[k][l % CUBE_SIZE] = + Tile::from_byte(bytes[l]); + } + line.clear(); + } + let mut offset_j = 0; + for mut r in row { + r.offset_i = offset_i; + r.offset_j = offset_j; + if !r.is_empty() { + faces.push(r); + } + offset_j += CUBE_SIZE; + } + offset_i += CUBE_SIZE; + } + let _ = std::io::stdin().read_line(&mut line); // Read empty line + assert!(faces.len() == 6, "Cube has {} faces", faces.len()); + Cube { faces } + } + + fn start_position(&self) -> Position { + let j = self.faces[0].tiles[0].iter() + .position(|t| *t == Tile::Walk).unwrap(); + Position { f: 0, i: 0, j, d: Direction::Right } + } + + pub fn walk(&self, pos: Position, n: usize, step: StepFn) -> Position { + let mut p = pos; + for _ in 0..n { p = step(self, p); } + p + } +} + +fn flips(d1: Direction, d2: Direction) -> bool { + match d1 { + Direction::Right | Direction::Up => + d2 == Direction::Right || d2 == Direction::Up, + Direction::Down | Direction::Left => + d2 == Direction::Down || d2 == Direction::Left + } +} + +fn swaps(d1: Direction, d2: Direction) -> bool { + match d1 { + Direction::Right | Direction::Left => + d2 == Direction::Up || d2 == Direction::Down, + Direction::Up | Direction::Down => + d2 == Direction::Right || d2 == Direction::Left + } +} + +fn validate_connections(conn: &[[Connection; 4]; 6]) { + for i in 0..6 { + for j in 0..4 { + let c = conn[i][j]; + let d = conn[c.face][c.side.value()]; + assert!(d.face == i, "Bad connection: face {}-{}", i, j); + assert!(d.side.value() == j, "Bad connection: side {}-{}", i, j); + } + } +} + +fn overflow(i: i64, j: i64) -> Option<Direction> { + if i == -1 { return Some(Direction::Up) } + if j == -1 { return Some(Direction::Left) } + if i as usize == CUBE_SIZE { return Some(Direction::Down) } + if j as usize == CUBE_SIZE { return Some(Direction::Right) } + None +} + +fn on_other_side(i: i64, flip: bool, positive: bool) -> usize { + const CUBE_SIZE_I64: i64 = CUBE_SIZE as i64; + (if i >= 0 && i < CUBE_SIZE_I64 { + if flip { CUBE_SIZE_I64 - i - 1 } else { i } + } else { + if positive { 0 } else { CUBE_SIZE_I64 - 1 } + }) as usize +} + +fn step(cube: &Cube, p: Position) -> Position { + let s = p.d.step(); + let mut inext = s.0 + p.i as i64; + let mut jnext = s.1 + p.j as i64; + + let ret = if let Some(d) = overflow(inext, jnext) { + let conn = CONNECTIONS[p.f][d.value()]; + let flip = flips(p.d, conn.side); + let new_direction = conn.side.opposite(); + let positive = new_direction.is_positive(); + if swaps(p.d, conn.side) { swap(&mut inext, &mut jnext) } + Position { + f: conn.face, + i: on_other_side(inext, flip, positive), + j: on_other_side(jnext, flip, positive), + d: conn.side.opposite() + } + } else { + Position { + f: p.f, + i: inext as usize, + j: jnext as usize, + d: p.d + } + }; + + if cube.faces[ret.f].tiles[ret.i][ret.j] == Tile::Wall { p } else { ret } +} + +fn password(cube: &Cube, p: Position) -> usize { + let f = &cube.faces[p.f]; + 1000 * (p.i + f.offset_i + 1) + 4 * (p.j + f.offset_j + 1) + p.d.value() +} + +fn main() { + validate_connections(&CONNECTIONS); + let cube = Cube::from_stdin(); + let mut position = cube.start_position(); + let (turns, steps) = read_instruction_line_from_stdin(); + for i in 0..turns.len() { + position = cube.walk(position, steps[i], step); + position.d = position.d.turn(turns[i]); + } + position = cube.walk(position, steps[steps.len()-1], step); + println!("{}", password(&cube, position)); +} diff --git a/2022/22/common.rs b/2022/22/common.rs @@ -0,0 +1,111 @@ +use std::cmp::*; + +#[derive(PartialEq, Copy, Clone, Debug)] +pub enum Tile { Walk, Wall, Skip } + +#[allow(dead_code)] +impl Tile { + pub fn from_byte(b: u8) -> Tile { + match b { + b'.' => Tile::Walk, + b'#' => Tile::Wall, + b' ' => Tile::Skip, + _ => panic!("not a valid tile {}", b) + } + } + + pub fn to_char(&self) -> char { + match self { + Tile::Walk => '.', + Tile::Wall => '#', + Tile::Skip => ' ' + } + } +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub enum Direction { Up, Down, Right, Left } + +#[allow(dead_code)] +impl Direction { + pub fn from_char(c: char) -> Direction { + match c { + 'U' => Direction::Up, + 'D' => Direction::Down, + 'R' => Direction::Right, + 'L' => Direction::Left, + _ => panic!("invalid char for direction") + } + } + + pub fn turn(&self, t: Direction) -> Direction { + match t { + Direction::Right => match self { + Direction::Up => Direction::Right, + Direction::Right => Direction::Down, + Direction::Down => Direction::Left, + Direction::Left => Direction::Up + }, + Direction::Left => match self { + Direction::Up => Direction::Left, + Direction::Left => Direction::Down, + Direction::Down => Direction::Right, + Direction::Right => Direction::Up + } + _ => panic!("cannot turn up or down") + } + } + + pub fn step(&self) -> (i64, i64) { + match self { + Direction::Up => (-1, 0), + Direction::Down => (1, 0), + Direction::Right => (0, 1), + Direction::Left => (0, -1) + } + } + + pub fn value(&self) -> usize { + match self { + Direction::Right => 0, + Direction::Down => 1, + Direction::Left => 2, + Direction::Up => 3 + } + } + + pub fn opposite(&self) -> Direction { + match self { + Direction::Right => Direction::Left, + Direction::Left => Direction::Right, + Direction::Up => Direction::Down, + Direction::Down => Direction::Up + } + } + + pub fn is_positive(&self) -> bool { + *self == Direction::Down || *self == Direction::Right + } +} + +pub fn read_instruction_line_from_stdin() -> (Vec<Direction>, Vec<usize>) { + let mut dir = vec![]; + let mut step = vec![]; + let mut line = String::new(); + let _ = std::io::stdin().read_line(&mut line); + let mut i = 0; + while i < line.len()-1 { + match line.chars().nth(i).unwrap() { + '0'..='9' => { + let j = line[i..].find(|c: char| !c.is_digit(10)).unwrap() + i; + step.push(line[i..j].parse::<usize>().unwrap()); + i = j; + }, + c => { + dir.push(Direction::from_char(c)); + i += 1; + } + } + } + (dir, step) +}