【译】用 Rust 实现 Advent of Code 2020 第11天

96 阅读9分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

新的一天,新的问题

这个问题看起来有点像“康威生命游戏”(Game of Life) ,或者是一种老式的细胞生命自动机

我们有一张这样的地图:

L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL

对于每一次迭代:

  • 如果是 L 符号,会变成 #,如果没有变成 #,则在其相邻的 8 个位置一定有一个可以转换成 #
  • # 符号,则变成 L,前提是相邻位置中有 4 个以上的单元格是 #
  • . 符号,则不会改变

这是一个令人感兴趣的问题。我们甚至可以回想一些我们在第 3 天做的东西!

首先是 2D vector 类型:

#[derive(Debug, Clone, Copy, PartialEq)]
struct Vec2 {
    x: i64,
    y: i64,
}

然后是 tile 枚举:

#[derive(Clone, Copy, PartialEq)]
enum Tile {
    Floor,
    EmptySeat,
    OccupiedSeat,
}

impl Default for Tile {
    fn default() -> Self {
        Self::Floor
    }
}

为 Tile 实现一个简洁的 Debug trait:

use std::fmt;

impl fmt::Debug for Tile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let c = match self {
            Tile::Floor => '.',
            Tile::EmptySeat => 'L',
            Tile::OccupiedSeat => '#',
        };
        write!(f, "{}", c)
    }
}

然后,Map —— 这一次,让它通用一些。只是为了好玩!

struct Map<T> {
    size: Vec2,
    tiles: Vec<T>,
}

如果 T 实现了 Default trait,可以很简单地实现 new:

impl<T> Map<T>
where
    T: Default,
{
    fn new(size: Vec2) -> Self {
        let num_tiles = size.x * size.y;
        Self {
            size,
            tiles: (0..num_tiles)
                .into_iter()
                .map(|_| Default::default())
                .collect(),
        }
    }
}

然后是 Map::index,从 2D 空间到 1D 空间的映射/索引:

impl<T> Map<T> {
    fn index(&self, pos: Vec2) -> Option<usize> {
        if (0..self.size.x).contains(&pos.x) && (0..self.size.y).contains(&pos.y) {
            Some((pos.x + pos.y * self.size.x) as _)
        } else {
            None
        }
    }
}

从这里我们可以很容易地实现 set:

impl<T> Map<T> {
    fn set(&mut self, pos: Vec2, tile: T) {
        if let Some(index) = self.index(pos) {
            self.tiles[index] = tile;
        }
    }
}

对于 get,我们需要 T 是可 Copy 的,这样我们就不需要使用引用:

impl<T> Map<T>
where
    T: Copy,
{
    fn get(&self, pos: Vec2) -> Option<T> {
        self.index(pos).map(|index| self.tiles[index])
    }
}

接下来 —— 我们要处理邻近位置,所以如果我们能够穷举出指定单元格的所有邻近位置,那将会非常简洁:

impl<T> Map<T> {
    fn neighbor_positions(&self, pos: Vec2) -> impl Iterator<Item = Vec2> {
        (-1..=1)
            .map(|dx| (-1..=1).map(move |dy| (dx, dy)))
            .flatten()
            .filter(|&(dx, dy)| !(dx == 0 && dy == 0))
            .map(move |(dx, dy)| Vec2 {
                x: pos.x + dx,
                y: pos.y + dy,
            })
    }
}

酷熊:这看起来有点不太靠谱,测试一下怎么样?

#[test]
fn test_neighbor_positions() {
    use std::collections::HashSet;

    let map = Map::<()>::new(Vec2 { x: 3, y: 3 });
    let positions: HashSet<_> = map
        .neighbor_positions(Vec2 { x: 1, y: 1 })
        .map(|v| (v.x, v.y))
        .collect();
    for p in &[
        (0, 0),
        (0, 1),
        (0, 2),
        (1, 0),
        (2, 0),
        (1, 2),
        (2, 2),
        (2, 1),
    ] {
        assert!(positions.contains(p));
    }
}
$ cargo t -q

running 1 test
.
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

酷熊:好吧,继续。

接下来,为了更好地连接上下文,我们可以使用 .neighbor_tiles 方法返回 Tile 的迭代器!

impl<T> Map<T>
where
    T: Copy,
{
    fn neighbor_tiles(&self, pos: Vec2) -> impl Iterator<Item = T> + '_ {
        self.neighbor_positions(pos)
            .filter_map(move |pos| self.get(pos))
    }
}

酷熊:+ '_ 是什么意思?

Amos:em,迭代器在借用时,生命周期和 &self 一样长,因为它是从它那里获取的!

酷熊:啊,那么如果是 impl Iterator<Item = T> (没有任何附加注释),生命周期又是多少呢?

Amos:就像 Box<T> 一样,它默认是 'static 的,这只适用于拥有所有权的类型(比如我们的 neighbor_positions 迭代器,它拥有产生数据项所需的所有权),或者,我猜测,'static 参数本质类似于一个 const,或者形如 Box::leakinclude_str 中持续存在的东西一样!

好了,现在我们可以开始实现一些“细胞自动机”逻辑了,我们可以基于 Tile 做一些处理:

impl Tile {
    fn next<I>(self, neighbors: I) -> Self
    where
        I: Iterator<Item = Self>,
    {
        match self {
            Self::Floor => Self::Floor,
            Self::EmptySeat => match neighbors
                .filter(|t| matches!(t, Self::OccupiedSeat))
                .count()
            {
                // no one around? we can sit here!
                0 => Self::OccupiedSeat,
                // social distancing please
                _ => Self::EmptySeat,
            },
            Self::OccupiedSeat => {
                match neighbors.filter(|t| matches!(t, Self::OccupiedSeat)).count() {
                    // up to 3 neighbors: still okay for now
                    0..=3 => Self::OccupiedSeat,
                    // that's too many folks!
                    _ => Self::EmptySeat,
                }
            }
        }
    }
}

看看,我们还需要什么...啊,对了,为 Map 实现 Debug trait —— 当且仅当 T 本身满足 Debug 约束的时候。哦,还有 Copy 约束,因为我们的 get 方法中需要用到:

impl<T> fmt::Debug for Map<T>
where
    T: fmt::Debug + Copy,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for y in 0..self.size.y {
            for x in 0..self.size.x {
                write!(f, "{:?}", self.get(Vec2 { x, y }).unwrap())?;
            }
            writeln!(f)?;
        }
        Ok(())
    }
}

然后... 你知道怎么做会更好吗?由于这些规则同时适用于所有的 tile,所以如果我们能够迭代所有 tile,那将是非常简洁的。

impl<T> Map<T>
where
    T: Copy,
{
    fn iter(&self) -> impl Iterator<Item = T> + '_ {
        self.tiles.iter().copied()
    }
}

酷熊:但是... 我们还需要知道他们的位置,对吧?这样我们就能推断出他们的邻近位置坐标了?

对! 为了做到这一点,我们引入另一种类型: Positioned:

#[derive(Debug)]
struct Positioned<T>(Vec2, T);

并使 iter 方法返回 position<T> 类型,并将类型作为 Item 的类型:

impl<T> Map<T>
where
    T: Copy,
{
    // 替换前面的 `iter` 方法
    fn iter(&self) -> impl Iterator<Item = Positioned<T>> + '_ {
        (0..self.size.y)
            .map(move |y| {
                (0..self.size.x).map(move |x| {
                    let pos = Vec2 { x, y };
                    Positioned(pos, self.get(pos).unwrap())
                })
            })
            .flatten()
    }
}

我们试试看:

