In the previous post I randomly generated some Lindenmeyer systems, but only about a third of seeds I tried generating something other than a line or squares. Others were somewhat chaotic, without the pleasing regularity we like to see in fractals. Rather than generating L-system definitions purely randomly, let's see if there are higher-order patterns in attractive systems.

One idea is to look at how steps follow each other in expansion rules. Here's a function to collect that info from an L-system rule:

```
(defn successors [rule]
{:start (first rule)
:successors (reduce
(fn [res [a b]]
(update res a (fnil conj []) b))
{}
(partition 2 1 rule))})
```

Let's check for higher-order patterns in the simpler of the L-systems we've seen so far, the Koch snowflake and Koch curve:

`(koch-snowflake :rules)`

`{F [F + F - - F + F]}`

`(successors (get (koch-snowflake :rules) 'F))`

`{:start F, :successors {F [+ - +], + [F F], - [- F]}}`

`(koch-curve :rules)`

`{F [F + F - F - F F + F + F - F]}`

`(successors (get (koch-curve :rules) 'F))`

`{:start F, :successors {F [+ - - F + + -], + [F F F], - [F F F]}}`

The symmetry of the original rules does seem to show up, possibly in a way we can generate. The successors of `+`

and `-`

are brief sequences of mostly `F`

, while the sequence of successors of `F`

suggests a mirror pattern, in which the end of the sequence either mirrors the beginning, or it mirrors the beginning *and* inverts the turns. Here are two functions to do just that:

```
(defn mirror [steps]
(concat steps (rest (reverse steps))))
```

```
(defn mirror-and-invert [steps]
(concat steps (map #(get '{+ -, - +} % %) (rest (reverse steps)))))
```

To keep generated sequences from terminating prematurely, we'll generate a successor for each `+`

and `-`

in the successors of `F`

:

```
(defn successors-length [steps step]
(count (filter #{step} steps)))
```

Now we can generate successors:

```
(defn generate-successors [rng]
(let [successors-f (repeatedly (rng-rand-int rng 3 5) #(rng-rand-nth rng '[F + -]))
op (rng-rand-nth rng [mirror mirror-and-invert])
f (op successors-f)]
{:start 'F
:successors {'F f
'- (repeatedly (successors-length f '-)
#(rng-rand-nth rng '[F F F -]))
'+ (repeatedly (successors-length f '+)
#(rng-rand-nth rng '[F F F +]))}}))
```

A couple things to notice: first, experimentation suggests favoring `F`

to follow `+`

and `-`

; secondly, it's not very fruitful to follow `+`

with `-`

or vice versa.

To draw a generated system, we'll need to convert this "successors encoding" into an L-system definition:

```
(defn successors->rule [{:keys [start successors]}]
(loop [step start
expansion [start]
successors successors]
(if-let [step' (first (successors step))]
(recur step'
(conj expansion step')
(update successors step rest))
expansion)))
```

`(generate-successors (java.util.Random. 0))`

`{:start F, :successors {F (+ - - - + + -), - (F F F -), + (F F F)}}`

`(successors->rule (generate-successors (java.util.Random. 0)))`

`[F + F - F - F - - F + F + F -]`

Looks promising. Let's see what the first handful of seeds generate:

Not all winners, but better than one-in-three.

To improve variety, we can pick different angles:

```
(defn machine->system-2 [i]
(let [rng (java.util.Random. i)
angle (rng-rand-nth rng [30 45 60 90 120 135])]
{:axiom '[F]
:rules {'F (successors->rule (generate-successors rng))}
:moves {'+ [:turn angle]
'- [:turn angle]
'F [:forward 1]}}))
```

There's a myriad of avenues for further exploration, including two-rule systems, systems that step forward by different distances, systems with more than one turn angle, and combinations of all three. Unfortunately, random generation seems to produce almost exclusively self-intersecting designs, rather than elegant space-filling curves. If we want something like a Hilbert curve or a hexagonal gosper, we probably need to hand-craft it, though some of the random drawings here could serve as inspiration for new designs.