diff --git a/2018/inputs/18.txt b/2018/inputs/18.txt new file mode 100644 index 0000000..161ad17 --- /dev/null +++ b/2018/inputs/18.txt @@ -0,0 +1,50 @@ +..|#..#......#|.#..#|#.|##|.|.##.||..|.||....###|. +||.|....|#.#|.#...#.|...|#.#.#.#....#......#....#. +...#...|.....#.#..|#...||..#.|.|#....#|#|..##...|. +.....#....|#.|..|.....#|##...#.#.#||....||||....#. +..###...#.||.|||.##.#.|....|.##.#....|.#.|..|..... +....|..#.#.|...|..|.....#....#.#.......|.||...|... +#..|.....##|..#..#...#..|.##.....#......||##|.|#.. +##........#...|#|.##.#|#..#|...#...#.##|||.#||..|| +......#..|..|.#...|#||.#....#.#.|#.||......|.....| +.|...|.##....#.||...|..|..|.|........#.#..|||..|## +..|....|...#####..|#|..|...#...#.|...|..|.|..|.#|# +....|...#.#.#.......#......#.#|......#|.##..##.#|. +.....|.#..|#...||..#......|..#.|#.#|...#|.|..#||.. +#....#.......|#.|..|...#...|..|.##|#.|#.#|.....|.. +....#.||#.....#..#...|....##.#.......#.|.|||.|.... +|.|..||##....|#..#..|..|.|.|..|||.##..#.|......##| +###..|.#|##|#.|||.#|.#..|#|..#..|.#|....#.#.#..||. +.|.....|#.#.|#||..#.....#.|.||.#.|.....|#..|...#.. +...##.........|...#.#|....##..#.|.|.......#..|...| +..#.#.|.|.....||#..||...##||.#|..|.....#|...|...#| +....#...#||..|...|.|..|#.#.........|#...#.|||...#. +.#..|.##.|.|.#...#.....#.#.......|#.|.#||#.#.....# +|...#|..#....#...|.##.####....|#.##|#.#.|.....||.. +....|.#.|#||..|#.|.|.#|...|.#....||.#...#|.#...|.# +.|..#.#..|#|..##..|.##..||...#...||....#||..#.|... +##......#.|...|.||.#.||....|.......#......##|#..|. +|#....#||....##...........#.|....|....|#|#.|..#... +..#...#|....|.|..|...#.......#.##.#.....#.||...... +...|....#|#..#|...|...#|....#.#|.......|.......|#. +.#||..##||.|..|.|#..|.|....|.#|.|.|.....#.#.|..#.# +.....|..|...|...|......||...##.....##.......|.#..| +.....#..#|.#...#..#...||.|##..|#..##.|#.....##.... +|...|.#||.........#..#..#||||....|...|..|..#...##. +#.#..|.|.......||..|#|..|....|.|#|#|.|..|.|...#.#. +#.|..||#.||||..###.|......|.#|||.##........||...|| +.....#|..#.#.....|..|#.....|....|.#|||#.|.....#... +...||#..#...#...||#.||......|#..#..#.|#.|#|...|..# +.||.....||..|#.|#||...##..###|.#....|.|..|#...#|.| +...#.|#||..#.|||......#...||.#..|||..|#.|.##..#.|# +.#||..#.|||.......|.|#.....|.|#.#..##..|.|....|#.. +..#.###..|.........|.....#..###|.|#..........#|.#| +#.||.|.#.|..||#|||#..##.|#....#.#.|.#.....|.|.#.|# +|..|..#.#..|.#.......#...#.|..|..|#..|..###|.||..| +|..|...||...|.#.#..##|.#..#...#|#..|.#..|.#..|.||. +..#.##..#.|..#.#..|..|#.|||.#..#..|#####.|..#..... +.|.|#.|#...||..##...#|#.........#...#|..##.#.#..#. +##|.|..#.#|....#..|..#....#......#||.|....||##..|| +.##|#....#..#..#.....#|.#...#..#.|#||||.##.#....|. +..|.......#|....|#|..||..##..#.|#|..#.#|....|..#|# +....|||.|||#..||...|||.##..#.#|##....|..|..||..#|# diff --git a/2018/src/day18.rs b/2018/src/day18.rs index aef91b5..71bea19 100644 --- a/2018/src/day18.rs +++ b/2018/src/day18.rs @@ -1,25 +1,189 @@ +use std::io::BufRead; +use std::io::BufReader; use std::io::Read; +use common::GroupingCount; use common::Solution; +use std::mem::swap; +use std::collections::HashMap; + +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +enum Tile { + Tree, + Lumber, + Open, +} + +impl Tile { + pub fn next(self, counts: &[usize; 3]) -> Self { + match self { + Tile::Open => { + if counts[Tile::Tree as usize] >= 3 { + Tile::Tree + } else { + Tile::Open + } + } + Tile::Tree => { + if counts[Tile::Lumber as usize] >= 3 { + Tile::Lumber + } else { + Tile::Tree + } + } + Tile::Lumber => { + if counts[Tile::Tree as usize] >= 1 && counts[Tile::Lumber as usize] >= 1 { + Tile::Lumber + } else { + Tile::Open + } + } + } + } +} + +impl From for Tile { + fn from(c: char) -> Self { + match c { + '|' => Tile::Tree, + '#' => Tile::Lumber, + '.' => Tile::Open, + _ => panic!("Invalid tile '{}'", c) + } + } +} + +impl From for char { + fn from(t: Tile) -> Self { + match t { + Tile::Tree => '|', + Tile::Lumber => '#', + Tile::Open => '.', + } + } +} #[derive(Default)] -pub struct Day18 {} +pub struct Day18 { + grid: Vec>, + buf: Vec>, + width: usize, +} impl Day18 { pub fn new() -> Self { Default::default() } + + fn read_input(&mut self, input: &mut Read) { + let reader = BufReader::new(input); + self.grid.clear(); + + for line in reader.lines() { + let line = line.unwrap(); + + self.grid.push(line.chars().map(From::from).collect()); + } + + self.width = self.grid.first().unwrap().len(); + self.buf = vec![vec![Tile::Tree; self.width]; self.grid.len()]; + } + + fn simulate(&mut self) { + let height = self.grid.len(); + let width = self.width; + + for y in 0..height { + let ymin = if y > 0 { y - 1 } else { y }; + let ymax = if y < height - 1 { y + 1 } else { y }; + + for x in 0..self.width { + let mut counts = [0; 3]; + let xmin = if x > 0 { x - 1 } else { x }; + let xmax = if x < width - 1 { x + 1 } else { x }; + + for ys in ymin..=ymax { + for xs in xmin..=xmax { + if ys != y || xs != x { + counts[self.grid[ys][xs] as usize] += 1; + } + } + } + + self.buf[y][x] = self.grid[y][x].next(&counts); + } + } + + swap(&mut self.buf, &mut self.grid); + } + + fn print(&self) -> String { + let mut buf = String::with_capacity(self.width * self.grid.len()); + for row in &self.grid { + buf.extend(row.iter().cloned().map(Into::::into)); + } + buf + } + + fn score(&self) -> String { + let result = self.grid.iter() + .flat_map(|x| x.iter()) + .cloned().grouping_count(); + + format!("{}", result[&Tile::Tree] * result[&Tile::Lumber]) + } } impl Solution for Day18 { - fn part1(&mut self, _input: &mut Read) -> String { - unimplemented!() + fn part1(&mut self, input: &mut Read) -> String { + self.read_input(input); + + for _ in 0..10 { + self.simulate(); + } + + self.score() } - fn part2(&mut self, _input: &mut Read) -> String { - unimplemented!() + fn part2(&mut self, input: &mut Read) -> String { + self.read_input(input); + let limit = 1_000_000_000; + + let mut seen = HashMap::new(); + seen.insert(self.print(), 0); + + for i in 1..=limit { + self.simulate(); + let summary = self.print(); + if let Some(first) = seen.get(&summary) { + let period = i - *first; + let remaining = (limit - *first) % period; + + for _ in 0..remaining { + self.simulate(); + } + + return self.score(); + } + + seen.insert(summary, i); + } + + // I encourage everyone to hit this line of code. + self.score() } } #[cfg(test)] -mod tests {} +mod tests { + use common::Solution; + use day18::Day18; + + const SAMPLE_INPUT: &[u8] = include_bytes!("samples/18.txt"); + + #[test] + fn sample_part1() { + let mut instance = Day18::new(); + assert_eq!("1147", instance.part1(&mut SAMPLE_INPUT)); + } +} diff --git a/2018/src/samples/18.txt b/2018/src/samples/18.txt new file mode 100644 index 0000000..13d299d --- /dev/null +++ b/2018/src/samples/18.txt @@ -0,0 +1,10 @@ +.#.#...|#. +.....#|##| +.|..|...#. +..|#.....# +#.#|||#|#| +...#.||... +.|....|... +||...#|.#| +|.||||..|. +...#.|..|.