开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
- Advent of Code 2020 Day11 译文(用 Rust 实现 Advent of Code 2020 第11天)
- 原文链接:fasterthanli.me/series/adve…
- 原文作者:Amos
- 译文来自:github.com/suhanyujie/…
- 译者:suhanyujie
- ps:水平有限,如有不当之处,欢迎指正
- 标签:Rust,advent of code, algo
新的一天,新的问题。
这个问题看起来有点像“康威生命游戏”(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::leak 或 include_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 到此结束!