fn main() {
    let mut m = Map::new(Vec2 { x: 3, y: 3 });
    m.set(Vec2 { x: 1, y: 1 }, Tile::OccupiedSeat);

    for tile in m.iter() {
        println!("{:?}", tile);
    }
}
$ cargo run --quiet
Positioned(Vec2 { x: 0, y: 0 }, .)
Positioned(Vec2 { x: 1, y: 0 }, .)
Positioned(Vec2 { x: 2, y: 0 }, .)
Positioned(Vec2 { x: 0, y: 1 }, .)
Positioned(Vec2 { x: 1, y: 1 }, #)
Positioned(Vec2 { x: 2, y: 1 }, .)
Positioned(Vec2 { x: 0, y: 2 }, .)
Positioned(Vec2 { x: 1, y: 2 }, .)
Positioned(Vec2 { x: 2, y: 2 }, .)

酷熊:看起来不错!

然后,最后,作为下一步 —— 我们可以将其收集到 Map 中。

酷熊:这有些不同! 我们该怎么做?

我们可以先看看 Iterator::collect 的签名:

fn collect<B>(self) -> B
where
    B: FromIterator<Self::Item>, 

我们只需要实现 FromIterator

use std::iter::FromIterator;

impl<A> FromIterator<Positioned<A>> for Map<A> {
    fn from_iter<T: IntoIterator<Item = Positioned<A>>>(iter: T) -> Self {
        todo!()
    }
}

还有一个问题,我们的 Map 构造函数需要确定 size:

fn new(size: Vec2) -> Self

我们只是得到已知位置的 tile 流 —— 我们不知道最后的地图有多大!所以我想我们的问题不太适合用 collect

也许还有另一种方法/trait?一种可以让我们获取现有 Map 的方法?

酷熊:有的,看我找到了什么:

fn extend<T>(&mut self, iter: T)
where
    T: IntoIterator<Item = A>, 

啊,看起来有用。

use std::iter::Extend;

impl<A> Extend<Positioned<A>> for Map<A> {
    fn extend<T: IntoIterator<Item = Positioned<A>>>(&mut self, iter: T) {
        for Positioned(pos, tile) in iter {
            self.set(pos, tile)
        }
    }
}

太棒了!

好吧,我想现在我们需要解析我们的地图:

impl Map<Tile> {
    fn parse(input: &[u8]) -> Self {
        let mut columns = 0;
        let mut rows = 1;
        for &c in input.iter() {
            if c == b'\n' {
                rows += 1;
                columns = 0;
            } else {
                columns += 1;
            }
        }

        let mut iter = input.iter().copied();
        let mut map = Self::new(Vec2 {
            x: columns,
            y: rows,
        });
        for row in 0..map.size.y {
            for col in 0..map.size.x {
                let tile = match iter.next() {
                    Some(b'.') => Tile::Floor,
                    Some(b'L') => Tile::EmptySeat,
                    Some(b'#') => Tile::OccupiedSeat,
                    c => panic!("Expected '.', 'L' or '#', but got: {:?}", c),
                };
                map.set(Vec2 { x: col, y: row }, tile);
            }
            iter.next();
        }
        map
    }
}

让我们来做一个快速的测试检查,我们的输入例子是:

L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL

我们的解析版本是:

fn main() {
    let m = Map::<Tile>::parse(include_bytes!("input.txt"));
    dbg!(&m.size);
    println!("{:?}", m);
}
$ cargo run --quiet
[src/main.rs:230] &m.size = Vec2 {
    x: 10,
    y: 10,
}
L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL

看起来不错!

现在,我想我们还需要在 Map 上实现一个 next 方法,对吗?但当 T = Tile 时:

impl Map<Tile> {
    fn next(&self) -> Self {
        let mut res = Self::new(self.size);
        res.extend(
            self.iter()
                .map(|Positioned(pos, tile)| Positioned(pos, tile.next(self.neighbor_tiles(pos)))),
        );
        res
    }
}

酷熊:这真是太棒了,我们所有的基础代码都有了回报!

让我们打印一些示例地图的迭代:

$ cargo add itertools
      Adding itertools v0.9.0 to dependencies
fn main() {
    let maps = itertools::iterate(Map::<Tile>::parse(include_bytes!("input.txt")), Map::next);
    for map in maps.take(5) {
        println!("{:?}", map);
    }
}

你真的需要把所有东西都变成迭代器吗?

Amos:现在是寒假,熊,我们还有时间!

$ cargo run --quiet
L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL

#.##.##.##
#######.##
#.#.#..#..
####.##.##
#.##.##.##
#.#####.##
..#.#.....
##########
#.######.#
#.#####.##

#.LL.L#.##
#LLLLLL.L#
L.L.L..L..
#LLL.LL.L#
#.LL.LL.LL
#.LLLL#.##
..L.L.....
#LLLLLLLL#
#.LLLLLL.L
#.#LLLL.##

#.##.L#.##
#L###LL.L#
L.#.#..#..
#L##.##.L#
#.##.LL.LL
#.###L#.##
..#.#.....
#L######L#
#.LL###L.L
#.#L###.##

#.#L.L#.##
#LLL#LL.L#
L.L.L..#..
#LLL.##.L#
#.LL.LL.LL
#.LL#L#.##
..L.L.....
#L#LLLL#L#
#.LLLLLL.L
#.#L#L#.##

现在回答问题,题目如下:

通过重复应用座位规则来模拟你的座位区域,直到座位状态不再改变。此时,有多少位置被占了?

酷熊:嗯,我们不得不将当前的状态与之前的状态对比... 听起来像是 tuple_windows 的工作!

Amos:确实是! 我不想一个一个地比较所有的 tile...

酷熊:我们可以在 Map 上派生 PartialEq trait?

Amos:这主意不错!

#[derive(PartialEq)]
struct Map<T> {
    size: Vec2,
    tiles: Vec<T>,
}
  • 酷熊热辣小贴士 注意: Vec2 已经派生了 PartialEq。至于 T,可能实现了,也可能没有实现。Map<T> 则只有在 T 本身实现/派生 PartialEq 时,才会实现 PartialEq

Amos:我们怎么确定这些?

酷熊:为什么,我们可以使用 cargo-expand 检查派生宏展开的结果!

#[automatically_derived]
#[allow(unused_qualifications)]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for Map<T> {
    #[inline]
    fn eq(&self, other: &Map<T>) -> bool {
        match *other {
            Map {
                size: ref __self_1_0,
                tiles: ref __self_1_1,
            } => match *self {
                Map {
                    size: ref __self_0_0,
                    tiles: ref __self_0_1,
                } => (*__self_0_0) == (*__self_1_0) && (*__self_0_1) == (*__self_1_1),
            },
        }
    }
    #[inline]
    fn ne(&self, other: &Map<T>) -> bool {
        match *other {
            Map {
                size: ref __self_1_0,
                tiles: ref __self_1_1,
            } => match *self {
                Map {
                    size: ref __self_0_0,
                    tiles: ref __self_0_1,
                } => (*__self_0_0) != (*__self_1_0) || (*__self_0_1) != (*__self_1_1),
            },
        }
    }
}

Amos:啊! 看... 生成了。这是合理的。而且它使用的是 core:: 而不是 std:: 中的类型,我想这也是有道理的。

继续 —— 让我们在 Map 上实现 last 方法,它模拟达到一个最终的的状态:

impl Map<Tile> {
    fn last(self) -> Self {
        use itertools::Itertools;
        itertools::iterate(self, Map::next)
            .tuple_windows()
            .find_map(|(prev, next)| if prev == next { Some(next) } else { None });

        todo!();
    }
}

酷熊:啊哦,这不管用:

$ cargo check --quiet
error[E0277]: the trait bound `Map<Tile>: Clone` is not satisfied
   --> src/main.rs:210:14
    |
210 |             .tuple_windows()
    |              ^^^^^^^^^^^^^ the trait `Clone` is not implemented for `Map<Tile>`

error[E0277]: the trait bound `Map<Tile>: Clone` is not satisfied
   --> src/main.rs:211:14
    |
211 |             .find_map(|(prev, next)| if prev == next { Some(next) } else { None });
    |              ^^^^^^^^ the trait `Clone` is not implemented for `Map<Tile>`
    |
    = note: required because of the requirements on the impl of `Iterator` for `TupleWindows<Iterate<Map<Tile>, for<'r> fn(&'r Map<Tile>) -> Map<Tile> {Map::<Tile>::next}>, (Map<Tile>, Map<Tile>)>`

error: aborting due to 2 previous errors

噢!我们也需要在 Map 上派生 Copy。但是如果我们要克隆 Map,我们不希望有太多的分配和拷贝。

酷熊:我们可以直接用 im crate 吗?

让我们看看它有多大程度上是一种“临时替代品”:

$ cargo add im
      Adding im v15.0.0 to dependencies
use im::Vector;

#[derive(PartialEq)]
struct Map<T> {
    size: Vec2,
    tiles: Vector<T>,
}

我们已经有一个问题了:

$ cargo check --quiet
error[E0369]: binary operation `==` cannot be applied to type `Vector<T>`
  --> src/main.rs:71:5
   |
