~cdv/aoc-2018-rs

bdb18e37a3f8f606e8721242641e76c822cc3174 — Christopher Vittal 2 years ago 983d968 day18
Solve day eighteen
5 files changed, 313 insertions(+), 4 deletions(-)

M Cargo.toml
A data/day18
A descriptions/Day18.md
M src/bin/day18.rs
M src/file.rs
M Cargo.toml => Cargo.toml +6 -3
@@ 5,10 5,13 @@ authors = ["Christopher Vittal <christopher.vittal@gmail.com>"]
edition = "2018"

[dependencies]
regex = "1"
bytecount = "0.4"
itertools = "0.8"
lazy_static = "1"
pathfinding = "1"
regex = "1"
rustc-hash = "1"
itertools = "0.8"

[profile.release]
lto = true
#lto = true
debug = true

A data/day18 => data/day18 +50 -0
@@ 0,0 1,50 @@
.||..#.#.#..#..#|...#|....|.|.#.###....##.|#..#...
..##||.#......|#...#.|..#.....##|.#.##|...|.|.#..#
##||.#..#.#...|..|...||.||...#|.#....|..|.#|..|#|#
....||..#...|||.#...|.|...|.#.|...##.#||.#.......#
.|.....#.||.#|#.#..|..#..#.|...||.##|.#......#..#|
|..|...#..#.|#|.#......|.||||.#....#....#|||..#..|
..#..#.#..#....|#|#||....|.#.#...||#..|.|.##..#|.|
....#.|...#|..#.|.#...|..#|.##.#......##.#....##..
.#..|.|.#..||....||....|.||.##.|#|#||.#...|.|.|||#
##..##|....|.|#.....|...........|#.|........|...|.
||.|...#.#...#....|.#|..##.|......|#..#.#.##..#|#.
|....|.#|#|....#...|...#.|#|..||.||##..##..|#|##..
|.||..#.#..|.#......|#..##|...#....|#.......#...|.
.|#......||.......|..||..#..#|..|..|.#.|.#.|......
.#.#.|.|.#.....|.|..|.###..|#....#.||..|..|.......
.|.......|##|.#.....|.|..|.##.||###.#.|..##|...|..
|#...##..||..|..|#.|...|....||.......|#.|...|.|.|.
..#.||#....#....||.|....#.#....#...|...#...#|.#.#.
|...|.#|..|||.|.#.#..#.|.|....#...#.##..#....|#.|#
#|#|..#.....|#||.....#|.|.#.||..||.|||.#|#||#|.|..
.....#|..|.##|....|.#..|.#.#...#...#....|.#|...|#.
#..|#.##..|..###.#|.||..#.#|#||.#|##.#..#.#.|.....
|.#.....##...|..###|...##|......##|#|.....#..|..#.
.|..|..|...#.|......|#|.........|....|#........|..
.#....|#.|...|.....|#.#...|.|###.|..##|.#|.#.|....
.......#|#||||#.|..|.#|||.#..#...##..#..#..#|#.#.#
|#...|||.||.....#.|##........|#|..|.##||..|.....|#
.|#|.||....###..#.#..|.....|.##..||.###.|...#....#
.#..#..||.....#.#..||#.#....#.||..|.#.|.|...|...#.
..#|#...||..#....#|..|..#.#.......|#####.|.##..|..
..|.|.||....#|#.......|.|.#...|.|||#|..#..#.#...##
||..#....#..||#....|..|.||#|.|##|.|#|.|#..|...#|..
#...|#..#..#....|...#.#.......#..#...|..|#||..|...
......#||#...#|###.#|.|...#..###|.|#..|..|...#..#|
.|...#....|#.#..#......#|...|.##.#|||.###.#|...#|#
..||..##.#|.#.....#...|..#..|.#..|#...|.#..||...##
...##.|.....##......##.##.#.##|##.|..|.#...#.#.|||
...#|#....#.....|.#.|...|||.#|.|#......|#...|#....
...........#|....|.##...|##|..|#|#|.|#.|...#......
....|..|..#..|#...#..#.###...###....####..##.##...
.##|.....#....|...|#..|..|#..|.|...#....#||....#..
#..#|#|..|.#....|..#..|#.#....#.#.||..#.#.#||#...|
||...|.|.|.||#.......|.#..#.|.#...#.||.||.#.|.|..#
...#....|.......#...||.......|.....#...........|#.
...#.#..##...|....#.|.|.#......#.|||....#.|.|...|.
.##|.|...#|...#.|.#|.|...|#...#....|#.#.|#..#|||..
###|.#.|#.|.|.#.||#.|.........|#...#.#....|#....#.
.#.........|....#.|...#..#...|.|......|.|.|..||...
|#...|....|||.#.||||.#|...#.#..|.......#....#.#...
#|#.##.#.#.|...|....#.###....|..#....#.....|#.||..

