HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Small turtle program using instruction strings to draw patterns

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
turtledrawprograminstructionpatternssmallusingstrings

Problem

A while ago, I wrote a program that combined L-systems and turtles. This was before I understood OOP (I still don't fully understand classes, or if they are even needed here). I tried to add some documentation and included some demos as functions. If I ever get around to it, I hope to include a simple GUI with user-adjustable patterns and parameters (like a typical Java applet).

Generate L-system

The inclusion of V, the alphabet, is for completeness in the formal description of a L-system as a 3-tuple.

import turtle

def l_system(V, w, P, n):
    """Generates an L-system run for n rounds.
    They are defined as
    G = (V, w, P)

    V = The alphabet (tuple, not actually used, can be specified as None)
    w = The start (string)
    P = The production rules (dictionary for replacement)
    """
    # Make sure all production rules are in alphabet
    if V:
        assert(all(key in V for key in P))

    current = w
    for i in range(n):
        current = [P[x] if x in P else x for x in list(current)]
        current = ''.join(current)

    return current


Run the turtle

```
def run_turtle(var, start, rules, iters, angle, startdir=0):
"""Var, start, rules and iters, correspond to (V, w, P, n) of the
l-system function. The distance moved is scaled down from size.
The turtle starts facing startdir.

Instructions are defined as the following:
F, G: Draw forward
M, N: Move forward (don't draw)
[, ]: Push and pop angle and location
+, -: Turn left and right by angle degrees
Variables not described can be used as constants.
"""

# Initialization
terry = turtle.Turtle()
turtle.mode("world") # Coordinate system
terry.pensize(1)
terry.pencolor("blue")
terry.speed(0) # Instant speed
turtle.tracer(0, 0) # Don't draw anything yet (could change in future)
turtle.setup(width=900, height=900, startx=None, starty=None) # Square pixels
terry.hideturtle()

dist = 1
positions = []

Solution

I like the concept of the question (using a domain-specific language to specify a fractal) and your implementation. An object-oriented rewrite could make it better, but it's not bad as it is.

l_system() could be better written as

def l_system(V, w, P, n):
    …
    cmd_seq = w
    for _ in range(n):
        cmd_seq = itertools.chain(*(P.get(cmd, cmd) for cmd in cmd_seq))
    return ''.join(cmd_seq)


Namely,

  • cmd_seq would be more meaningful than current. cmd would be more meaningful than x.



  • Use _ for a variable whose value is disregarded.



  • String are directly iterable; you don't have to convert them into lists first.



  • Dictionary lookup with a default can be done using get(key, default).



  • Instead of re-forming the string with each iteration, just chain them, and join the result just once at the end.



Aside from that, I just have a few minor remarks:

  • The parameters are l_system(V, w, P, n) but run_turtle(var, start, rules, iters, …) — why not use consistent notation?



  • As you said, the l_system() function doesn't really need V. I would prefer to just leave it out altogether.



  • In various places, you refer to the turtle's heading as terry.heading(), startdir, and angles. I suggest using consistent terminology based on "heading".



  • update_bounds(bounds) takes a bounds array as an explicit parameter, but takes terry through its scope chain. I'd prefer to see either both variables as explicit parameters, or both via the closure.



  • Rather than having two separate stacks for positions and angles, I would prefer to see one turtle_state stack that stores (position, heading) tuples.



  • The epilogue could use a comment, like # Extend the shorter dimension of the window to make it square

Code Snippets

def l_system(V, w, P, n):
    …
    cmd_seq = w
    for _ in range(n):
        cmd_seq = itertools.chain(*(P.get(cmd, cmd) for cmd in cmd_seq))
    return ''.join(cmd_seq)

Context

StackExchange Code Review Q#114121, answer score: 3

Revisions (0)

No revisions yet.