71 |     tiles: Vector<T>,
   |     ^^^^^^^^^^^^^^^^
   |
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting this bound
   |
68 | #[derive(PartialEq + std::cmp::PartialEq)]
   |                    ^^^^^^^^^^^^^^^^^^^^^

让我们看看 im::Vector 是否实现了 PartialEq

impl<A: Clone + PartialEq> PartialEq<Vector<A>> for Vector<A>

但前提是 A 实现了 Clone trait

好吧,Tile 绝对实现了 Clone,我们可以这么做:

#[derive(PartialEq)]
struct Map<T>
where
    T: Clone,
{
    size: Vec2,
    tiles: Vector<T>,
}

现在我们需要将这个约束添加到我们的其他 impl 块中 —— 这留给读者作为练习。

  • 酷熊热辣小贴士 无论在哪里,我们有 T: Copy 的约束,我们都不需要添加 +Clone,因为实现了 Copy 意味着实现了 Clone
pub trait Copy: Clone { }

现在我们还可以在 Map 上派生 Clone trait:

#[derive(PartialEq, Clone)]
struct Map<T>
where
    T: Clone,
{
    size: Vec2,
    tiles: Vector<T>,
}

完善我们 last 的实现:

impl Map<Tile> {
    fn last(self) -> Self {
        use itertools::Itertools;
        itertools::iterate(self, Map::next)
            .tuple_windows()
            .find_map(|(prev, next)| if prev == next { Some(next) } else { None })
            .unwrap()
    }
}

我们试试!

fn main() {
    let last = Map::<Tile>::parse(include_bytes!("input.txt")).last();
    println!("{:?}", last);
}
$ cargo run --quiet
#.#L.L#.##
#LLL#LL.L#
L.#.L..#..
#L##.##.L#
#.#L.LL.LL
#.#L#L#.##
..L.L.....
#L#L##L#L#
#.LLLLLL.L
#.#L#L#.##

酷熊:耶,结束了! 接招,停机问题

而且,既然我们对这个问题的答案是以数字的形式出现,那么让我们来计算一下最终有多少座位被占:

fn main() {
    let last = Map::<Tile>::parse(include_bytes!("input.txt")).last();
    println!("{:?}", last);
    println!(
        "there are {} occupied seats",
        last.iter()
            //      👇  this is a Positioned<Tile>
            .filter(|p| matches!(p.1, Tile::OccupiedSeat))
            .count()
    );
}
$ cargo run --quiet
#.#L.L#.##
#LLL#LL.L#
L.#.L..#..
#L##.##.L#
#.#L.LL.LL
#.#L#L#.##
..L.L.....
#L#L##L#L#
#.LLLLLL.L
#.#L#L#.##

there are 37 occupied seats

这个例子就是这样! 我们用题目的输入来试试:

