Vladimir Shevyakov
Vladimir Shevyakov

Reputation: 2831

Strange L-system in python turtle graphics

I tried to use the turtles module in Python 3 to recreate the fractal found here: https://en.wikipedia.org/wiki/L-system#Example_7:_Fractal_plant but whenever I try it it gives me a very strange result...

Here's my code:

import turtle
wn = turtle.Screen()
wn.bgcolor("white")
wn.screensize(10000, 10000)

tess = turtle.Turtle()
tess.color("lightgreen")
tess.pensize(1)
tess.speed(0)
tess.degrees()

inst = 'X'
steps = 3

for counter in range(steps):
    _inst = ''
    for chtr in inst:
        if chtr == 'X':
            _inst += 'F−[[X]+X]+F[+FX]−X'
        elif chtr == 'F':
            _inst += 'FF'
        else:
            _inst += chtr
    inst = _inst
    print(inst)


for chtr in inst:
    if (chtr == 'F'):
        tess.forward(25)
    elif (chtr == '+'):
        tess.right(25)
    elif (chtr == '-'):
        tess.left(25)
    elif (chtr == '['):
        angle = tess.heading()
        pos = [tess.xcor(), tess.ycor()]
    elif (chtr == ']'):
        tess.setheading(angle)
        tess.penup()
        tess.goto(pos[0], pos[1])
        tess.pendown()
wn.exitonclick()

I triple-checked everything and I seem to have no bugs - but it still does not work. What am I doing terribly wrong?

Thanks in advance for any help!

Upvotes: 0

Views: 1364

Answers (2)

Blckknght
Blckknght

Reputation: 104722

There are two issues in your code.

The first is that your code doesn't handle nested brackets properly. The inner opening bracket saves its state over the top of the previous state saved when the outer opening bracket was seen. This won't matter for immediately nested brackets like [[X]+X] (since both have the same starting state), but once you get more complicated nesting (as you will after a few substitution loops), the issue starts to make things go wrong.

To solve this you probably want to store your saved state values to a stack (a list can do). Push the values you want to save, and pop them back off when you're ready to restore them.

stack = [] # use a list for the stack
for chtr in inst:
    if (chtr == 'F'):
        tess.forward(25)
    elif (chtr == '+'):
        tess.right(25)
    elif (chtr == '-'):
        tess.left(25)
    elif (chtr == '['):
        angle = tess.heading()
        pos = [tess.xcor(), tess.ycor()]
        stack.append((angle, pos)) # push state to save
    elif (chtr == ']'):
        angle, pos = stack.pop()  # pop state to restore
        tess.setheading(angle)
        tess.penup()
        tess.goto(pos[0], pos[1])
        tess.pendown()

The second issue is more trivial. Your parser looks for the "minus" character (-). But your pattern generating code uses a different, slightly longer kind of dash (). Change one of them to match the other (it doesn't really matter which one) and your code will work as expected.

Upvotes: 1

RootTwo
RootTwo

Reputation: 4418

According to the Wikipedia page, the symbol '[' means to save the current state (angle and positions). The matching ']' means to restore the previously saved position. Because the '[' and ']' can be nested, a stack is needed.

from collections import deque

...

stack = deque()

for chtr in inst:
    if (chtr == 'F'):
        tess.forward(25)

    elif (chtr == '+'):
        tess.right(25)

    elif (chtr == '-'):
        tess.left(25)

    elif (chtr == '['):
        angle = tess.heading()
        pos = [tess.xcor(), tess.ycor()]
        stack.append((angle, pos))         ### New statement

    elif (chtr == ']'):
        angle, pos = stack.pop()           ### New statement
        tess.setheading(angle)
        tess.penup()
        tess.goto(pos[0], pos[1])
        tess.pendown()

. . .

Upvotes: 1

Related Questions