A descriptions/Day18.md => descriptions/Day18.md +187 -0
@@ 0,0 1,187 @@
\-\-- Day 18: Settlers of The North Pole \-\--
----------------------------------------------

On the outskirts of the North Pole base construction project, many Elves
are collecting [lumber]{title="Trade wood for sheep?"}.

The lumber collection area is 50 acres by 50 acres; each acre can be
either *open ground* (`.`), *trees* (`|`), or a *lumberyard* (`#`). You
take a scan of the area (your puzzle input).

Strange magic is at work here: each minute, the landscape looks entirely
different. In exactly *one minute*, an open acre can fill with trees, a
wooded acre can be converted to a lumberyard, or a lumberyard can be
cleared to open ground (the lumber having been sent to other projects).

The change to each acre is based entirely on *the contents of that acre*
as well as *the number of open, wooded, or lumberyard acres adjacent to
it* at the start of each minute. Here, \"adjacent\" means any of the
eight acres surrounding that acre. (Acres on the edges of the lumber
collection area might have fewer than eight adjacent acres; the missing
acres aren\'t counted.)

In particular:

-   An *open* acre will become filled with *trees* if *three or more*
    adjacent acres contained trees. Otherwise, nothing happens.
-   An acre filled with *trees* will become a *lumberyard* if *three or
    more* adjacent acres were lumberyards. Otherwise, nothing happens.
-   An acre containing a *lumberyard* will remain a *lumberyard* if it
    was adjacent to *at least one other lumberyard and at least one acre
    containing trees*. Otherwise, it becomes *open*.

These changes happen across all acres *simultaneously*, each of them
using the state of all acres at the beginning of the minute and changing
to their new form by the end of that same minute. Changes that happen
during the minute don\'t affect each other.

For example, suppose the lumber collection area is instead only 10 by 10
acres with this initial configuration:

    Initial state:
    .#.#...|#.
    .....#|##|
    .|..|...#.
    ..|#.....#
    #.#|||#|#|
    ...#.||...
    .|....|...
    ||...#|.#|
    |.||||..|.
    ...#.|..|.

    After 1 minute:
    .......##.
    ......|###
    .|..|...#.
    ..|#||...#
    ..##||.|#|
    ...#||||..
    ||...|||..
    |||||.||.|
    ||||||||||
    ....||..|.

    After 2 minutes:
    .......#..
    ......|#..
    .|.|||....
    ..##|||..#
    ..###|||#|
    ...#|||||.
    |||||||||.
    ||||||||||
    ||||||||||
    .|||||||||

    After 3 minutes:
    .......#..
    ....|||#..
    .|.||||...
    ..###|||.#
    ...##|||#|
    .||##|||||
    ||||||||||
    ||||||||||
    ||||||||||
    ||||||||||

    After 4 minutes:
    .....|.#..
    ...||||#..
    .|.#||||..
    ..###||||#
    ...###||#|
    |||##|||||
    ||||||||||
    ||||||||||
    ||||||||||
    ||||||||||

    After 5 minutes:
    ....|||#..
    ...||||#..
    .|.##||||.
    ..####|||#
    .|.###||#|
    |||###||||
    ||||||||||
    ||||||||||
    ||||||||||
    ||||||||||

    After 6 minutes:
    ...||||#..
    ...||||#..
    .|.###|||.
    ..#.##|||#
    |||#.##|#|
    |||###||||
    ||||#|||||
    ||||||||||
    ||||||||||
    ||||||||||

    After 7 minutes:
    ...||||#..
    ..||#|##..
    .|.####||.
    ||#..##||#
    ||##.##|#|
    |||####|||
    |||###||||
    ||||||||||
    ||||||||||
    ||||||||||

    After 8 minutes:
    ..||||##..
    ..|#####..
    |||#####|.
    ||#...##|#
    ||##..###|
    ||##.###||
    |||####|||
    ||||#|||||
    ||||||||||
    ||||||||||

    After 9 minutes:
    ..||###...
    .||#####..
    ||##...##.
    ||#....###
    |##....##|
    ||##..###|
    ||######||
    |||###||||
    ||||||||||
    ||||||||||

    After 10 minutes:
    .||##.....
    ||###.....
    ||##......
    |##.....##
    |##.....##
    |##....##|
    ||##.####|
    ||#####|||
    ||||#|||||
    ||||||||||

After 10 minutes, there are `37` wooded acres and `31` lumberyards.
Multiplying the number of wooded acres by the number of lumberyards
gives the total *resource value* after ten minutes: `37 * 31 = 1147`.

*What will the total resource value of the lumber collection area be
after 10 minutes?*

\-\-- Part Two \-\-- {#part2}
--------------------

This important natural resource will need to last for at least thousands
of years. Are the Elves collecting this lumber sustainably?

*What will the total resource value of the lumber collection area be
after 1000000000 minutes?*

M src/bin/day18.rs => src/bin/day18.rs +64 -1
@@ 1,1 1,64 @@
fn main() {}
use std::io;
use std::io::prelude::*;

use pathfinding::matrix::Matrix;
use rustc_hash::*;

static INPUT: &str = "data/day18";
const MAX_TIME: usize = 1_000_000_000;
const PART_ONE: usize = 10;
const YARD_SIZE: usize = 50;

#[allow(unused)]
fn print_matrix(m: &Matrix<u8>) -> io::Result<()> {
    let s = io::stdout();
    let mut s = s.lock();
    for _ in 0..m.columns { write!(s, "=")?; }
    writeln!(s)?;
    for i in 0..m.rows {
        let row = &m.as_ref()[i*m.columns..(i+1)*m.columns];
        s.write_all(row)?;
        writeln!(s)?;
    }
    Ok(())
}

fn main() {
    let input: Vec<u8> = aoc::file::to_strings_iter(INPUT).map(Vec::from).flatten().collect();
    let mut yard = Matrix::from_vec(YARD_SIZE, YARD_SIZE, input);

    let mut m = FxHashMap::default();
    let mut t = 0;
    while t < MAX_TIME {
        let mut new = Matrix::new_square(YARD_SIZE, b'.');
        for r in 0..yard.rows {
            for c in 0..yard.columns {
                let idx = &(r, c);
                let mut adj_tree = 0;
                let mut adj_lumb = 0;
                for n in yard.neighbours(&(r, c), true) {
                    adj_tree += (yard[&n] == b'|') as u8;
                    adj_lumb += (yard[&n] == b'#') as u8;
                }
                if yard[idx] == b'.' && adj_tree >= 3 {
                    new[idx] = b'|';
                } else if yard[idx] == b'|' {
                    new[idx] = if adj_lumb >= 3 { b'#' } else { b'|' };
                } else if yard[idx] == b'#' && adj_tree >= 1 && adj_lumb >= 1 {
                    new[idx] = b'#';
                }
            }
        }
        yard = new.clone();
        if let Some(t_) = m.insert(new, t) {
            let skip = (MAX_TIME - t) / (t - t_);
            t += skip * (t - t_);
        }
        if t == PART_ONE - 1 || t == MAX_TIME - 1 {
            let trees = bytecount::count(yard.as_ref(), b'|');
            let lumber = bytecount::count(yard.as_ref(), b'#');
            println!("  {}: {}", 1 + (t == MAX_TIME - 1) as u8, trees * lumber)
        }
        t += 1;
    }
}

M src/file.rs => src/file.rs +6 -0
@@ 38,6 38,12 @@ pub fn to_strings<P: AsRef<Path>>(p: P) -> Vec<String> {
    to_lines(p).map(|r| r.expect("error in read")).collect()
}

#[inline]
pub fn to_strings_iter<P: AsRef<Path>>(p: P) -> impl Iterator<Item = String> {
    to_lines(p).map(|r| r.expect("could not read line"))
}


pub fn to_single_parsed<F, P>(p: P) -> Vec<F>
where
    P: AsRef<Path>,