commit dbb5a4e5711fba371fbb011356bcd8a28cc963e4
parent 2641a35d5473beb6889b299b54de294fbf782b1f
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date: Tue, 8 Jul 2025 17:10:53 +0200
Day 22 2022
Diffstat:
| A | 2022/22/a.rs | | | 81 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | 2022/22/b.rs | | | 314 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | 2022/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)
+}