$ cargo run --quiet
#L#L#.#LL#L##L#.#LL##L##L#.#L#L#L#.#L#L#L#L#L#L#L#L#.#L#L#L#L#.#L##L#.#L#L#L#.#L#L#L#L#LL#L#L#L#L#
LLLLL.LL.L.LLLL.LLL..LLLLL.LLLLLL#LLLLLLLLLLLLLLLLLL.LLLLLLLLL.LLLLLL.LLLLLLL.LLLLLLL.LLLLLLLLLLLL
#L#L#.L#L#.#L#L#L#L#L#L#L#L#L#L.L..#L#L#L#L#L#L#L#L#L#L#L#L#L#.#L#L#L.L#L#L##L#L#L#L#.#.L#L#L#L#L#
LLLLLLLLLL.LLLL.LLLLLLLLLL.LLLL#L#.LLLLLLLLLLLLLLLLL.LLLLLLLLL.LLLLLL#LLLLLLLLLLLLLLL.LLLLLLLLLLLL
#L#L#L#L#L#L#L#.#L##.#L#L#.#L#LLLL.##.L#L#L#..#L#L#L#L#L#L#.L#.#L#L#L#.#L#L#L#L##L#L#.L#L#L#L#L#L#
LLLLLLLLLL.LLLL.LLLL.LLLLL.LLLL#L#LLLLLLLLLL.LLLLLLLLLLLLLLLLL.LLLLLL.LLLLLL.LLLLLLLLLLLLLLLLLLLLL
#L#L#L.L##.##L#.#L##.#L#L#L##LLLL#.#L#L#L#L#.#L#L#LL.#L##L#.##.#L##.#L#L#L#.#L#LL#L##.##L#L#L#L#LL
.LL..L#.L..L.L...LL..L..L.L...L#..L...LL..LLLL.#.L.#.....L....L.LL...L.LL.LL....#...L..L....L....#
#L#L#.LL##.#L#L#L#L#.#L#L#.#L#LLL#L#L#L#L#LLL#LLL#LLL#L#L#L#L#.#L#L#L.#L#L#L#.#LLL.#L#LLL#L#L#L#LL
#LLLLL#LLL.LL#L.L#LL.LLL.L.LLLL#L#L#L#L#L#L#LLL#LLL#.L.LLLLLLLLLL.LLLLLL#L#L#.#L#LL#L.L#LLLLL.LLL#
LL#LL.#LLL.LLLL.LLL#L#L#L#.#L#LLLLLLLLLLLLLLL#LLL#LL.#L#L#L#L#.#L#L#LLLLLLLLL...LLLLLLLLL#.#L#L#LL
#LLL#L.L#L#L#L#.#L#L.LLLL#L#L.L#L#.#L#L#L#L#..L#LL.#.LLLLLL#L#.#LLLLL#.L#L#L#.#L#LL#L#L#LLL.LLL.L#
L.#...#.....LLL.L..L#...#......L.LLL........L#..L#L..#.#L#....L..#.#.L..L..L.LL..L.......LL..L.#..
#LLL#.#L#L#L#L#L#LLL#L#LLL#L#LL#L#.#L#L#L#L#.LL#LLL#LLLLLLL#L#.#LLL.L#L#L#L#L###L#L#L#L#L#L#L#LLL#
LL#L#LLLL.LLLLL.LL#L.LLL#L.LLLLLLL.LLLLLLLLLL#LLL#LL.#L#L#LLLL.LL#L#L.LL.LLLL.LLLLLLLLLL.LLLLLL#LL
#LLL###L#L.L#L#.#LLL.#LLL.##LL#L#.##L#L#L#L#.#L#LLL#.LLLLLL#L#.#LLLLL.#L#L#.#.#L#L#L#.##L#L#L#L#L#
LL#LL.LLLL#LLLL.LLL#.LL#LL.LLLLL.LLLLLLLLLLL.#LLL#LL.#L#L#LLL#LLL##L#.LLLLLLL..LLLLLL.LLLL.LLLLLLL
#LLL#.#L#L.L#L#.#LLL.#LLL#L#L#.#L#L#L##L#L#L.L##LLL#.LLLL.L#L#.#LLLLLL#L#L#L#.#L#L#L#.#L##L#L#L#L#
..#..L.L........LL.#.#..##L........L....#L..#L#.L#....#.......LL.##L#....L...L.LL........#.L......
#LLL#.#.L#.#L#L#L#L#.LLLLL.#L#L#L#L#.#LLLLLL.LLLLLL#.LLLL#L#L#.#LLLLL.#L#L#L#LLL#L#L#.#.LLL#L#L#.#
LL#L#.LLLL.LLLL..LL#.#L#L#L#LLLLLL.#L#L#L#L#L#L#L#LL.#L#LLLL.LLLL##L#L#L#LLLLL#LLLLLL.LL##LLLLLLLL
.LLLLL##L#.#L##.#LLL..LLL#LLLLLLLL.#LLLLLLLL.LLLLLLL.LLLL#L#L#.#LLL.L.L..L#L#.LL#L#L#L#LLLL#L#L#L#
#L#L#.LLLL.LL#L.LL##.#L#LLL#.#L#L#LLL#L#L#L#.#L#L#L#.#L#L#LLLL.LL##L#.#L#L#LL.#LLL#L#L#LL#LL.#LLLL
.....L#.L.##...#..LL.LL..#...#..L.L#..LL..L.L..........#....#...#.L....L.L.L....#L.........#L..#L.
#L#L#.LLL#.LL#L.L#L#L#L#LLL#LLL#L#.LL#.#L#L#L#L#L#L#.#LLL#LLLL.##L#L#.#L#L#L#.#.#.#L#.#L#LLLL#LLL#
LLLLLL##LL.LLLL.LLLL.LLLL#.#L#L#LLL#LLLLLL.LLLL.LLLL.LL#LLL#.#LLLLLLL.LLL.LLL.LLLLLLL.LLLL##LLL#LL
#L#L#.LLL#.#L#L#L.L#.#L#LL.LLLLLL#.#L#L#L#L#L#L#L#L#.#L.L#LLL..##L#L#.#L#L#L#L#L#L#L#.#L#LLLL#L.L#
LLLLLL##.LLLLLL.L#LL.LLLL#.#L#L#LL.LLLLLL.LL.LLLLLLLLLLLLLL#.#..LLLLLLLLLLLLL.LLLLLLL.LLLL##LLLLLL
#L#L#.LLL#.#LL#.LLL#.#L#LL.LLLLLL#L#L#L#L#L#.#L#L###.#L#L#LLLL.L#L#L#.#L#.#L#L#L#L#L#.#L#LLLL#L#L#
L..L#.#.......L.#L..L.....#..#.#..L.L...L....LLL..........LL#.#L......L.L..L.LL..L....LL..#.......
#L#LL.LLL#.#LL#.LLLLL#L#LL.LLLLLL#.LL#L#L#L#.#L#L#L#.#L#LLLLLLLLL#L#LL#L#L#L#.L#L#L#L#L#LLLLL#L#L#
#LLL#.##.L.LLL..#L#L.LL#L#.#L#L#LL.#LLLLLLLL.LLLLLLL.LLLL#L#L#.#LLLLL.LLLLLLLLLLLLLLL.LLL#L#LLLLL.
LL#LL.LLL#.#LL#.LLLL.#LLLL.LLLLLL#LLL#L#L#L#.#L#L#L#.#L#LLLLL#.LL#L##.#L#L#L#.#L#L#L#.#.LLLLL#L#L#
#LLL#.##LL.LLLL.#L##.LL#L#.#L#L#LL.#LLLLLLLL.LLLLLLLLLLLL#L#LL.#LL.LL.#LLLLLL.#LLLLLLLLLL#L#LLLLLL
LL#LL.LLL#.#.L#.LLLLL#LLLL.LLLLLL#.LL#L#L#L#.#L#L#L#.#L#LLLLL#.LL#L##.#L#L#L#.LLL#L#L.L#LLLLL#L#L#
#LLL#.##L#.#LLL.#L#..LL#L#.#L#L#L..#LLL#LLLL.LLLLLL#.LLLL#L#L#.#LLLLL.L.LLLLL.#LLLLLL#LLL#L#LLLLLL
LL#LLLLLLL.LL##.LLLL#LLLLL.LLLL.L..LL#LLL#L#.#L#L#LL.#L#LLL.LLLLL#L#L##L#L#L#.LLL#L#L.L#LLLLL#L#L#
#LLL#.##L#.#LLL.#L#L.L#L#L#L#L#L##.#L.L#L#LL.L.LLLL#.LLLL#L#L#L#LLL#..LLLLLLL.L#LLLLL#L#L#L#L#LLLL
...L#LLL..L...#..L..L.............L..#...#..#.#.#..LL#.#.....L.L.#.#.#..#.L#.#..L.#..#LL..LL...#L#
#L#LL.L#L#.#LLL.##L#.#L#L#.#L#.#L#.#LLL#LLLL.LL.LL##.LLLL.L#L#L#LLLLL.LLLLLL..LL#LLLL.L#L#L#L#LLLL
LLLL#LLLLL.L.##.LLLLLL.LLL.LLLLLLL.LL#LLL#L#.#L#LLL#L#L#L#LLL..L.#L#L.LL#L.L#.#LLL#L#.LLLLLLLLL#L#
.#LLLL##L#.#LLL.#L##.##L#L.L#.##L#.#LLL#LLLL.LLLL#LL.#L#L#L###L#LLLLL#LLLL#LL.LL#LLLL.##.#L#L#LLLL
LLL##.LLLL.LLL#.LLLL.LLLLL#LL.LLLL.LL#LLL#L#.#L#LLL#.LLLLLLLLL.LL#L#L.L##LLL#L#LLL#L#.LLLLLLLLL#L#
#LLL..#L#L#L#LL.#L##.#L.L#..##L#L#.#LLL#LL.L.LLLL#L#.#L#.#L#L#.#LLL#L#L#.L#L#LLL#L#LL.##L#.#L#LLLL
LL#L#L.LLL.LLL#.LLLL.LL#LL.#LL...LLLL#LLL#L#L#L#LLLLLLLLLLLLLLLLL#LLL.LLLLLLL.#LLLLL#.LLLLLLLLL#L#
#LLL#.#L#L.L#LL.#L##.#L#L#.LL#L#L#.#LLL#LLLL.LLLL#L#.#L#L#L#L#L#LLLL#.#L#L#L#.LL#L#LL.##.#L#.#LLLL
LL#LLLLLLL#LLL#LL.LL.LLLLL.#LLLLLL.LL#LLLLL#L#L#LLLLLLLLLLLLLLLLL##LLLLLLLLLL.#LLLLL#.LLLLLLLLL#L#
#LLLL##L#L..#LL.#L##.#L#L#.#L#L#L#.#L.L#L#L#L.L#L#L#.#L#L#L#L#.#LLLL#L#L#L#L#.LL#L#LL.L#L#L#L#LLLL
.L#..L....#..L#....L.......#........L#......L...L..............L.#...L.L....LL#L.....#......LLL..#
#L#L#.#L#L.L#LL.#L##.#L#L#L#L#L#L#.#LLL#L#L#.#L#L###L#L#L.L#L..#L#L####LLL#L#.LLL#L#L#L#L#L#L#L#LL
LLLLL.#LLL#LLL#.LLLLLLLLLL.LL.LLLL.LL#LLLLLLLLLLLLLL.LLLL#LLL#.LLLLLL.LL#.LLLL#LLLLLL.LLLLLLLLLLL#
#L#L#.LL#L.L#LLL#L#L#L##L#.#L#L#L#.#L.L#L#L#.#L#L#L.L#L#LLL#LL.L#L#L#.#LLL#L#.LL#L#L#.##L##L#.L#L#
LLLLL.#LLL.LLL#.LLLLLLLLLL.#LLLLLL.LLLLLLLLLLLLLLLL#LLLLL#LLLL#LLLLLL.LL#LLLL.#LLLLLL.LLLLLLLLLLLL
#L#L#.LLL##L#LL.#L#L.#L#L#.##L#L#..#L#L#L#L#.#L#L#LLL#L#LLL##L.L#L#L#L#LLL#L#.LL##L#L##L#L#.L#L#L#
...L.L.#.#LL..#......LLL.L..L......L...LLL....L....#..LL.#.L..#..L......#.....#L.L.....L........L.
#L.L#LLL.L.#LLL.L#L#.#L#L#.#L#.#L#.#L#L#L#L#.#L#L#L#.#L#LLL##..L#L#L#.#L#L#L#.LLLL#L#.L.L#L#L#L#L#
LL#LL.##L#.LL#L#LLLLL#LLLL.LLLLLLL.L.LLLLLL..LLLLLLL.LLLL#LLLL.LLLLLL.LL#LLLL.#L#L#LLL##LLLLLLL#LL
#LLL#.LLL..#LLLLL#L#.L.#L#.#L#L#L#.#L#L#L#L#.#L#L#L#L#L#LLLLL#.##L#L#.#LLL#L#LLLLLLLL.LLL#L#L#LLL#
LL#L#.#L#..#L#L#LLLLL#LLLLLLLLLLLL.LLLLLLLLLLLLL.LL..LLLL#L#.L.LLL.LL.LL#LL.LL#L#L##L#L#LLLLLLL#LL
LLLLL.LLLL.LLLLLL#L#.LL#L#.#L#L#L#.#L#L#L#L#.#L#L#L#.#L#LL.LLL#L#L#L#.#LLL#L#.LLLLLLL.L.L#L#L#LLL#
#L#L#L#L#L#L#L#.LLLL.#LLLL.LLLLLLL.LLLLLLLLLLLLLLLLL.#LLLL#L#L.LLLLLLLLL#LLLL.#L#L#L#.L#LLLLLL.#L.
LLLLL.LLLL.LLLL.L#L#.LL#L#L#L#L#L##L#L#L#L#L#L#L#L#L.LLL##LLLL.##L#L#L#LLL#L#.LLLLLLLLL.L#L#L#L.L#
#L##L#.#L#L#L#L#LLL#.#LLLLLLLLLLL..LLLLL#LLL.LLLLL#L#L#LLLL#L#.LLLLL#.LL#L#L#L#L#L#L#.##LLLLLLLLLL
...L...L...L...#L...LL.#.#..##..#....##...#L#L.#........#......#..#....L.L....L...L.L.L.#L#.#..#.#
#L#L#.#.L#.LL#L.L#L#.#LLLLLLLLLLLLL#L.LL#LLL.LLLL.L#.#L.LLL#L#.LLLLL#.#L#L#L#L#L#L#L#.#LLLLLLLLLLL
LLLL#.LLLL.#L#L.L#LLLLL#L#L#L#L#L#LLL#LLL.L#.#L#L#L#L#L#L#L#L##L#L#LL.LLLLLLLLL.LLLLL.LLL#L#L#L#L#
#L#LL.L#L#.LLLL#L#L#.#LLLL.LLLL.LL.#LLL#L#LL.LLLLLLL.LLLLLLLLLLLLLLL#.#L#L#L#.#L#L#LL#L#LLLLLLLLLL
LLLLL#LLLL.#L#L.LLLL.LL#L#.#L#L#L#.LL#LLLLL#.#L#L#L#L#L#L#L#L#.#L#LLLLLLLLLLL.LLLLLLLLLLL#.#L#L#L#
#L#LL.LL#L.LLLL.LL##L#LLLLLLLL.LLL.#LLL#L#LL.LLLLLLL.LLLLLLLLL.LLLL#L#L#L#L#L.L#L####.L#LLLLLLLLLL
LLLL#.#LLL#L#L#.#LLLLLL#L#L#L#L#L#LLL#LLLLL#.###L#L#.#L#L#L#L#.#L#LL.LLLLLLLL#LLLLLLL.LLL#L#L#.#L#
#L#LL.LL#LLLLLLLLL#L#LLLL#LLLLLLLL.#LLLLLLLLLLLLLLLLLL.LLLLLLL.L.LL#L#L#L#L#LLLLLL#L#L#LLLLLLLLLLL
LL#L#.#LLL#L#L#.#L.L.L#LLLL#L#L#L#.LL##L#L##L#L#L#L#L#L#L#L#L#L#L#LLL.LLLLLLL.#L#LLL..#L##L#L#L#L#
#L.L...L#..LL..L..#.L....#...L..L..#L......#L.L.......L..LL........L#.#L#L#.......#.....LL.#L.....
LL#LL.LLLLLL#.#.#LLL.#L#LL.#L#L#L#.LL#L#L#LLLLL#L#L#.#L#L#LLL..LL#LLL.LLLLLL#L#L#L.L#.#L##LLLLL#L#
#L#L#.#L#L#L#.L.LL##.#LLL#.LLLLLLL.#LLLLLLL#.#LLLLLL.LL#LLL#L##LL#L###L#L#L##.#L#L#LL.LLLLL#L#LLLL
LLLLL.LLLL.LLL#L#LLLL.L#LL.L.#L#L#.LL#L#L#LL.LL#L#L#.#LL.#LLLL.LLLLLL.LLLLLLLLLLLL#L#.#L##LLLLL#L#
#L#.#.#L#L#L#L#.#L#L##LLL#L#LLLLLL.#LLLLL.L#.#L#LLLL.LL#LLL#L#.##L#L#.#L#L#L#.#L#LLLL.LLLLL#L#.#LL
..#.#....LL.LL.....L.LL......#.#L..LLL#L#......LL.L.#L.LL#...LLLL......L.L.L...LL..#..#..#...LL..#
#L#L#.#L#L#L#LL.#L#..#LLL#.#LLLLL#.#LLLLLLL#.#L#L#L#..#LLLL###.#LLL#L###.##L#.#L##LLL.LLLLL#L#.#L#
LL#L..LLLL.LLL#.LLLL.LL#LL.LL#L#LLLLL#L#L#LLLLLLLLLL.LLLL#LLLL.LL#LLL.LLLLLLL.LLLLL#L##L##LLLLL#LL
#L#.#.#L##.#LLL.L#L#.#LLL#.#LLLLL#.#LLLLLLL#L#L#L#.#.#L#LLLL#L#LLLLLL.#L#L#L#.#L##LLL.LLLLL#L#L.L#
L.#L..LLLL.L.#L#L#LLLLL#LL.LL#L#LL.LL#L#L#L..LLL.LLL.LLLL#L.LL.L#L#L#.LLLLLLLLLLLLLL#.#L##LLLLLLLL
#L#L#.#LL#.#L.L.LLLL.#LLL#.#LLLLL#LLLLLLLLL#L#L#L#L#.#L#L.L#L#LLLLLL..#L#L#L#.#L#L#LL.LLLLL#L#L#L#
L....L...L..L.#...#....#......#..#L#..#..#L.LL.L.L....L..#.#.LL#.#.L#LL.........L..L#.#L#......L..
#L#L#.##L#.#LLL.#LLL.#L#L#.#LLLLLL.LLLLLL#L#L#L#L#L#.#L#L#.LL#LLLLLLLL#L#L#L#.#L#LLLL.LLLLL#L#L#.#
LL#LL.LLL..LLL#.LLL#.LLLLL.LL#L#L#.#L#L#L#L#L.LLLL.LLLLLL#L#LLLL#L#L#.LLLLLLL.LL#L#L#.#LL#LLLLL.LL
LLLL#.#LL#LL.LL.LLLL.#L#L#.#LLLLLL.L.LLLLLLL.LLLLL#L#LLLLLLLLL#LLLLLLL#L#L#L#L#LLLLLL.LLLLL#L#L#L#
#L#LL.LLL#.#L###L#L#.#L#LL.LL#L#L#.#L#LL#L#.#L#L#L#L.L#L#L#L#L.L#L#L#.#L.L#L#L#L#L#L#.#LL#L#L#L#L#
LLLLL.LLLL.LLLL.LLLL.LLLLL.LLLLLLL.LLLLLLLLLLLLLLLLL.LLLLLLLLL.LLLLLL.LLLLLLL.LLLLLLLLLLLLLLLLLLLL
#L#L#LL#L#L#.#L#L#L#.#L##L#.#L#L#L#L#L##L#L#.#L#L#.#L#L#L#L#L#.#L#L#L##L#L#L#.#L#L#L#.#LL#L#L#L#L#

