用ggplot2画迷宫
Image ggplot2绝对算得上我掌握的最实用编程技能。随机地画一大堆迷宫,每个迷宫都有且只有一条正确路径,这对于讨好喜欢走迷宫的小闺女来说,可真是太好用了。
下面就来介绍,这个画迷宫的想法,是如何变成现实的。
1
首先,对题图这样的迷宫,可以将其考虑是一个 w x h 的网格( w 和 h 分别表示横向和纵向分别有多少个格子),从中拆除部分相邻格子间的隔板后,而得到的结果。 为方便ggplot2使用,在构造数据时,直接构造 w x h 行数据记录,并且用 x 和 y 两个字段来分别表示每条记录对应的坐标位置,其他字段则分别表示该坐标的其他属性。于是就有如下代码,来完成一个“拆除隔板”前的迷宫的绘制: library(tidyverse)
w <- 20 h <- 12 a <- expand.grid(x = 0:w, y = 0:h) %>% mutate(hline = (x < w), vline = (y < h))
a %>% ggplot(aes(x, y)) + geom_segment(aes(xend = x + hline, yend = y)) + geom_segment(aes(xend = x, yend = y + vline)) + theme_void() 这里,使用了一个实现技巧,即考虑每个格子只拥有右侧和上侧的隔板(对应下面代码中的 vline 和 hline ,这两个字段若取值为FALSE,则表示拆除了该隔板,即不再绘制),左侧和下侧的隔板则共享其相邻格子的。相应地,其实我们构造了一个 (w + 1) x (h + 1) 行的数据表。为了去除网格在上边界和有边界的多余线段,在构造 hline 和 vline 初始值时,限制了相应的范围。
有了上面这个框架,接下去,我们只需要设置 hline 和 vline 字段,实现合适的隔板拆除,迷宫就可以完成。
2
该拆除哪些隔板呢? 这需要一个循环过程。首先假定有一个精灵,站在迷宫的起点,然后采取随机游走的方式,走到相邻的下一个格子。游走的过程需要进行标记,避免走入已经到达过的格子。依靠这个游走的过程进行隔板拆除,最终,就能确保有且只有一条正确路径,到达预期终点。 下面这个动画,比较直观地展示了上述过程。相应的代码,就不在这里详细解释了。 Image 值得注意的是,这个精灵并不是完全一条道走到黑。如果是,则生成的迷宫很可能会变得很简单。所以,我们需要按照某个概率(比如10%),从已经到达过的某个格子出发,分叉出去,走另一条路径。这个概率,可以用来帮助调整迷宫的复杂度。 3
在上述动画中,展示了每次步进,拆除一片隔板,从而生成整个迷宫的过程。这个动画的每一帧,也同样都是基于ggplot2语句进行绘制的。其间无非是增加了几个字段,分别表示出对应路径及其他图像元素,然后将其映射到对应的geom_xxx() 语句即可。 此外,ggplot2有个映射坐标系的强大功能,可以直接将笛卡尔坐标系(即我们通常熟悉的互相垂直的x轴和y轴所构成的平面)转化为极坐标系。这让我们可以直接把上面实现的迷宫,变成一个圆形迷宫。 Image 最终迷宫实现(包括上述演示动画)的完整代码,均可在github上找到,点击“阅读原文”可进行查看。
注:本文首发表于“不靠谱颜论”公众号,并同步至本站。