day10.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. use aoc2023_niels_overkamp::common::{self, AOCResult};
  2. use std::collections::HashSet;
  3. const DAY: &str = "day10";
  4. fn main() -> Result<(), Box<dyn std::error::Error>> {
  5. common::run(DAY, &run)
  6. }
  7. #[derive(Clone, Copy, Debug, PartialEq, Eq)]
  8. enum Curvature {
  9. Right = -1, Straight = 0, Left = 1
  10. }
  11. impl Curvature {
  12. fn as_number(&self) -> isize {
  13. *self as isize
  14. }
  15. }
  16. #[derive(Clone, Copy, Debug, PartialEq, Eq)]
  17. enum Direction {
  18. North, East, South, West
  19. }
  20. impl Direction {
  21. fn take_pipe(&self, pipe: &u8) -> Option<(Self, Curvature)> {
  22. match (pipe, self) {
  23. (b'|', Self::North) => Some((Self::North, Curvature::Straight)),
  24. (b'|', Self::South) => Some((Self::South, Curvature::Straight)),
  25. (b'-', Self::West) => Some((Self::West, Curvature::Straight)),
  26. (b'-', Self::East) => Some((Self::East, Curvature::Straight)),
  27. (b'L', Self::South) => Some((Self::East, Curvature::Left)),
  28. (b'L', Self::West) => Some((Self::North, Curvature::Right)),
  29. (b'J', Self::South) => Some((Self::West, Curvature::Right)),
  30. (b'J', Self::East) => Some((Self::North, Curvature::Left)),
  31. (b'7', Self::East) => Some((Self::South, Curvature::Right)),
  32. (b'7', Self::North) => Some((Self::West, Curvature::Left)),
  33. (b'F', Self::West) => Some((Self::South, Curvature::Left)),
  34. (b'F', Self::North) => Some((Self::East, Curvature::Right)),
  35. _ => None
  36. }
  37. }
  38. fn curvature(&self, other: &Direction) -> Curvature {
  39. if *self == *other {
  40. Curvature::Straight
  41. } else if self.left() == *other {
  42. Curvature::Left
  43. } else {
  44. Curvature::Right
  45. }
  46. }
  47. fn do_move(&self, (x, y): (usize, usize)) -> Option<(usize, usize)> {
  48. match self {
  49. Self::North => y.checked_sub(1).map(|y| (x, y)),
  50. Self::East => Some((x + 1, y)),
  51. Self::South => Some((x, y + 1)),
  52. Self::West => x.checked_sub(1).map(|x| (x, y))
  53. }
  54. }
  55. fn left(&self) -> Self {
  56. match self {
  57. Self::North => Self::West,
  58. Self::East => Self::North,
  59. Self::South => Self::East,
  60. Self::West => Self::South
  61. }
  62. }
  63. fn right(&self) -> Self {
  64. match self {
  65. Self::West => Self::North,
  66. Self::North => Self::East,
  67. Self::East => Self::South,
  68. Self::South => Self::West
  69. }
  70. }
  71. }
  72. const DIRECTIONS: [Direction;4] = [Direction::North, Direction::East, Direction::South, Direction::West];
  73. pub fn run(input: &Vec<String>) -> AOCResult {
  74. let mut start = None;
  75. 'outer: for (y, line) in input.iter().enumerate() {
  76. for (x, b) in line.as_bytes().iter().enumerate() {
  77. if *b == b'S' {
  78. start = Some((x,y));
  79. break 'outer;
  80. }
  81. }
  82. }
  83. let input: Vec<&[u8]> = input.into_iter().map(|s| s.as_bytes()).collect();
  84. let (mut x, mut y) = start.ok_or::<Box<dyn std::error::Error>>("No S found in input".into())?;
  85. let mut start_direction = None;
  86. if let Some(_) = Direction::East.take_pipe(&input[y][x + 1]) {
  87. start_direction = Some(Direction::East);
  88. } else if let Some(_) = Direction::South.take_pipe(&input[y + 1][x]) {
  89. start_direction = Some(Direction::South);
  90. } else if x > 0 {
  91. if let Some(_) = Direction::West.take_pipe(&input[y][x - 1]) {
  92. start_direction = Some(Direction::West);
  93. }
  94. } else if y > 0 {
  95. if let Some(_) = Direction::North.take_pipe(&input[y][x - 1]) {
  96. start_direction = Some(Direction::North);
  97. }
  98. }
  99. let mut direction = start_direction.ok_or::<Box<dyn std::error::Error>>("No valid connection to S found".into())?;
  100. let start_direction = direction;
  101. let mut loop_size = 0;
  102. let mut curvature = 0;
  103. let mut left_outline = HashSet::new();
  104. let mut line = HashSet::new();
  105. let mut right_outline = HashSet::new();
  106. loop {
  107. loop_size += 1;
  108. (x, y) = direction.do_move((x, y)).ok_or_else(|| -> Box<dyn std::error::Error> { format!("Invalid move at {}, {} {:?}", x, y, direction).into() })?;
  109. line.insert((x,y));
  110. let pipe = input[y][x];
  111. let (out_direction, pipe_curvature) = if pipe == b'S' {
  112. (start_direction, direction.curvature(&start_direction))
  113. } else {
  114. direction
  115. .take_pipe(&pipe)
  116. .ok_or_else(|| -> Box<dyn std::error::Error> { format!("No valid connection at {}, {} {:?}", x, y, direction).into() })?
  117. };
  118. // println!("{}, {}, {:?}-{:?}->{:?}", x, y, direction, pipe_curvature, out_direction);
  119. match pipe_curvature {
  120. Curvature::Left => {
  121. if let Some(coord) = out_direction.right().do_move((x,y)) {
  122. // println!("{:?} right", coord);
  123. right_outline.insert(coord);
  124. }
  125. if let Some(coord) = direction.right().do_move((x,y)) {
  126. // println!("{:?} right", coord);
  127. right_outline.insert(coord);
  128. }
  129. }
  130. Curvature::Right => {
  131. if let Some(coord) = out_direction.left().do_move((x,y)) {
  132. // println!("{:?} left", coord);
  133. left_outline.insert(coord);
  134. }
  135. if let Some(coord) = direction.left().do_move((x,y)) {
  136. // println!("{:?} left", coord);
  137. left_outline.insert(coord);
  138. }
  139. }
  140. Curvature::Straight => ()
  141. }
  142. direction = out_direction;
  143. curvature += pipe_curvature.as_number();
  144. if pipe == b'S' {
  145. break
  146. }
  147. }
  148. // println!("{:?}", left_outline);
  149. // println!("{:?}", right_outline);
  150. // println!("{:?}", line);
  151. // println!("{}", curvature);
  152. let outline = if curvature.signum() == (Curvature::Left as isize) {
  153. left_outline
  154. } else {
  155. right_outline
  156. };
  157. let mut visited = HashSet::new();
  158. let mut inner_count = 0;
  159. let mut frontier = vec![];
  160. for coord in outline.into_iter() {
  161. if visited.contains(&coord) || line.contains(&coord) {
  162. continue
  163. }
  164. frontier.push(coord);
  165. while let Some(coord) = frontier.pop() {
  166. if visited.contains(&coord) || line.contains(&coord) {
  167. continue
  168. }
  169. if !line.contains(&coord) {
  170. println!("{:?} {:?}", coord, visited);
  171. visited.insert(coord);
  172. inner_count += 1;
  173. }
  174. DIRECTIONS.iter()
  175. .for_each(|direction| {
  176. direction
  177. .do_move(coord)
  178. .filter(|coord| !visited.contains(coord) && !line.contains(coord))
  179. .map(|coord| frontier.push(coord));
  180. });
  181. }
  182. }
  183. Ok([Some((loop_size / 2).to_string()), Some(inner_count.to_string())])
  184. }
  185. #[test]
  186. pub fn test_day10() {
  187. assert!(common::run_test(DAY, &run))
  188. }