there are 2289 occupied seats

就这样吧,我们可以进入下一部分。

第二部分

这是一个新的部分,规则,他们有些改变。

现在我们要找的座位可能不是相邻的,但它是 8 个方位之一。

之前,我们看过这些,我们只看到两个有人的座位:

但是现在,我们朝每个方向前进并观察,直到我们在对应方向上看到一个座位 —— 我们看到 8 个有人坐的座位:

是时候使用一个新方法了! visible_seats 完全可以做到这一点。

impl Map<Tile> {
    fn visible_seats(&self, pos: Vec2) -> impl Iterator<Item = Tile> + '_ {
        use itertools::Itertools;
        (-1..=1)
            .map(|dx| (-1..=1).map(move |dy| (dx, dy)))
            .flatten()
            .filter(|&(dx, dy)| !(dx == 0 && dy == 0))
            // for each direction...
            .map(move |(dx, dy)| {
                // keep moving in set direction
                itertools::iterate(pos, move |v| Vec2 {
                    x: v.x + dx,
                    y: v.y + dy,
                })
                // as long as we're on the map
                .map(move |pos| self.index(pos))
                .while_some()
                // and until we reach a seat
                .filter_map(move |index| match self.tiles[index] {
                    Tile::Floor => None,
                    seat => Some(seat),
                })
                .take(1)
            })
            .flatten()
    }
}

酷熊:好吧,那个肯定需要测试一下。

Amos:同意。

现在是使用 indoc crate 的好时机!

$ cargo add indoc
      Adding indoc v1.0.3 to dependencies
