Los sistemas L , por lo que puedo decir *, son un conjunto de reglas de sustitución gramaticales que puede aplicar de forma recursiva para obtener resultados interesantes, "orgánicos".
Las plantas son donde a menudo se usan los sistemas L, ya que muestran un gran crecimiento recursivo (es decir, las ramas se dividen en más ramas). Para un ejemplo simple, mostraré un árbol "lollipop" generado usando un L-System:
variables : | o (these are the things that will grow)
start : o
| (this is what we start with)
rules : (o → o o) (these are the substitution rules that we apply
\ / one step at a time)
Entonces, en la generación 1, solo tenemos el comienzo:
o
|
En la generación 2, seguimos cada una de las reglas y sustituimos las partes existentes de acuerdo con las reglas. Reemplazamos las "bolas" con "dos palos y bolas":
o o
\ /
|
Generación 3:
o o o o
\| |/
\ /
|
¡Pronto tendremos un árbol grande y bonito!
Para hacer esto en código, puede hacer esto de forma recursiva (es decir, DFS), aplicando continuamente las reglas en las mismas partes hasta llegar a un final arbitrario, o puede hacerlo de forma iterativa (es decir, BFS) como lo hemos hecho en este ejemplo , realizando una regla "pasar" en todos los elementos y repitiendo una serie de pasos. Es decir:
Recursivamente:
tree = start
grow(tree, start)
func grow(tree, part)
if this part of the tree is big enough
stop
if part is 'o'
replace part with 'o\/o'
grow(tree, the left 'o')
grow(tree, the right 'o')
Iterativamente:
tree = start
for a number of iterations
for each part in tree
if part is 'o':
replace with 'o\/o'
Muchos de los usos de L-Systems realizan el paso de "crecimiento" utilizando la subdivisión, es decir, las partes se vuelven más pequeñas a medida que "crecen", las partes más grandes simplemente se dividen. De lo contrario, su sistema de crecimiento puede comenzar a superponerse sobre sí mismo. Verás en mi ejemplo del árbol de piruletas, mágicamente me aseguré de que las dos ramas no se superpongan en el medio al cambiar la forma de las nuevas ramas. Hagamos el ejemplo de la ciudad usando la subdivisión:
variables: block_vertical block_horizontal road_vertical road_horizontal
start: block_vertical
rules: (block_vertical → block_horizontal road_vertical block_horizontal)
(block_horizontal → block_vertical road_horizontal block_vertical)
Esto tendrá sentido en un minuto.
Generación 1:
+--------------------+
| |
| |
| |
| V |
| |
| |
| |
+--------------------+
Un solo bloque vertical aburrido. (La V significa vertical).
Generación 2: reemplazamos el bloque vertical con bloques horizontales con una carretera vertical en el medio
+--------------------+
| r |
| r |
| r |
| H r H |
| r |
| r |
| r |
+--------------------+
¡La r significa camino! He separado aleatoriamente la división, no queremos partes regulares aburridas en PCG.
Generación 3: reemplazamos los bloques horizontales con bloques verticales divididos con caminos horizontales. Los caminos existentes permanecen; No hay reglas para ellos.
+--------------------+
| V r |
| r |
|rrrrrrrr |
| r V |
| V r |
| rrrrrrrrrrrrr|
| r V |
+--------------------+
Observe cómo las carreteras se conectan entre sí, lo cual es bueno. Repita esto suficientes veces y terminará con algo como esto (descaradamente arrancó una respuesta relacionada ):
Tenga en cuenta que hay muchos detalles que no he cubierto, y que este resultado se ve "obviamente" generado: las ciudades reales se ven algo diferentes. Eso es lo que hace que PCG sea divertido / difícil. Hay infinitas cosas que puede hacer para ajustar y mejorar sus resultados, pero al no estar relacionado con L-Systems, dejaré esta respuesta aquí; Espero que esto te ayude a comenzar.
* - No he estudiado L-Systems formalmente, aunque he encontrado tipos específicos como gramáticas y vegetación PCG; corríjame si me equivoco con alguna definición o concepto