use crate::utils; use std::collections::HashMap; pub fn task1() { let (map, mut carts) = read_input(); println!("We have {} carts initially", carts.len()); loop { perform_round(&map, &mut carts); } } fn read_input() -> (Vec>, Vec) { let input = utils::read_file("input/day13.txt"); let input: Vec<&str> = input.lines().collect(); let width = input[0].len(); let height = input.len(); let mut map = Vec::new(); let mut inner_vec = Vec::new(); inner_vec.resize(height, ' '); map.resize(width, inner_vec); let mut carts = Vec::new(); let tiles = vec!['|', '-', '/', '\\', '+']; for (y, line) in input.iter().enumerate() { for (x, c) in line.chars().enumerate() { if tiles.contains(&c) { map[x][y] = c; } else if c != ' ' { map[x][y] = match c { '>' => '-', '<' => '-', '^' => '-', 'v' => '-', _ => panic!("{} is invalid input char at this point", c), }; carts.push(Cart { x, y, direction: c, intersections_visited: 0, active: true, }); } } } (map, carts) } #[derive(Debug)] struct Cart { x: usize, y: usize, direction: char, intersections_visited: usize, active: bool, } fn perform_round(map: &[Vec], carts: &mut Vec) { carts.sort_unstable_by(|a, b| { if a.y == b.y { a.x.cmp(&b.x) } else { a.y.cmp(&b.y) } }); let mut positions: HashMap<(usize, usize), usize> = carts .iter() .enumerate() .filter(|(_, cart)| cart.active) .map(|(i, cart)| ((cart.x, cart.y), i)) .collect(); for cart_index in 0..carts.len() { let cart = &mut carts[cart_index]; if !cart.active { continue; } let pos_old = (cart.x, cart.y); match cart.direction { '>' => cart.x += 1, '<' => cart.x -= 1, '^' => cart.y -= 1, 'v' => cart.y += 1, c => panic!("invalid direction {}", c), } cart.direction = match (cart.direction, map[cart.x][cart.y]) { ('>', '/') => '^', ('>', '\\') => 'v', ('<', '/') => 'v', ('<', '\\') => '^', ('^', '/') => '>', ('^', '\\') => '<', ('v', '/') => '<', ('v', '\\') => '>', (direction, '+') => { let new_direction = match cart.intersections_visited % 3 { 0 => match direction { '>' => '^', '^' => '<', '<' => 'v', 'v' => '>', _ => panic!("Invalid direction"), }, 1 => direction, 2 => match direction { '>' => 'v', '^' => '>', '<' => '^', 'v' => '<', _ => panic!("Invalid direction"), }, _ => panic!("modulo 3 cannot be larger than 2"), }; cart.intersections_visited += 1; new_direction } (_, _) => cart.direction, }; if positions.contains_key(&(cart.x, cart.y)) { // Task1: panic here with coordinates println!("We have a collision at {},{}!", cart.x, cart.y); let other_index = positions[&(cart.x, cart.y)]; cart.active = false; if !carts[other_index].active { panic!("that one crashed already before!"); } carts[other_index].active = false; positions.remove(&(carts[cart_index].x, carts[cart_index].y)); positions.remove(&pos_old); println!( "{} carts left", carts.iter().filter(|cart| cart.active).count() ); } else { positions.remove(&pos_old); positions.insert((cart.x, cart.y), cart_index); } } if carts.iter().filter(|cart| cart.active).count() == 1 { panic!( "exactly one active cart left: {:?}", carts.iter().find(|cart| cart.active) ); } }