#[test]
fn test_visible_seats() {
    let map = Map::<Tile>::parse(
        indoc::indoc!(
            "
            .......#.
            ...#.....
            .#.......
            .........
            ..#L....#
            ....#....
            .........
            #........
            ...#.....
            "
        )
        .trim()
        .as_bytes(),
    );
    println!("{:?}", map);
    assert_eq!(map.visible_seats(Vec2 { x: 3, y: 4 }).count(), 8);
    assert_eq!(map.visible_seats(Vec2 { x: 8, y: 0 }).count(), 2);
}
$ cargo t -q

running 2 tests
..
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

看起来不错!

还有第二个测试:

#[test]
fn test_visible_seats2() {
    let map = Map::<Tile>::parse(
        indoc::indoc!(
            "
            .##.##.
            #.#.#.#
            ##...##
            ...L...
            ##...##
            #.#.#.#
            .##.##.
            "
        )
        .trim()
        .as_bytes(),
    );

    assert_eq!(map.visible_seats(Vec2 { x: 3, y: 3 }).count(), 0);
}
$ cargo t -q

running 3 tests
.F.
failures:

---- test_visible_seats2 stdout ----
.##.##.
#.#.#.#
##...##
...L...
##...##
#.#.#.#
.##.##.

thread 'test_visible_seats2' panicked at 'assertion failed: `(left == right)`
  left: `8`,
 right: `0`', src/main.rs:224:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    test_visible_seats2

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

酷熊:看起来很糟糕!

那么,这里发生了什么? 让我们试着找出它看到了哪些 tile:

// in our test
dbg!(map.visible_seats(Vec2 { x: 3, y: 3 }).collect::<Vec<_>>());
assert_eq!(map.visible_seats(Vec2 { x: 3, y: 3 }).count(), 0);
$ cargo t -q seats2

running 1 test
F
failures:

---- test_visible_seats2 stdout ----
.##.##.
#.#.#.#
##...##
...L...
##...##
#.#.#.#
.##.##.

[src/main.rs:224] map.visible_seats(Vec2{x: 3, y: 3,}).collect::<Vec<_>>() = [
    L,
    L,
    L,
    L,
    L,
    L,
    L,
    L,
]
thread 'test_visible_seats2' panicked at 'assertion failed: `(left == right)`
  left: `8`,
 right: `0`', src/main.rs:225:5

酷熊:哦,它只看到它自己! 因为我们是从 pos 开始的,而不是 pos + (dx, dy)

Amos:是的,看起来我们在调用 itertools::iterate 时把初始值搞错了。

酷熊:或者我们可以跳过这个?

Amos:我也这样觉得。

// keep moving in set direction
itertools::iterate(pos, move |v| Vec2 {
    x: v.x + dx,
    y: v.y + dy,
})
// 👇 new!
.skip(1)

现在我们所有的测试都通过了:

$ cargo t -q

running 3 tests
...
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

我们调整第二部分问题中指定的 Tile::next 方法:

现在需要 5 个或更多可见的有人坐的座位才能使一个有人坐的座位变成空的(而不是以前规则中的 4 个或更多)

Self::OccupiedSeat => { match neighbors
        .filter(|t| matches!(t, Self::OccupiedSeat))
        .count()
    {
        // 👇 new!
        // up to 4 neighbors: still okay for now
        0..=4 => Self::OccupiedSeat,
        // that's too many folks!
        _ => Self::EmptySeat,
    }
}

然后更改 Map::<Tile>::next 使用 visible_seats 而不是 neighbor_tiles

impl Map<Tile> {
    fn next(&self) -> Self {
        let mut res = Self::new(self.size);
        res.extend(
            self.iter()
                //                                                          👇👇👇
                .map(|Positioned(pos, tile)| Positioned(pos, tile.next(self.visible_seats(pos)))),
        );
        res
    }
}

我们应该可以开始运行了!

这个程序在 debug 模式时构建需要一点时间:

$ cargo build --quiet
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
$ time ./target/debug/day11 > /dev/null
./target/debug/day11 > /dev/null  5.68s user 0.00s system 99% cpu 5.680 total

让我们看看 release 构建的版本执行情况:

$ cargo build --release
    Finished release [optimized] target(s) in 0.01s
$ time ./target/release/day11 > /dev/null 
./target/release/day11 > /dev/null  0.24s user 0.00s system 99% cpu 0.240 total

好多了!

  • 酷熊热辣小贴士 如果您的 Rust 程序在 crate 运行时比较慢,不要忘记使用 --release 标志来启用优化运行速度!

现在让我们来看看最终答案:

$ cargo run --release
    Finished release [optimized] target(s) in 0.01s
     Running `target/release/day11`
#L#L#.L#L#L#L#L.#L#L#L#L#L.#L#L#L#.L#L#L#L#L#L#L#LL#.L#L#L#L#L.#L#L#L.#L#L#L#.L#L#L#L#L#L#L#L#L#L#
LLLLL.#L.L.LLLL.LLL..LLLLL.LLLLLLLLLLLLLLLLLLLLLLLLL.LLLLLLLL#.LLLLL#.LLLLLLL.LLLLLLL.LLLLLLLLLLLL
#L#L#.LL#L.L#LL#L#L#L#L#LL#L#L#.L..#L#L#L#L#L#L#L##L#LL#L#L#LL.##L#LL.##L#L#L#L#L#L#L.#.L#L#L#L#L#
LLLLLL#LLL.LLLL.LLLLLLLLLL.LLLLL#L.LLLLLLLLLLLLLLLLL.LLLLLLLL#.LLLLL#LLLLLLLLLLLLLLL#.L#LLLLLLLLLL
#L#L#LLL#L#L#L#.L#L#.LL#L#.L#L#LLL.#L.#L#L#L..#L#LL#L#L#L#L.LL.##L#LLL.L#L#L#L#L#L#LL.LLL#L#L#L#L#
LLLLLL#LLL.LLLL.#LLL.#LLL#.LLLLL#L#LLLLLLLLL.LLLLLLLLLLLLLL#L#.LLLLL#.LLLLLL.LLLLLLL#LL#LLLLLLLLLL
#L#L#L.L#L.#L##.LLL#.LL##LLL##L#LL.L#L#L#L#L.#L#L##L.#L#L#L.LL.##L#.LLL#L#L.L#L#L##LL.LLL#L#L#L#L#
.LL..LL.L..L.L...#L..#..L.#...LL..L...LL..LLLL.L.L.#.....L....L.LL...#.LL.L#....L...#..L....L....L
LL#LL.L#L#.L#LLLLLLL.LLL#L.LLLL#LLL#L#L#L#L#L#LL#LLL#L#LL#LLL#.LLLL#L.L#LLLL#.LLLL.LLL#L#L#L#L#LL#
#LLL#LLLLL.L#L#.L#L#.LL#.L.L#L#LL#LLLLLLLLLLLLLLLL#L.L.LLLL#LLLL#.LLLLLLL#LLL.L#L#LL#.LLLLLL#.LLLL
LL#LL.##L#.LLLL.#LLLL#LLL#.LLLLLLLL#L#L#L#L#L#L##LLL.#L#L#LLL#.LL#L#LLL#LLL#L...LLLLLLL#L#.LL#L#L#
#LLL#L.LL#L#L##.LL#L.LL#L#L#L.L#L#.LLLLLLLLL..LLLL.#.LLLLLL#LL.#LLLLL#.LL#LLL.#LL#L#L#LLLL#.LLL.LL
L.#...L.....LLL.#..L#...L......L.LL#........LL..##L..#.L#L....L..L.#.L..L..L.L#..L.......LL..#.L..
#LLLL.L#LL#L#L#LLLL#LLLL#L#L#L#L#L.LL#L#L#L#.L#LLLL#LLLLLL#LL#.LL#L.LL#L#L#L#LLL#L#L#L#LL#L#L#L#L#
LL#L#LLLL.LLLLL.##LL.#L#LL.LLLLLLL.#LLLLLLLLL#LL##LL.L#L#LL#LL.#LLLL#.LL.LLLL.#LLLLLLLL#.LLLLLLLLL
#LLLLL#LL#.L#L#.LLL#.LLLL.L#L#L#L.LLL#L#L#L#.LL#LLL#.LLLL#LLL#.L#L#LL.#LLLL.#.LL#L#L#.LL#LL#L#L#L#
LL#L#.LLLLLLLLL.##LL.#L#L#.LLLLL.#L#LLLLLLLL.#LLL#LL.L#LLLL#LL#LLLLL#.LL#L#LL..LLLLLL.#LL#.LLLLLLL
#LLLL.#L#L.#L##.LLL#.LLLLLL#L#.L#LLLL#L#L#L#.LLL##L#.LLL#.LL#L.L#L#LLL#LLL#L#.L#L##L#.L#LLL#L#L#L#
..L..#.L........##.L.#..L#L........#....LL..#L#.L#....L.......LL.L#L#....L...L.#L........#.L......
L#L#L.L.L#.L#LLLLLL#.LL#LL.L#L#LL#LL.L#L#L#L.LLL#L#L.#L#L#L#LL.#LLLLL.L#L#L#LLLLLLLLL.#.LL#L#L#L.L
#LLLL.#LLL.L#L#..LLL.#LLL##LLLLLLL.##LLLLL#LL#LLLLL#.LLLLLLL.#LLL#L#L#LLLLLLL#L#L#L#L.LL#LLLLLL#L#
.LL#LLLL##.LLLL.#L#L..L#LLLL#L#L#L.LLL#L#LLL.LL#L#LL.L#L#L#LLL.LLLL.L.L..L#L#.LLLLLLL##LL#L#L#LLLL
L#L#L.#L#L.##L#.LL#L.LLLL#LL.LLL#L#L#LLLLLLL.#LLLLL#.LLLLLLL#L.L#L#LL.#L#LLLL.L#L#L#LLLLLLLL.LL#L#
.....L#.L.#L...#..LL.#L..L...#..L.LL..#L..#.L..........#....L...L.L....L.L.#....LL.........L#..LL.
#L#L#.LLL#.LLLL.L#L#LLL#L##LLLL#L#.LLL.L#LLLLL#L#L#L.#L#L#LL#L.#L#L#L.L#L#L#L.#.L.#L#.#L#L#LLLL#LL
LLLLLLL#L#.L#L#.LLLL.#LLLL.L#LLLLLLLL#LLLL.#L#L.LLL#.LLLLLLL.LLLLLLL#.LLL.LLL.#LLLLLL.LLLLL#L#LLL#
#L#L#.LLLL.LLL#L#.L#.LL#L#.LLL#L#L.#L#L#L#LLLLL#L#LL.##.L#L#L..L#L#LL.#L#LL#LLLL##L#L.L#L#LLLLL.LL
LLLLLL##.LLL#LL.#L#L.#LLLL.##LLLLL.LLLLLL.L#.LLLLLL#LLLLLLLL.#..LLLL#LLLLLLLL.#LLLLLL.LLLLL#L#L#L#
#L#L#.LLL#.LLL#.LLLL.LL#L#.LLL#L#L#L##L#LL#L.#L#L#LL.L#L#L#LLL.L#L#LL.##L.#L#LLL#L#L#.L#L#LLLLLLLL
L..LL.#.......L.#L..#.....L..L.L..L.L...L....LLL..........LL#.LL......L.#..L.L#..L....LL..L.......
#LL##.LL#L.LL##.LLLLLL#L#L.#L#L#L#.#LLLLL#L#.L#L#L#L.#L#L#LLL#L#L#L#L##LL#L#L.LLL#L#LL#L#LL#L#L#L#
LL#LL.#L.L.#LL..#L#L.LLLLL.LLLLLLL.LL#L#LLL#.LLL#LLL.#L#L#L#L#.LLLLLL.LLLLLLL#L#LLLL#.LLL#LLLLLLL.
#LLL#.LLL#.LLLL.LLLL.#L#L#.LL#L#LL#L#LLLL#LL.L#LLL#L.LLLLLLLLL.#L#L#L.##L#L#L.LLLL#LL.#.LLL#L#L#LL
LL#LL.##LL.#LL#.L#L#.LLLLL.#LLLLLL.LLL#L#LL#.LLL#LLL##L#L#L#L#.LLL.L#.LLLLLLL.#L#LLL#LL#L#LLLLLLL#
#LLL#.LLL#.L.L#.LLLLL#L#L#.LL#L#L#.L#LLLLLLL.#L#LL#L.LLLLLLLLL.L#L#LL.#LL#L##.LLLL#LL.LLLLL#L#L#LL
LL#LL.##LL.##L#.L#L..LLLLL.#LLLLL..#LLL#L#L#.LLLLLLL.#L#L#L#L#.LLLLL#.L.LLLLL.#L#LL#L#L#L#LLLLLLL#
#LLL#LLLL#.LLLL.LLL#L#L#L#.LL#L.#..LL#LLLLLL.#L#L#L#.LLLLLL.LL#L#L#LLL##L#L##.LLLLLLL.LLLLL#L#L#LL
LLL#L.##LL.##L#.L#LL.LLLLLL#L#L#LL.#L.L#L#L#.L.LLLLL.#L##L#L#LLLLLLL..LLLLLLL.#L#L##LLL#L#LLLLLLL#
...LL#LL..#...L..L..#.............L..L...L..#.L.L..#L#.L.....L.#.L.#.L..#.L#.L..#.L..#LL..LL...#LL
#L#L#.L##L.LLLL.LL#L.LL#L#.L#L.L#L.L#L#L#L#L.L#.L#LL.LL#L.L#L#LLL#LLL.#LLLLL..LLLL#LL.#L#LL#L#LLL#
LLLLLLLLLL.#.LL.#L#LL#.LLL.#LL#L#L.LLLLLLLLL.LLLLLLL#LLLL#LLL..#.LL##.LL#L.#L.#L#LLL#.LLL#LLLLL#LL
.L#L#L#L##.LL#L.LLLL.LL#L#.LL.LLLL.#L#L#L#L#.L#L#L#L.#L#LLLL#LLLL#LLLL#LLLLL#.LLL#LLL.L#.LL#L#LLL#
#LLLL.LLLL.#LLL.#L#L.#LLLLL#L.#L#L.LLLLLLLLL.#LLLLL#.LLL#L#LLL.#LLL##.LL#L#LLL#LLLL#L.LL##LLLLL#LL
LL#L..LL##LLL#L.LLLL.LL.L#..LLLLLL.#L#L#L#.L.LL#L#LL.##L.LL#L#.LL#LLLL#L.LLLLLLL#LLLL.#LLL.L#LLLL#
#LLL#L.LLL.#LLL.#L#L.#L#LL.L#L...#LLLLLLLLL#L#LLLLL#LLL#L#LLLLL#LL#L#.#LLLLL#.LL#L##L.#L#L#LLL#LLL
LL#LL.#L##.LL#L.LLLL.LLLL#.LLL#LLL.#L#L#L#LL.LLL##LL.#L#L#L#L#LLLLLLL.#L#L#LL.#LLLLLL.LL.LLL.LLL##
LLLL#LLL#L#L#LL#L.#L.#L#L#.L#LLL#L.LLLLLL#L##L#L#L#L#LLLLLLLLL##L#L#LLLLLLLL#.LLL#L#L.L#LL#L#L#LLL
#L#LLLLLLL..#L#.LLLL.LLLL#.LLL#LLL.LL.#LLLLLL.LLLL#L.L#L#L#L#L.LL#L#L#L#L#L#L.L#L#L#L.L#L#LLLLLLL#
.LL..#....L..L#....L.......L........#L......L...#..............L.L...L.#....L#LL.....L......#L#..L
LL#LL.L###.LLLL.##L#.LLLL#LLL#L#L#.L#L##L#L#.L#L##L#LLLLL.LLL..#L#LLLLLLLL#L#.LL#L#L#L#L#L#LLLLLL#
#LLL#.LLLL#L#L#.LLLLL#L#LL.#L.LLL#.LLLLLLLL#LL#LLLLL.##L#L#L##.LLLL#L.#LL.LLLL#L#LLLL.#LLLLLL#L#LL
LL#LL.LL#L.LLLLL#L#LLLLLL#.LLLL#LL.#L.L#L#LL.LL#L#L.LLLL#L#LLL.#L#LLL.#L#L#L#.LLLL#L#.LL#L#L#.LLL#
#LL#L.#LLL.#L#L.LLLL#LL#LL.#L#LLL#.LL#LLLLL#L#LLLLL#L#LLLLLL#LLLLLL#L.LLLLLLL.#L#LLLL.#LLLLLLLL#LL
L#LLL.LL#LLLLLL.#L#L.#LLL#.LLLL#L..#LLL#L#LL.LLL##LLLLL#L#L#LL.#L#LLL#L#L#L#L.LLLL#L#LLLL#L.#L#LL#
...#.L.L.LL#..L......LL#.L..#......L...LLL....#....#..LL.#.L..L..L......L.....L#.L.....#........L.
#L.LLL#L.#.LL#L.LLLL.#LLLL.LLL.LLL.##LL#L#L#.LLLLLLL.##LLLLL#..L#L#L#.LLLL#LL.#LL#LLL.L.LLLLL#L#LL
LL#L#.LLLL.#L#L#L#L#LLL#L#.L#L##L#.L.#LLLLL..#L##L##.LLL#L#LLL.#LLLLL.#L#LLL#.LL#LL#L#LLL#L#L#LLL#
#LLLL.#L#..LLLLLLLLL.#.LLL.LLLLLLL.#LL#L#LLL.LLLLLLLL##LLLLL##.LL#L##.LLLL#LLL#LLL#LL.L#LLLLLLL#LL
LL#L#.LL#..LL#L#L#L#LLLLL#L#L#L#L#.L#LLLLL#L##L#.L#..LLL#L##.L.L#L.LL.L#L#L.#LLL#LLL#LLLL#L#L#LLL#
#LLLL.#LLL.#LLLLLLLL.#L#L#.LLLLLLL.LLL#L#LLL.LLLLLLL.#L#LL.LL#LLLL#LL.LLLLLLL.#LLL#LL.#.LLLLLLL#LL
LL#L#LL#L#LLL#L.#L#L.LLLLL.L#L#L#L.L#LLLLL#LLL#L##L#.LLLL#L#LL.##LLL#L#L#L#L#.LL#LLL#.LLL#L#L#.LL.
#LLLL.LLLL.#LLL.LLLL.L#L#L#LLLLLLL#LLL#L#LLL#LLLLLLL.L#LLLLLL#.LLL#LLL#LLLLLL.#LLL#LLL#.LLLLLLL.L#
L#L#L#.L#LLLL#L#L#L#.L#L#L#L#L#L#..L#LLL#L#L.#LL#L#L#LLL#L#L#L.##LLL#.LLL#L#LLLL#L#LL.#L#L#L#L##LL
...L...L...#...LL...LL.L.L..LL..L....L#...L#LL.L........#......L..L....#.L....#...L.L.L.LLL.#..L.#
#L#LL.#.L#.LLLL.#L#L.#L#L#L#LLL#LLL#L.LLL#LL.##LL.LL.#L.LLLLLL.#LLLL#.LLLL#L#LLLL#L#L.L#L#LLLLLLLL
LLLL#.LLLL.#L#L.LLLLLLLLLLL#L#L#L#LLL#L#L.L#.LLL##L#LLL#L#L#L#LLL#L#L.#L#L#LLL#.LLLLL.LLLLL#L#L#L#
#L#LL.#L#L.LLLL#LL#L.#L#L#.LLLL.LL.#LLLLLLLL.##LLLLL.#LLLLLLLLL#LLLLL.LLLLLL#.LLL#L#L#L#L#LLLLLLLL
LLLL#LLLLL.#L#L.#LLL.LLLLL.#LL#L##.LL#L#L#L#.LLL##L#LLL#L#L#L#.LL#L#L#L#L#L#L.L#LLLLLLLLLL.#L#L#L#
#L#LL.#L#L.LLL#.LL#L#L#L#LLL#L.LLL.#LLLLLLLL.##LLLLL.#LLLLLLLL.L#LLLLLLLLLLLL.LLL#L#L.#L#LLLLLLLLL
LLLL#.LLLL#L#LL.#LLLLLLLLL#LLLL#L#LLL#L#L#L#.LLL##L#.LL#L#L#L#.LLL#L.L#LL#L#L#L#LLLLL.#LLL#L#L.#L#
#L#LL.#L#LLLLL#LL#LL#L#L#LLLL#LLLL.#LLLLLLLLLL#LLLLLL#.LLLLLLL.L.LLL#LLL#LLLLLLLL#L#LLLL#LLLLLLLLL
LLL#L.LLLL#L#LL.LL.L.LLLL#L#LLL#L#.LL#L#LL#L#LLL#L#L#LL#L#L#L#LL#L#LL.#LLL#LL.#L#LLL..#LLL#L#L#LL#
#L.L...L#..LL..L..#.L....L...#..L..LL......LL.#.......L..LL........L#.LL#LL.......L.....#L.LL.....
LL#LL.#LLLLL#.L.#LLL.##L#L.LLLLLLL.#LLLLL#L#LLLLLLLL.#LL#L#L#..LLLLLL.#LLL#LL#L#L#.L#.LLLLL#L#L#LL
#LLL#.LL#L#LL.L.L#L#.LLLLL.#L#L#L#.LL#L#LLLL.##L##L#.LL#LLLLLLL#L#L#LLLL#LLL#.L#LLLLL.L#L#LLLLLLL#
LL#LL.#LLL.#L#L#LLLLL.#L#L.L.LLLLL.#LLLLL#L#.LLLLLLL.L#L.LL#L#.LLLLLL.#LLL#LLLLLLL#L#.LLLLL#L#L#LL
#LL.#.LL#LLLLLL.L#L#LLLLLLLL#L#L#L.LL#L#L.LL.L#L#L#L.LLL#L#LLL.L#L#L#.#L#LLL#.LL#LLLL.L#L#L#LL.LL#
..#.L....#L.L#.....L.#L......L.LL..#LLLLL......LL.L.L#.LLL...#LLL......L.L.L...#L..#..L..L...L#..L
LLLL#.LLLLL#LLL.LL#..LLLL#.LL#L#L#.L#L#L#L#L.#L#L#L#..LL#LL#LL.#L#LLL#L#.L###.LLL#LLL.#L#L#L#L.LL#
#L#L..L#L#.LL#L.#LLL.#L#LL.#LLLLLLLLLLLLLLLLLLLLLLLL.L#LLL#LL#.LLLLL#.LLLLLLL.#L#LLL#LLLLLLLLLL#LL
LL#.L.LLLL.#LLL.LL#L.LLLL#.L#L#L#L.#L#L#L#L#L#LL#L.#.LLL#LLL#LL#LL#LL.#L#L#LL.LLLL#LL.#LL#L#L#L.L#
#.LL..#L#L.L.#LL#LLL##L#LL.LLLLLLL.LLLLLLLL..LLL.LLL.L#LLL#.LL.LL#LL#.LLLLLL#L#L#LLL#.LL#LLLLLLLLL
LL##L.LLLL.#L.#.LL#L.LLL#L.L#L#L#L#L#L#L#LLL#L#LL#L#.LLL#.LLL#L#LLL#..L#L#LLL.LLLL#LL.#LLL#L#LL#L#
#....L...L..#.L...L....L......L..L#L..L..L#.LL.#.L....#..L.#.LLL.#.LLLL.........#..L#.LL#......L..
LLLLL.#L#L.LLLL.L#LL.##LLL.#L#LLLL.#L#LLLLLLLLLLLL#L.LLLLL.LL#L#LLLL#L#L#L#L#.LLLLLLL.LLLLLLLLL#.L
#L#L#.LL#..L#L#.LLL#.LLL#L.LL#L#L#.LL#L#L#LL#.L#L#.L#L#L#L#LLLLLL#L#L.#L#L#LL.#L#L#L#.L#L#L#L#L.L#
LLLLL.#LLLLL.LL.#LLL.#LLLL.#LLLLLL.L.LLLLLLL.LLLLLLLLLLLLLLL#L#LLLLLLLLLLLLL#LLLLLLLL.LLLLLLLLLLLL
#L#L#.LL#L.#L#LLL#L#.LL#L#.L#L#L#L.L#L#L#L#.L#L#L#L#.L#L#L#LLL.L#L#L#.L#.L#LLL#L#L#L#.L#L#L#L#L#L#
LLLLL.LLLL.LLLL.LLLL.#LLLL.LLLLLLL.LLLLLLLLLLLLLLLLL.LLLLLLLL#.LLLLLL.LLLLLL#.LLLLLLLLLLLLLLLLLLLL
#L#L#L#L#L#L.#L#L#L#.L#L#L#.L#L#L#L#L#L#L#L#.L#L##.L#L#L#L#L#L.#L#L#L#L#L#L#L.#L#L#L#.L#L#L#L#L#L#

there are 2059 occupied seats

第 11 天 AOC 到此结束!